📄 ch11_05.htm
字号:
<HTML><HEAD><TITLE>Recipe 11.4. Taking References to Functions (Perl Cookbook)</TITLE><METANAME="DC.title"CONTENT="Perl Cookbook"><METANAME="DC.creator"CONTENT="Tom Christiansen & Nathan Torkington"><METANAME="DC.publisher"CONTENT="O'Reilly & Associates, Inc."><METANAME="DC.date"CONTENT="1999-07-02T01:40:33Z"><METANAME="DC.type"CONTENT="Text.Monograph"><METANAME="DC.format"CONTENT="text/html"SCHEME="MIME"><METANAME="DC.source"CONTENT="1-56592-243-3"SCHEME="ISBN"><METANAME="DC.language"CONTENT="en-US"><METANAME="generator"CONTENT="Jade 1.1/O'Reilly DocBook 3.0 to HTML 4.0"><LINKREV="made"HREF="mailto:online-books@oreilly.com"TITLE="Online Books Comments"><LINKREL="up"HREF="ch11_01.htm"TITLE="11. References and Records"><LINKREL="prev"HREF="ch11_04.htm"TITLE="11.3. Taking References to Hashes"><LINKREL="next"HREF="ch11_06.htm"TITLE="11.5. Taking References to Scalars"></HEAD><BODYBGCOLOR="#FFFFFF"><img alt="Book Home" border="0" src="gifs/smbanner.gif" usemap="#banner-map" /><map name="banner-map"><area shape="rect" coords="1,-2,616,66" href="index.htm" alt="Perl Cookbook"><area shape="rect" coords="629,-11,726,25" href="jobjects/fsearch.htm" alt="Search this book" /></map><div class="navbar"><p><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch11_04.htm"TITLE="11.3. Taking References to Hashes"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 11.3. Taking References to Hashes"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><B><FONTFACE="ARIEL,HELVETICA,HELV,SANSERIF"SIZE="-1"><ACLASS="chapter"REL="up"HREF="ch11_01.htm"TITLE="11. References and Records"></A></FONT></B></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch11_06.htm"TITLE="11.5. Taking References to Scalars"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 11.5. Taking References to Scalars"BORDER="0"></A></TD></TR></TABLE></DIV><DIVCLASS="sect1"><H2CLASS="sect1"><ACLASS="title"NAME="ch11-28899">11.4. Taking References to Functions</A></H2><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch11-pgfId-476">Problem<ACLASS="indexterm"NAME="ch11-idx-1000004810-0"></A><ACLASS="indexterm"NAME="ch11-idx-1000004810-1"></A></A></H3><PCLASS="para">You need to manipulate a subroutine by reference. This might happen if you need to create a signal handler, a Tk callback, or a hash of function pointers.</P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch11-pgfId-482">Solution</A></H3><PCLASS="para">To get a code reference:</P><PRECLASS="programlisting">$cref = \&func;$cref = sub { ... };</PRE><PCLASS="para">To call a code reference:</P><PRECLASS="programlisting">@returned = $cref->(@arguments);@returned = &$cref(@arguments);</PRE></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch11-pgfId-498">Discussion</A></H3><PCLASS="para">If the name of a function is <CODECLASS="literal">func</CODE>, you can produce a reference to this code by preceding that name with <CODECLASS="literal">\&</CODE>. You can also create anonymous functions using the <CODECLASS="literal">sub</CODE> <CODECLASS="literal">{}</CODE> notation. These code references can be stored just like any other reference.</P><PCLASS="para">Perl 5.004 introduced the postfix arrow notation for dereferencing a code reference. Prior to that, to call a subroutine by reference, you had to say <CODECLASS="literal">&$funcname(@ARGS)</CODE>, where <CODECLASS="literal">$funcname</CODE> contained the name of a function. Although it is still possible to store the name of a function in a variable, such as:</P><PRECLASS="programlisting">$funcname = "thefunc";&$funcname();</PRE><PCLASS="para">that's not a very good solution for several reasons. First, it uses symbolic references, not real (hard) references, so it is forbidden under the <CODECLASS="literal">use</CODE> <CODECLASS="literal">strict</CODE> <CODECLASS="literal">'refs'</CODE> pragma. Symbolic references to variables are usually a bad idea, since they can't access lexical variables, only globals, and aren't reference counted.</P><PCLASS="para">Second, it doesn't include package information, so if executed in a different package, it would try to call the wrong function. Finally, in the odd case that the function were redefined at some point, the symbolic reference would get whatever the current definition for the function was, whereas the hard reference would retain its old definition.</P><PCLASS="para">Instead of placing the name of the function in the variable, use the backslash operator to create a reference to the function. This is the normal way to store a function in a variable or pass it to another function. You can mix and match references to named functions with references to unnamed ones:</P><PRECLASS="programlisting">my %commands = ( "happy" => \&joy, "sad" => \&sullen, "done" => sub { die "See ya!" }, "mad" => \&angry,);print "How are you? ";chomp($string = <STDIN>);if ($commands{$string}) { $commands{$string}->();} else { print "No such command: $string\n";} </PRE><PCLASS="para">If you create an anonymous function that refers to a lexical (<CODECLASS="literal">my</CODE>) variable from an enclosing scope, Perl's reference counting ensures that the lexical variable is never deallocated so long as that function reference exists:</P><PRECLASS="programlisting">sub counter_maker { my $start = 0; return sub { # this is a closure return $start++; # lexical from enclosing scope };}$counter = counter_maker();for ($i = 0; $i < 5; $i ++) { print &$counter, "\n";}</PRE><PCLASS="para">Even though <CODECLASS="literal">counter_maker</CODE> has ended and <CODECLASS="literal">$start</CODE> has gone out of scope, Perl doesn't free it because the anonymous subroutine referenced by <CODECLASS="literal">$counter</CODE> still has a reference to <CODECLASS="literal">$start</CODE>. If we call <CODECLASS="literal">counter_maker</CODE> again, it'll return another anonymous subroutine reference that uses a <EMCLASS="emphasis">different</EM> <CODECLASS="literal">$start</CODE>:</P><PRECLASS="programlisting">$counter1 = counter_maker();$counter2 = counter_maker();for ($i = 0; $i < 5; $i ++) { print &$counter1, "\n";}print &$counter1, " ", &$counter2, "\n";<CODECLASS="userinput"><B><CODECLASS="replaceable"><I>0</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>1</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>2</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>3</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>4</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>5 0</I></CODE></B></CODE></PRE><PCLASS="para"><ACLASS="indexterm"NAME="ch11-idx-1000004818-0"></A>Closures are often used in callback routines. In graphical and other event-based programming, you associate code with a keypress, mouse click, window expose event, etc. The code will be called much later, probably from an entirely different scope. Variables mentioned in the closure must be available when it's finally called. To work properly, such variables must be lexicals, not globals.</P><PCLASS="para">Another use for closures is function generators, that is, functions that create and return brand new functions. <CODECLASS="literal">counter_maker</CODE> is a function generator. Here's another simple one:</P><PRECLASS="programlisting">sub timestamp { my $start_time = time(); return sub { return time() - $start_time };} $early = timestamp(); sleep 20; $later = timestamp(); sleep 10;printf "It's been %d seconds since early.\n", $early->();printf "It's been %d seconds since later.\n", $later->();<CODECLASS="userinput"><B><CODECLASS="replaceable"><I>It's been 30 seconds since early.</I></CODE></B></CODE><CODECLASS="userinput"><B><CODECLASS="replaceable"><I>It's been 10 seconds since later.</I></CODE></B></CODE></PRE><PCLASS="para">Each call to <CODECLASS="literal">timestamp</CODE> generates and returns a brand new function. The <CODECLASS="literal">timestamp</CODE> function creates a lexical called <CODECLASS="literal">$start_time</CODE> that contains the current clock time (in epoch seconds). Every time that closure is called, it returns how many seconds have passed since it was created by subtracting its starting time from the current time.<ACLASS="indexterm"NAME="ch11-idx-1000004812-0"></A><ACLASS="indexterm"NAME="ch11-idx-1000004812-1"></A><ACLASS="indexterm"NAME="ch11-idx-1000004812-2"></A></P></DIV><DIVCLASS="sect2"><H3CLASS="sect2"><ACLASS="title"NAME="ch11-pgfId-614">See Also</A></H3><PCLASS="para">The section on <ACLASS="olink"HREF="../prog/ch04_03.htm#PERL2-CH-4-SECT-3.6">"Closures"</A> in <ACLASS="olink"HREF="../prog/ch04_01.htm">Chapter 4</A> of <ACLASS="citetitle"HREF="../prog/index.htm"TITLE="Programming Perl"><CITECLASS="citetitle">Programming Perl</CITE></A> and the discussion on closures in <ICLASS="filename">perlref </I>(1); <ACLASS="xref"HREF="ch10_12.htm"TITLE="Prototyping Functions">Recipe 10.11</A>; <ACLASS="xref"HREF="ch11_05.htm"TITLE="Taking References to Functions">Recipe 11.4</A></P></DIV></DIV><DIVCLASS="htmlnav"><P></P><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><TABLEWIDTH="684"BORDER="0"CELLSPACING="0"CELLPADDING="0"><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch11_04.htm"TITLE="11.3. Taking References to Hashes"><IMGSRC="../gifs/txtpreva.gif"ALT="Previous: 11.3. Taking References to Hashes"BORDER="0"></A></TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="book"HREF="index.htm"TITLE="Perl Cookbook"><IMGSRC="../gifs/txthome.gif"ALT="Perl Cookbook"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228"><ACLASS="sect1"HREF="ch11_06.htm"TITLE="11.5. Taking References to Scalars"><IMGSRC="../gifs/txtnexta.gif"ALT="Next: 11.5. Taking References to Scalars"BORDER="0"></A></TD></TR><TR><TDALIGN="LEFT"VALIGN="TOP"WIDTH="228">11.3. Taking References to Hashes</TD><TDALIGN="CENTER"VALIGN="TOP"WIDTH="228"><ACLASS="index"HREF="index/index.htm"TITLE="Book Index"><IMGSRC="../gifs/index.gif"ALT="Book Index"BORDER="0"></A></TD><TDALIGN="RIGHT"VALIGN="TOP"WIDTH="228">11.5. Taking References to Scalars</TD></TR></TABLE><HRALIGN="LEFT"WIDTH="684"TITLE="footer"><FONTSIZE="-1"></DIV<!-- LIBRARY NAV BAR --> <img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p> <a href="copyrght.htm">Copyright © 2002</a> O'Reilly & Associates. All rights reserved.</font> </p> <map name="library-map"> <area shape="rect" coords="1,0,85,94" href="../index.htm"><area shape="rect" coords="86,1,178,103" href="../lwp/index.htm"><area shape="rect" coords="180,0,265,103" href="../lperl/index.htm"><area shape="rect" coords="267,0,353,105" href="../perlnut/index.htm"><area shape="rect" coords="354,1,446,115" href="../prog/index.htm"><area shape="rect" coords="448,0,526,132" href="../tk/index.htm"><area shape="rect" coords="528,1,615,119" href="../cookbook/index.htm"><area shape="rect" coords="617,0,690,135" href="../pxml/index.htm"></map> </BODY></HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -