Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 46 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
46
Dung lượng
366,19 KB
Nội dung
CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 62 action4 } } } You might agree that this looks pretty messy. To make it nicer, we can combine the else and the next if into a single word, elsif. Here’s what the preceding would look like when rephrased in this way: if ( condition1) { action1 } elsif ( condition2 ) { action2 } elsif ( condition3 ) { action3 } else { action4 } Much neater! We don’t have an awful cascade of closing curly braces at the end, and it’s easier to see what we’re testing and when we’re testing it. Let’s look at an example. Most of us will not go outside if it’s raining, but we will always go out for a walk in the snow. We will not go outside if it’s less than 18 degrees Celsius. Otherwise, we’ll probably go out unless we’ve got too much work to do. Do we want to go for a walk? #!/usr/bin/perl # walking.pl use warnings; use strict; print "What's the weather like outside? "; chomp(my $weather = <STDIN>); print "How hot is it, in degrees? "; chomp(my $temperature = <STDIN>); print "And how many emails left to reply to? "; chomp(my $work = <STDIN>); if ($weather eq "snowing") { print "It's snowing, let's go!\n"; } elsif ($weather eq "raining") { print "No way, sorry, it's raining so I'm staying in.\n"; } elsif ($temperature < 18) { print "Too cold for me!\n"; } elsif ($work > 30) { print "Sorry - just too busy.\n"; } else { print "Well, why not?\n"; } CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 63 Let’s say it is 20 1 degrees, we’ve got 27 e-mails to reply to, and it’s cloudy out there: $ perl walking.pl What's the weather like outside? cloudy How hot is it, in degrees? 20 And how many emails left to reply to? 27 Well, why not? $ Looks like we can fit a walk in after all. The point of this rather silly little program is that once it has gathered the information it needs, it runs through a series of tests, each of which could cause it to finish. First, we check to see if it’s snowing: if ($weather eq "snowing") { print "It's snowing, let's go!\n"; If so, then we print our message and, this is the important part, do no more tests. If not, we move on to the next test: } elsif ($weather eq "raining") { print "No way, sorry, it's raining so I'm staying in.\n"; Again, if this is true, we stop testing; otherwise, we move on. Finally, if none of the tests are true, we get to the else: } else { print "Well, why not?\n"; } Please remember that this is very different from what would happen if we used four separate if statements. The tests overlap, so it is possible for more than one condition to be true at once. For example, if it was snowing and we had over 30 emails to reply to, we’d get two conflicting answers. elsif tests should be read as “Well, how about if . . . ?” Now let’s update the program we saw earlier, guessnum1.pl, to use if/elsif/else. The decision we made in the first version was implemented with three if statements: if ($target == $guess) { print "That's it! You guessed correctly!\n"; exit; } if ($guess > $target) { print "Your number is more than my number\n"; exit; } if ($guess < $target){ print "Your number is less than my number\n"; 1 Celsius, that is. CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 64 exit; } Notice that in each if statement we execute the exit() function since, if the condition is true, there is no reason to check any of the following conditions. Instead of using the exit() function in each of the if blocks, this would be better written with an if/elsif/else, as shown in guessnum2.pl: #!/usr/bin/perl # guessnum2.pl use warnings; use strict; my $target = 12; print "Guess my number!\n"; print "Enter your guess: "; my $guess = <STDIN>; if ($target == $guess) { print "That's it! You guessed correctly!\n"; } elsif ($guess > $target) { print "Your number is more than my number\n"; } elsif ($guess < $target) { print "Your number is less than my number\n"; } The unless Statement There’s another way of saying if (not $a). As always in Perl, there’s more than one way to do it. 2 Some people prefer to think “If this is not true, then { },” but other people like to think “Unless this is true, then { }.” Perl caters to both thought patterns, and we could just as easily have written: unless ($a) { print "\$a is not true\n"; } The psychology is different, but the effect is the same. We’ll see later how Perl provides a few alternatives for these control structures to help them more effectively fit the way you think. 2 TMTOWTDI-our favorite acronym! CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 65 Expression Modifiers When we talk in English, it’s quite normal to say • If this is not true, then this happens, or • Unless this is true, this happens. And it’s also quite natural to reverse the two phrases • This happens if this is not true, or • This happens unless this is true. In Perl-speak, we can take this if statement: if ($number == 0) { die "can't divide by 0"; } and rewrite it as follows: die "can't divide by 0" if $number == 0; Notice how the syntax here is slightly different, it’s action if condition. There is no need for parentheses around the condition, and there are no curly braces around the action. Indeed, the indentation isn’t part of the syntax, so we can even put the whole statement on one line. Only a single statement will be covered by the condition. This form of the if statement is called an expression modifier. We can turn unless into an expression modifier too, so, instead of this: if (not $name) { die "\$name has a false value"; } you may find it more natural to write this: die "\$name has a false value" unless $name; Using Short-Circuited Evaluation There is yet another way to do something if a condition is true. By using the fact that Perl stops processing a logical operator when it knows the answer, we can create a sort of unless conditional: $name or die "\$name has a false value"; How does this work? Well, it relies on the fact that Perl uses short-circuited, or lazy, evaluation to give a logical operator its value. If we have the statement X or Y, then if X is true, it doesn’t matter what Y is, so Perl doesn’t look at it. If X isn’t true, Perl has to look at Y to see whether or not that’s true. So if CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 66 $name has a true value, then the die() function will not be executed. Instead, Perl will do nothing and continue on to execute the next statement. This form of conditional is most often used when checking that something we did succeeded or returned a true value. We will see it often when we’re handling files. To create a positive if conditional this way, use and instead of or. For example, to add one to a counter if a test is successful, you can say $success and $counter++; As you’ll recall, and statements require both substatements to be true. So, if $success is not true, Perl won’t bother evaluating $counter++ and upping its value by 1. If $success is true, then it would. Looping Constructs Now we know how to do everything once. What about if we need to repeat an operation or series of operations? Of course, there are constructs available in Perl to do this, too. In programming, there are various types of loops. Some loop forever, and are called infinite loops, while most, in contrast, are finite loops. We say that a program “gets into” or “enters” a loop, and then “exits” or “falls out” when finished. Infinite loops may not sound very useful, but they certainly can be— particularly because most languages, Perl included, provide a way to exit the loop. They are useful when you want the program to continue running until the user stops it manually, the computer powers down, or the heat death of the universe occurs, whichever comes first. There’s also a difference between definite loops and indefinite loops. In a definite loop, you know in advance how many times the block will be repeated. An indefinite loop will check a condition in each iteration to determine whether it should loop again. There’s also a difference between an indefinite loop that checks before the iteration, and one that checks afterward. The latter will always go through at least one iteration, in order to get to the check, whereas the former checks first and so may not go through any iterations at all. Perl supports ways of expressing all of these types of loops. First, let’s examine the while loop. The while Loop Let’s start with indefinite loops. These check a condition, then do an action, then go back and check the condition again. We’ll look first at the while loop. As you might guess from the name, this type of loop keeps doing something while a condition is true. The syntax of while is much like the syntax of if: while ( condition ) { action } Once again, those curly braces are required. Here’s a very simple while loop: #!/usr/bin/perl # while1.pl use warnings; use strict; my $countdown = 5; while ($countdown > 0) { CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 67 print "Counting down: $countdown\n"; $countdown ; } And here’s what it produces: $ perl while1.pl Counting down: 5 Counting down: 4 Counting down: 3 Counting down: 2 Counting down: 1 $ Let’s see a flow chart for this program. While there’s still a value greater than 0 in the $counter variable, we do these two statements: print "Counting down: $countdown\n"; $countdown ; Perl goes through the loop a first time when $countdown is 5; the condition is met, so a message is printed and $countdown gets decreased to 4. Then, as the flow chart illustrates, back we go to the top of the loop. We test again: $countdown is still more than 0, so off we go again. Eventually, $countdown is 1, we print our message, $countdown is decreased, and now it’s 0. This time around, the test fails, and we exit the loop. while (<STDIN>) Recall that we talked about using <STDIN> to read from standard input (normally the keyboard). This statement reads the next line of standard input, up to and including the newline character: $line_in = <STDIN>; CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 68 We can put this assignment within a while loop that will read from standard input until end of file (in Unix a ^D, or the Ctrl and D keys pressed at the same time; in Windows a ^Z<enter>). This loop reads a line at a time into $line_in and then prints that line: while ($line_in = <STDIN>) { print $line_in; } This behavior, reading from standard input until end of file, is so common that if <STDIN> is by itself within the while loop parentheses (and only within the while loop parentheses), then the line of standard input is magically assigned to the special variable $_. This loop reads each line into $_, and then the line is printed: while (<STDIN>) { print $_; } This is so common that print() defaults to printing $_: while (<STDIN>) { print; } Let’s look at an example of using this magic variable $_. This program will loop through standard input one line at a time until end of file, and for each line it will print a message followed by the line entered: #!/usr/bin/perl # while2.pl use warnings; use strict; while (<STDIN>) { print "You entered: "; print; } Here is an example of running this program in Unix: $ perl while2.pl Perl You entered: Perl is You entered: is cool You entered: cool ^D $ CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 69 ■ Note In Unix, end of file is ^D (Control-D). In Windows, end of file is ^Z<ret> (yes, you have to type the return key). You will have to adjust the example above if you are working in Windows. The $_ variable is very useful—it is the default argument for many different functions, such as the chomp() function. The statement chomp $_; could have been written as chomp; Many Perl programmers find it convenient and readable to write a loop like this one: while ($line = <STDIN>) { chomp $line; } using the default nature of $_: while (<STDIN>) { chomp; } Whether or not you write code to take advantage of the magic nature of $_ is up to you, but we suggest you practice with it enough to be able to read code that others have written using $_ . Infinite Loops The obvious but important point is that what we’re testing gets changed inside the loop. If our condition is always going to give a true result, we have an infinite loop. Let’s just remove the second of those two statements: #!/usr/bin/perl # while3.pl use warnings; use strict; my $countdown = 5; while ($countdown > 0) { print "Counting down: $countdown\n"; } CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 70 $countdown never changes. It’s always going to be 5, and 5 is, we hope, always going to be more than 0. So this program will keep printing its message until you interrupt it by holding down Ctrl and D. Hopefully, you can see why you need to ensure that what you do in your loop affects the condition. Should we actually want an infinite loop, there’s a fairly standard way to do it. Just put a true value— typically 1—as the condition: while (1) { print "Bored yet?\n"; } The converse, of course, is to say while (0) in the loop’s declaration, but nothing will ever happen because this condition is tested before any of the commands in the loop are executed. A bit silly really. Looping Until In the same sense that the opposite of if is unless, the opposite of while is until. The until loop is exactly the same as while (not condition) { }. Using the condition in the program while1.pl shown previously: while ($countdown > 0) { Its logical negation would be until ($countdown <= 0) { Therefore, we can write while1.pl as #!/usr/bin/perl # until.pl use warnings; use strict; my $countdown = 5; until ($countdown <= 0) { print "Counting down: $countdown\n"; $countdown ; } And here’s what it produces: $ perl until.pl Counting down: 5 Counting down: 4 Counting down: 3 Counting down: 2 Counting down: 1 $ www.wowebook.com CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 71 ■ Note The negation of > is <=, not <. Yes, we have made the same mistake many times.… The for Loop Perl has a for loop, similar to the one found in C, C++, and Java. Its syntax is for (init_expression; test_expression; step_expression) { action } The init_expr is done first and once. Then the test_expr is tested to be true or false. If true, the action is executed, then the step_expr is executed. Then the test_expr is tested to be true or false, and so on. The most common use of a for loop is as an alternative way of writing a while loop that might resemble this one: $i = 1; while ($i <= 5) { # do something important $i++; } This can be written in a for loop as for ($i = 1; $i <= 5; $i++) { # do something important } The foreach Loop Perl has another loop called the foreach loop. It is used to loop through lists and arrays. We will talk about arrays in the next chapter, but since we have seen examples of a list, we can look at the foreach loop processing a list of numbers: #!/usr/bin/perl # foreach.pl use warnings; use strict; my $number; foreach $number (1 10) { print "the number is: $number\n"; } [...]... "\n"; print "Going from 3 to z: ", (3 'z'), "\n"; print "Going from z to 3: ", ('z' 3) , "\n"; Which of those will work and which won’t? Let’s find out $ perl ranges.pl Argument "z" isn't numeric in range (or flop) at ranges.pl line 14 Argument "z" isn't numeric in range (or flop) at ranges.pl line 15 Counting up: 1 234 56 Counting down: Counting down (properly this time): 65 432 1 Half the alphabet:... number:$test One last thing to note is that Perl automatically flattens lists That is, if you try putting a list inside another list, the internal list loses its identity In effect, Perl removes all the parentheses apart from the outermost pair There’s no difference at all between any of these three lists: (3, 8, 5, 15) ( (3, 8), (5, 15)) (3, (8, 5), 15) Similarly, Perl sees each of these lists exactly the... in, and save it as mixedlist.pl: #!/usr/bin /perl # mixedlist.pl use warnings; use strict; my $test = 30 ; print "Here is a list containing strings, (this one) ", "numbers (", 3. 6, ") and variables: ", $test, "\n" ; When you run that, here’s what you should see: $ perl mixedlist.pl Here is a list containing strings, (this one) numbers (3. 6) and variables: 30 $ Notice that the print() function prints... number as per the rules in Chapter 2, and got a 0 It’s equivalent to this: print "Going from 3 to z: ", (3 0), "\n"; print "Going from z to 3: ", (0 3) , "\n"; The first one produces an empty list, and the second one counts up from 0 to 3 Accessing List Values We’ve now seen most of the ways of building up lists in Perl, and we can pass lists to functions like print() But another thing we need to be able... arrays as lists This isn’t a particularly good way to do it, but it works: #!/usr/bin /perl # addelem.pl use warnings; use strict; my @array1 = (1, 2, 3) ; my @array2; @array2 = (@array1, 4, 5, 6); print "@array2\n"; @array2 = (3, 5, 7, 9); @array2 = (1, @array2, 11); print "@array2\n"; $ perl addelem.pl 1 2 3 4 5 6 1 3 5 7 9 11 $ It’s far better, however, to use the functions we’re going to see later... $month = 2.2; Perl will round the number in this case, and you should get the answer “March” In fact, Perl always rounds towards 0, so anything between 2 and 3 will get you March What about negative numbers? Actually, something interesting happens here Perl starts counting backward from the end of the list So element –1 is the last one, –2 the second to last one, and so on #!/usr/bin /perl # backwards.pl... and then 3 is assigned to $mtwo This is called an assignable list If you’re okay with that, now is a good time for a quick quiz Suppose we’ve done the preceding: $mone is 1 and $mtwo is 3 What do you think would happen if we said this? ($mone, $mtwo) = ($mtwo, $mone); The right-hand list is built up first, so Perl looks at the values of the variables and constructs the list (3, 1) Then the 3 is assigned... come to the statements that produce the warnings: Argument "z" isn't numeric in range (or flop) at ranges.pl line 13 Argument "z" isn't numeric in range (or flop) at ranges.pl line 14 The lines in question are print "Going from 3 to z: ", (3 'z'), "\n"; print "Going from z to 3: ", ('z' 3) , "\n"; What does the error message mean? Pretty much what it says: we gave an argument of “z” to a range when it... $array[0] can only refer to an element of the @array array If you use the wrong prefix, Perl will complain with a warning #!/usr/bin /perl # badprefix.pl use warnings; use strict; my @array = (1, 3, 5, 7, 9); print @array[1]; will print $ perl badprefix.pl Scalar value @array[1] better written as $array[1] at badprefix.pl line 8 3$ We call the number in the square brackets the array index or array subscript... out of the inner loop and increment $i, then go back up to test the expression for the outer while loop This generates the following output: $ 1 1 2 2 3 3 4 4 5 5 $ perl ** 1 ** 1 ** 1 ** 2 ** 1 ** 2 ** 1 ** 2 ** 1 ** 2 looplabel1.pl = 1 = 1 = 2 = 4 = 3 = 9 = 4 = 16 = 5 = 25 To make the last statement last out of the outer looping construct, we must label the outer looping construct with a loop label . me!
"; } elsif ($work > 30 ) { print "Sorry - just too busy.
"; } else { print "Well, why not?
"; } CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 63 Let’s say it is 20 1 . true, Perl has to look at Y to see whether or not that’s true. So if CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 66 $name has a true value, then the die() function will not be executed. Instead, Perl. of running this program in Unix: $ perl while2.pl Perl You entered: Perl is You entered: is cool You entered: cool ^D $ CHAPTER 3 ■ CONTROL FLOW CONSTRUCTS 69 ■ Note In Unix,