📄 ch08_03.htm
字号:
<blockquote><pre class="programlisting">print "We need @{ [$n + 5] } widgets!\n";</pre></blockquote>Be careful though: square brackets supply a list context to theirexpression. In this case it doesn't matter, although the earlier callto <tt class="literal">mysub</tt> might care. When it does matter, use anexplicit <tt class="literal">scalar</tt> to force the context:<blockquote><pre class="programlisting">print "mysub returns @{ [scalar mysub(1,2,3)] } now.\n";</pre></blockquote></p><a name="ch08-sect-closure"></a><h3 class="sect2">8.3.7. Closures</h3><a name="INDEX-2031"></a><a name="INDEX-2032"></a><a name="INDEX-2033"></a><p>Earlier we talked about creating anonymous subroutines with a nameless<tt class="literal">sub {}</tt>. You can think of those subroutines asdefined at run time, which means that they have a time of generationas well as a location of definition. Some variables might be in scopewhen the subroutine is created, and different variables might be inscope when the subroutine is called.</p><p>Forgetting about subroutines for a moment, consider a reference thatrefers to a lexical variable:<blockquote><pre class="programlisting">{ my $critter = "camel"; $critterref = \$critter;}</pre></blockquote>The value of <tt class="literal">$$critterref</tt> will remain"<tt class="literal">camel</tt>" even though <tt class="literal">$critter</tt>disappears after the closing curly brace. But<tt class="literal">$critterref</tt> could just as well have referred to asubroutine that refers to <tt class="literal">$critter</tt>:<blockquote><pre class="programlisting">{ my $critter = "camel"; $critterref = sub { return $critter };}</pre></blockquote>This is a <em class="emphasis">closure</em>, which is a notion out of the functionalprogramming world of LISP and Scheme.<a href="#FOOTNOTE-6">[6]</a> It means that when you define an anonymousfunction in a particular lexical scope at a particular moment, itpretends to run in that scope even when later called from outside thatscope. (A purist would say it doesn't have to pretend--it actually<em class="emphasis">does</em> run in that scope.)</p><blockquote class="footnote"><a name="FOOTNOTE-6"></a><p>[6] In this context, theword "functional" should not be construed as an antonym of"dysfunctional".</p></blockquote><p>In other words, you are guaranteed to get the same copy of a lexicalvariable each time, even if other instances of that lexical variablehave been created before or since for other instances of that closure.This gives you a way to set values used in a subroutine when you defineit, not just when you call it.</p><p><a name="INDEX-2034"></a>You can also think of closures as a way to write a subroutine templatewithout using <tt class="literal">eval</tt>. The lexical variables act asparameters for filling in the template, which is useful for setting uplittle bits of code to run later. These are commonly called<em class="emphasis">callbacks</em> in event-based programming, where youassociate a bit of code with a keypress, mouse click, window exposure,and so on. When used as callbacks, closures do exactly what youexpect, even if you don't know the first thing about functionalprogramming. (Note that this closure business only applies to<tt class="literal">my</tt> variables. Global variables work as they'vealways worked, since they're neither created nor destroyed the waylexical variables are.)</p><p><a name="INDEX-2035"></a>Another use for closures is within <em class="emphasis">functiongenerators</em>; that is, functions that create and return brandnew functions. Here's an example of a function generator implementedwith closures:<blockquote><pre class="programlisting">sub make_saying { my $salute = shift; my $newfunc = sub { my $target = shift; print "$salute, $target!\n"; }; return $newfunc; # Return a closure}$f = make_saying("Howdy"); # Create a closure$g = make_saying("Greetings"); # Create another closure# Time passes...$f->("world");$g->("earthlings");</pre></blockquote>This prints:<blockquote><pre class="programlisting">Howdy, world!Greetings, earthlings!</pre></blockquote>Note in particular how <tt class="literal">$salute</tt> continues to refer to the actualvalue passed into <tt class="literal">make_saying</tt>, despite the fact that the <tt class="literal">my $salute</tt> has gone out of scope by the time the anonymous subroutineruns. That's what closures are all about. Since <tt class="literal">$f</tt> and <tt class="literal">$g</tt> holdreferences to functions that, when called, still need access to thedistinct versions of <tt class="literal">$salute</tt>, those versions automatically stickaround. If you now overwrite <tt class="literal">$f</tt>, <em class="emphasis">its</em> version of <tt class="literal">$salute</tt> wouldautomatically disappear. (Perl only cleans up when you're not looking.)</p><p><a name="INDEX-2036"></a><a name="INDEX-2037"></a><a name="INDEX-2038"></a>Perl doesn't provide references to object methods (described in<a href="ch12_01.htm">Chapter 12, "Objects"</a>) but you can get a similar effect using aclosure. Suppose you want a reference not just to the subroutine themethod represents, but one which, when invoked, would call that methodon a particular object. You can conveniently remember both the objectand the method as lexical variables bound up inside a closure:<blockquote><pre class="programlisting">sub get_method_ref { my ($self, $methodname) = @_; my $methref = sub { # the @_ below is not the same as the one above! return $self->$methodname(@_); }; return $methref;}my $dog = new Doggie:: Name => "Lucky", Legs => 3, Tail => "clipped";our $wagger = get_method_ref($dog, 'wag');$wagger->("tail"); # Calls $dog->wag('tail').</pre></blockquote>Not only can you get Lucky to wag what's left of his tail now, evenonce the lexical <tt class="literal">$dog</tt> variable has gone out of scope and Luckyis nowhere to be seen, the global <tt class="literal">$wagger</tt> variable can still gethim to wag his tail, wherever he is.</p><a name="ch08-sect-clos"></a><h3 class="sect3">8.3.7.1. Closures as function templates</h3><p><a name="INDEX-2039"></a><a name="INDEX-2040"></a>Using a closure as a function template allows you to generate manyfunctions that act similarly. Suppose you want a suite of functions thatgenerate HTML font changes for various colors:<blockquote><pre class="programlisting">print "Be ", red("careful"), "with that ", green("light"), "!!!";</pre></blockquote>The <tt class="literal">red</tt> and <tt class="literal">green</tt> functions would be very similar. We'd like toname our functions, but closures don't have names since they're justanonymous subroutines with an attitude. To get around that, we'llperform the cute trick of naming our anonymous subroutines. You canbind a coderef to an existing name by assigning it to a typeglob ofthe name of the function you want. (See the section <a href="ch10_01.htm#ch10-sect-st">Section 8.1, "Symbol Tables"</a> in <a href="ch10_01.htm">Chapter 10, "Packages"</a>. In this case, we'll bind it to twodifferent names, one uppercase and one lowercase:<blockquote><pre class="programlisting">@colors = qw(red blue green yellow orange purple violet);for my $name (@colors) { no strict 'refs'; # Allow symbolic references *$name = *{uc $name} = sub { "<FONT COLOR='$name'7gt;@_</FONT>" };}</pre></blockquote>Now you can call functions named <tt class="literal">red</tt>,<tt class="literal">RED</tt>, <tt class="literal">blue</tt>,<tt class="literal">BLUE</tt>, and so on, and the appropriate closure willbe invoked. This technique reduces compile time and conserves memory,and is less error-prone as well, since syntax checks happen duringcompilation. It's critical that any variables in the anonymoussubroutine be lexicals in order to create a closure. That's thereason for the <tt class="literal">my</tt> above.</p><p><a name="INDEX-2041"></a>This is one of the few places where giving a prototype to a closuremakes sense. If you wanted to impose scalar context on the argumentsof these functions (probably not a wise idea for thisexample), you could have written it this way instead:<blockquote><pre class="programlisting">*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };</pre></blockquote>That's almost good enough. However, since prototype checking happensduring compile time, the run-time assignment above happens too late tobe of much use. You could fix this by putting the whole loop ofassignments within a <tt class="literal">BEGIN</tt> block, forcing it tooccur during compilation. (More likely, you'd put it out in a modulethat you <tt class="literal">use</tt> at compile time.) Then the prototypeswill be visible during the rest of the compilation.</p><h3 class="sect3">8.3.7.2. Nested subroutines</h3><p><a name="INDEX-2042"></a><a name="INDEX-2043"></a><a name="INDEX-2044"></a>If you are accustomed (from other programming languages) to usingsubroutines nested within other subroutines, each with their ownprivate variables, you'll have to work at it a bit in Perl. Namedsubroutines do not nest properly, although anonymous onesdo.<a href="#FOOTNOTE-7">[7]</a> Anyway, wecan emulate nested, lexically scoped subroutines using closures. Here's anexample:<blockquote><pre class="programlisting">sub outer { my $x = $_[0] + 35; local *inner = sub { return $x * 19 }; return $x + inner();}</pre></blockquote>Now <tt class="literal">inner</tt> can only be called from within <tt class="literal">outer</tt>, because ofthe temporary assignments of the closure. But when it is, it has normalaccess to the lexical variable <tt class="literal">$x</tt> from the scope of <tt class="literal">outer</tt>.</p><blockquote class="footnote"><a name="FOOTNOTE-7"></a><p>[7]To be more precise, globally named subroutinesdon't nest. Unfortunately, that's the only kind of named subroutinedeclaration we have. We haven't yet implemented lexically scoped,named subroutines (known as <tt class="literal">my sub</tt>s), but when we do,they should nest correctly.</p></blockquote><p>This has the interesting effect of creating a function local to anotherfunction, something not normally supported in Perl. Because <tt class="literal">local</tt>is dynamically scoped, and because function names are global to theirpackage, any other function that <tt class="literal">outer</tt> called could also call thetemporary version of <tt class="literal">inner</tt>. To prevent that, you'd needan extra level of indirection:<blockquote><pre class="programlisting">sub outer { my $x = $_[0] + 35; my $inner = sub { return $x * 19 }; return $x + $inner->();}</pre></blockquote></p><a name="INDEX-2045"></a><a name="INDEX-2046"></a><a name="INDEX-2047"></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="ch08_02.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="ch08_04.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">8.2. Creating References</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">8.4. Symbolic References</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 + -