📄 ch04_04.htm
字号:
}for (@christmas, @easter) { # change menu s/ham/turkey/;}s/ham/turkey/ for @christmas, @easter; # same thingfor ($scalar, @array, values %hash) { s/^\s+//; # strip leading whitespace s/\s+$//; # strip trailing whitespace}</pre></blockquote><a name="INDEX-1107"></a><a name="INDEX-1108"></a></p><p>The loop variable is valid only from within the dynamic or lexicalscope of the loop and will be implicitly lexical if the variable waspreviously declared with <tt class="literal">my</tt>. This renders it invisible to any function defined outside the lexical scope of the variable, even if called fromwithin that loop. However, if no lexical declaration is in scope, theloop variable will be a localized (dynamically scoped) globalvariable; this allows functions called from within the loop to accessthat variable. In either case, any previous value the localizedvariable had before the loop will be restored automatically upon loop exit.</p><p>If you prefer, you may explicitly declare which kind of variable(lexical or global) to use. This makes it easier for maintainersof your code to know what's really going on; otherwise, they'll needto search back up through enclosing scopes for a previous declarationto figure out which kind of variable it is:<blockquote><pre class="programlisting">for my $i (1 .. 10) { ... } # $i always lexicalfor our $Tick (1 .. 10) { ... } # $Tick always global</pre></blockquote>When a declaration accompanies the loop variable, the shorter <tt class="literal">for</tt>spelling is preferred over <tt class="literal">foreach</tt>, since itreads better in English.</p><p>Here's how a C or Java programmer might first think to code up aparticular algorithm in Perl:<blockquote><pre class="programlisting">for ($i = 0; $i < @ary1; $i++) { for ($j = 0; $j < @ary2; $j++) { if ($ary1[$i] > $ary2[$j]) { last; # Can't go to outer loop. :-( } $ary1[$i] += $ary2[$j]; } # this is where that last takes me}</pre></blockquote>But here's how a veteran Perl programmer might do it:<blockquote><pre class="programlisting">WID: foreach $this (@ary1) { JET: foreach $that (@ary2) { next WID if $this > $that; $this += $that; }}</pre></blockquote>See how much easier that was in idiomatic Perl? It's cleaner, safer,and faster. It's cleaner because it's less noisy. It's saferbecause if code gets added between the inner and outer loops lateron, the new code won't be accidentally executed, since <tt class="literal">next</tt>(explained below) explicitly iterates the outer loop rather thanmerely breaking out of the inner one. And it's faster because Perlexecutes a <tt class="literal">foreach</tt> statement more rapidly than it would theequivalent <tt class="literal">for</tt> loop, since the elements are accessed directlyinstead of through subscripting.</p><p>But write it however you like. TMTOWTDI.</p><p><a name="INDEX-1109"></a> Like the<tt class="literal">while</tt> statement, the <tt class="literal">foreach</tt>statement can also take a <tt class="literal">continue</tt> block. Thislets you execute a bit of code at the bottom of each loop iteration nomatter whether you got there in the normal course of events or througha <tt class="literal">next</tt>.</p><p>Speaking of which, now we can finally say it: <tt class="literal">next</tt> is next.</p><h3 class="sect2">4.4.4. Loop Control</h3><a name="INDEX-1110"></a><a name="INDEX-1111"></a><a name="INDEX-1112"></a><p>We mentioned that you can put a <em class="replaceable">LABEL</em> on aloop to give it a name. The loop's <em class="replaceable">LABEL</em>identifies the loop for theloop-control operators <tt class="literal">next</tt>,<tt class="literal">last</tt>, and <tt class="literal">redo</tt>. The<em class="replaceable">LABEL</em> names the loop as a whole, not justthe top of the loop. Hence, a loop-control operator referring to theloop doesn't actually "go to" the loop label itself. As far as thecomputer is concerned, the label could just as easily have been placedat the end of the loop. But people like things labeled at the top,for some reason.</p><p>Loops are typically named for the item the loop is processing oneach iteration. This interacts nicely with the loop-control operators,which are designed to read like English when used with an appropriatelabel and a statement modifier. The archetypal loop works onlines, so the archetypal loop label is <tt class="literal">LINE:</tt>, and the archetypalloop-control operator is something like this:<blockquote><pre class="programlisting">next LINE if /^#/; # discard comments</pre></blockquote><a name="INDEX-1113"></a><a name="INDEX-1114"></a><a name="INDEX-1115"></a><a name="INDEX-1116"></a><a name="INDEX-1117"></a><a name="INDEX-1118"></a></p><p>The syntax for the loop-control operators is:<blockquote><pre class="programlisting">last <em class="replaceable">LABEL</em>next <em class="replaceable">LABEL</em>redo <em class="replaceable">LABEL</em></pre></blockquote>The <em class="replaceable">LABEL</em> is optional; if omitted, the operator refers to theinnermost enclosing loop. But if you want to jump past more than onelevel, you must use a <em class="replaceable">LABEL</em> to name the loop you want to affect.That <em class="replaceable">LABEL</em> does not have to be in your lexical scope, though itprobably ought to be. But in fact, the <em class="replaceable">LABEL</em> can be anywhere inyour dynamic scope. If this forces you to jump out of an <tt class="literal">eval</tt> orsubroutine, Perl issues a warning (upon request).</p><p><a name="INDEX-1119"></a><a name="INDEX-1120"></a>Just as you may have as many <tt class="literal">return</tt> operators in a function as you like,you may have as many loop-control operators in a loop as youlike. This is not to be considered wicked or even uncool. Duringthe early days of structured programming, some people insisted thatloops and subroutines have only one entry and one exit. The one-entrynotion is still a good idea, but the one-exit notion has led peopleto write a lot of unnatural code. Much of programming consists oftraversing decision trees. A decision tree naturally starts witha single trunk but ends with many leaves. Write your code with thenumber of loop exits (and function returns) that is natural to theproblem you're trying to solve. If you've declared your variableswith reasonable scopes, everything gets automatically cleaned upat the appropriate moment, no matter how you leave the block.</p><p><a name="INDEX-1121"></a><a name="INDEX-1122"></a><a name="INDEX-1123"></a><a name="INDEX-1124"></a>The <tt class="literal">last</tt> operator immediately exits the loop in question. The<tt class="literal">continue</tt> block, if any, is not executed. The following examplebombs out of the loop on the first blank line:<blockquote><pre class="programlisting">LINE: while (<STDIN>) { last LINE if /^$/; # exit when done with mail header ...}</pre></blockquote><a name="INDEX-1125"></a><a name="INDEX-1126"></a><a name="INDEX-1127"></a></p><p>The <tt class="literal">next</tt> operator skips the rest of the current iteration of theloop and starts the next one. If there is a <tt class="literal">continue</tt> clause onthe loop, it is executed just before the condition is re-evaluated,just like the third component of a three-part <tt class="literal">for</tt> loop. Thusit can be used to increment a loop variable, even when a particulariteration of the loop has been interrupted by a <tt class="literal">next</tt>:<blockquote><pre class="programlisting">LINE: while (<STDIN>) { next LINE if /^#/; # skip comments next LINE if /^$/; # skip blank lines ...} continue { $count++;}</pre></blockquote><a name="INDEX-1128"></a><a name="INDEX-1129"></a></p><p>The <tt class="literal">redo</tt> operator restarts the loop block withoutevaluating the conditional again. The <tt class="literal">continue</tt>block, if any, is not executed. This operator is often used byprograms that want to fib to themselves about what was just input.Suppose you were processing a file that sometimes had a backslash atthe end of a line to continuethe record on the next line. Here's howyou could use <tt class="literal">redo</tt> for that:<blockquote><pre class="programlisting">while (<>) { chomp; if (s/\\$//) { $_ .= <>; redo unless eof; # don't read past each file's eof } # now process $_}</pre></blockquote>which is the customary Perl shorthand for the more explicitly (andtediously) written version:<blockquote><pre class="programlisting">LINE: while (defined($line = <ARGV>)) { chomp($line); if ($line =~ s/\\$//) { $line .= <ARGV>; redo LINE unless eof(ARGV); } # now process $line}</pre></blockquote><a name="INDEX-1130"></a>Here's an example from a real program that uses all three loop-controloperators. Although this particular strategy of parsing command-linearguments is less common now that we have the <tt class="literal">Getopts::*</tt> modulesbundled with Perl, it's still a nice illustration of the use ofloop-control operators on named, nested loops:<blockquote><pre class="programlisting">ARG: while (@ARGV && $ARGV[0] =~ s/^-(?=.)//) { OPT: for (shift @ARGV) { m/^$/ && do { next ARG; }; m/^-$/ && do { last ARG; }; s/^d// && do { $Debug_Level++; redo OPT; }; s/^l// && do { $Generate_Listing++; redo OPT; }; s/^i(.*)// && do { $In_Place = $1 || ".bak"; next ARG; }; say_usage("Unknown option: $_"); }}</pre></blockquote><a name="INDEX-1131"></a><a name="INDEX-1132"></a>One more point about loop-control operators. You may have noticedthat we are not calling them "statements". That's because theyaren't statements--although like any expression, they can be usedas statements. You can almost think of them as unary operatorsthat just happen to cause a change in control flow. So you can usethem anywhere it makes sense to use them in an expression. In fact,you can even use them where it doesn't make sense. One sometimessees this coding error:<blockquote><pre class="programlisting">open FILE, $file or warn "Can't open $file: $!\n", next FILE; # WRONG</pre></blockquote>The intent is fine, but the <tt class="literal">nextFILE</tt> is being parsed as one of the arguments to<tt class="literal">warn</tt>, which is a list operator. So the<tt class="literal">next</tt> executes before the <tt class="literal">warn</tt>gets a chance to emit the warning. In this case, it's easily fixed byturning the <tt class="literal">warn</tt> list operator into the<tt class="literal">warn</tt> function call with some suitably situatedparentheses:<blockquote><pre class="programlisting">open FILE, $file or warn("Can't open $file: $!\n"), next FILE; # okay</pre></blockquote>However, you might find it easier to read this:<blockquote><pre class="programlisting">unless (open FILE, $file) { warn "Can't open $file: $!\n"; next FILE;}</pre></blockquote></p><a name="INDEX-1133"></a><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch04_03.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch04_05.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">4.3. if and unless Statements</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">4.5. Bare Blocks</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -