Systems and Network Management Perl Data Structures — Solutions 1 1.1 Background Scalars You can specify scalar numerical values in the same ways as in C: The following are all scalar constant values: 123, 12.4, 5E-10, 0xff (a hexadecimal value), 0377 (octal). Single quotes preserve all characters unchanged, except for ‘\\’ and ‘\’’: my $see = "very far"; my $x = "big X"; print ’What you \’$see\’ is (almost) what \n you get’; print "\n"; print ’Don\’t Walk’; print "\n"; print "How are you?" . ’ ’ . "Substitute values of $x and \n in \" quotes."; print "\n"; The output is: What you ’$see’ is (almost) what \n you get Don’t Walk How are you? Substitute values of big X and in " quotes. Back ticks work rather like they do in the shell: my $total_file_size = ‘du -s $directory_name‘; my $date = ‘date‘; Here are three scalar values; the second is an array element, the third is a hash element: $x $list_of_things[5] $lookup{key} Single-quotes ’...’ allow no substitution except for \\and \’. Double-quotes "..." allow substitution of variables like $x and control codes like \n (newline). Back-quotes ‘...‘ also allow substitution, then try to execute the result as a system command, returning as the final value whatever the system command sends to standard output. 1.2 Arrays (or Lists) Here are some examples of constant lists: ( ’Sunday’, ’Monday’, ’Tuesday’, ’Wednesday’, ’Thursday’, ’Friday’, ’Saturday’ ) ( 13, 14, 15, 16, 17, 18, 19 ) is equivalent to (13..19) ( 13, 14, 15, 16, 17, 18, 19 )[2..4] equivalent to (15, 16, 17) Nick Urbanik ver. 1.6 Solutions Perl Data Structures Systems and Network Management 2 1.3 Hashes Here are a few examples of hashes: $DaysInMonth{January} = 31; $enrolled{’Chan Wai-yee’} = 1; $StudentName{012345678} = ’Chan Wai-yee’; %whole_hash 1.4 Some More Examples 12 elements: 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); => => => => => => => ’SNM lecture and workshop’, ’SNM workshops’, ’OSSI lecture and workshops’, ’Projects, OSSI workshops’, ’OSSI workshops’, ’riding bicycle with my son’, ’Write more workshops and notes’, # A list with @days = ( 31, %days = ( Mon Tue Wed Thu Fri Sat Sun ); $#days # $#days = 7; # @days # @days[3,4,5] # @days{’Mon’, ’Wed’} # %days # Last index of @days; 11 for above list @days shortens or lengthens list @days to 8 elements ( $days[0], $days[1],... ) = ( 30, 31, 30 ) same as ($days{Mon}, $days{Wed}) (key1, value1, key2, value2, ...) 1.5 Comparisons This example program illustrates comparisons, and the “here document” (borrowed from the shell): #! /usr/bin/perl # The following "<<" variation of quoting can help simplify things sometimes my $x = ’operator’; print < ver. 1.6 Solutions Perl Data Structures Systems and Network Management 3 if ($x eq ’stuff’) { print "Use eq, ne, lt, gt, etc for strings.\n"; } The output looks like this: A common mistake: Confusing the assignment operator = and the numeric comparison operator ==, and the character comparison operator eq. x is 7 x is now 5,the assignment is successful. Use eq, ne, lt, gt, etc for strings. Note that if the first line has a minus w ‘-w’ like this: #! /usr/bin/perl -w then the compiler also generates a warning: Found = in conditional, should be == at ./comparisons line 12. 1.6 split It is very useful to be able to split a string into elements of an array. The Perl builtin function for this is called split. Here is a simple fragment to show how it works: my $string = " What a lovely piece of my @array = split ’ ’, $string; string "; Now the elements of @array have been filled with the words of the string: $array[0] $array[1] $array[2] $array[3] $array[4] $array[5] contains contains contains contains contains contains "What" "a" "lovely" "piece" "of" "string" 2 What to do 1. Type in this program and run it: #! /usr/bin/perl -w use strict; print "Enter numeric: day month year\n"; my ( $day, $month, $year ) = split ’ ’, ; print "day=$day, month=$month, year=$year\n"; Complete this program. Print an error message if the month is not valid. Print an error message if the day is not valid for the given month (31 is ok for January but not for February). See if you can reduce the use of conditional statements (if, unless, ?, and, or, &&, ||,. . . ) and use data structures as far as is practical without making your program hard to read. Approach this incrementally. Nick Urbanik ver. 1.6 Solutions Perl Data Structures Systems and Network Management 4 (a) On the first draft, assume that the user enters 3 numbers separated by spaces and that February has 28 days. Solution: #! /usr/bin/perl -w use strict; print "Enter numeric: day month year\n"; my ( $day, $month, $year ) = split ’ ’, ; print "day=$day, month=$month, year=$year\n"; my @days = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); --$month >= 0 or die "bad date\n"; $day > 0 and $days[$month] and $day <= $days[$month] or die "bad date\n"; print "good date\n"; (b) Modify your program so that you can enter the month with three letters, from Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov and Dec. Solution: #! /usr/bin/perl -w use strict; print "Enter numeric: day month year\n"; my ( $day, $month, $year ) = split ’ ’, ; print "day=$day, month=$month, year=$year\n"; my %days = ( Jan => 31, Feb => 28, Mar => 31, Apr => 30, May => 31, Jun => 30, Jul => 31, Aug => 31, Sep => 30, Oct => 31, Nov => 30, Dec => 31, ); $day > 0 and $days{$month} and $day <= $days{$month} or die "bad date\n"; print "good date\n"; (c) Subsequent refinements should account for bad input and leap year. A year is a leap year if it is divisible by 400, or if it is divisible by 4 but is not also divisible by 100. Solution: #! /usr/bin/perl -w use strict; print "Enter numeric: day month year\n"; my ( $day, $month, $year ) = split ’ ’, ; print "day=$day, month=$month, year=$year\n"; my %days = ( Jan => 31, Feb => 28, Mar => 31, Apr => 30, May => 31, Nick Urbanik ver. 1.6 Solutions Perl Data Structures Systems and Network Management 5 Jun => 30, Jul => 31, Aug => 31, Sep => 30, Oct => 31, Nov => 30, Dec => 31, ); $days{Feb} += ( not $year % 400 or not $year % 4 and $year % 100 ) ? 1 : 0; $day > 0 and $days{$month} and $day <= $days{$month} or die "bad date\n"; print "good date\n"; Hmm, this is getting a bit poor with the conditional operator ?...:.... Would be nice to use a data structure instead, since this is what the tutorial is about. This check is not bulletproof, and should allow a number of month input formats. (d) Finally, find a Perl builtin function that converts a date to system time, and see how to use that to validate time data generally. Note that all Perl builtin functions are listed in the documention “perlfunc,” which you can view with $ perldoc perlfunc You could search for strings such as date or time. There is a Perl module called Time::Local that you could investigate. How? Do perldoc Time::Local. Solution: #! /usr/bin/perl -w use strict; print "Enter: day month year in format n mmm y\n"; my ( $day, $month, $year ) = split ’ ’, ; print "day=$day, month=$month, year=$year\n"; $month = lc $month; my %months = ( jan => 0, feb => 1, mar => 2, apr => 3, may => 4, jun => 5, jul => 6, aug => 7, sep => 8, oct => 9, nov => 10, dec => 11, ); if ( $month =~ /^\d+$/ ) { --$month } else { $month = $months{$month} } eval { my $time = timelocal( 0, 0, 0, $day, $month, $year ); }; if ( $@ ) { print "$@: bad" } else { print "good" } print " date\n"; Nick Urbanik ver. 1.6