📄 ch3.htm
字号:
$y = $list->[1];</FONT></TT>
</BLOCKQUOTE>
<P>
Note that the same result of assigning values to <TT><FONT FACE="Courier">$x</FONT></TT>,
<TT><FONT FACE="Courier">$y</FONT></TT>, and <TT><FONT FACE="Courier">$z</FONT></TT>
could be achieved by these two lines of code:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">($x,$y,$z) = @$list;<BR>
$x = $list->[0];</FONT></TT>
</BLOCKQUOTE>
<P>
This works because you are dereferencing what <TT><FONT FACE="Courier">$list</FONT></TT>
points to and using it as an array, which in turn is assigned
to the list <TT><FONT FACE="Courier">($x,$y,$z)</FONT></TT>. <TT><FONT FACE="Courier">$x</FONT></TT>
is still assigned with the <TT><FONT FACE="Courier">-></FONT></TT>
operator.
<P>
When working with hashes or arrays, dereferencing by <TT><FONT FACE="Courier">-></FONT></TT>
is like a dollar-sign (<TT><FONT FACE="Courier">$</FONT></TT>)
dereference. When accessing individual array elements, you are
often faced with writing statements like these two:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$$names[0] = "Kamran";<BR>
$names->[0] = "Kamran";</FONT></TT>
</BLOCKQUOTE>
<P>
Both lines are equivalent. The substring <TT><FONT FACE="Courier">"$names"</FONT></TT>
in the first line has been replaced with the <BR>
<TT><FONT FACE="Courier">-></FONT></TT> operator to create
the second line. The same procedure can be applied for hash operations:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$$lastnames{"Kamran"} = "Husain";
<BR>
$lastnames->{"Kamran"} = "Husain";</FONT></TT>
</BLOCKQUOTE>
<P>
Arrays in Perl can be created with a fixed size set to the value
of the highest index that is used. They do not have to remain
at this size, though, and can grow on demand. Referencing them
for the first time creates the array and space for the item that
is being indexed in the array. Referencing the array again at
different indexes creates those elements at the indexed references
if they do not already exist. Array references can be created
automatically when first referenced in the left side of an equation.
Using a reference such as <TT><FONT FACE="Courier">$array[$i]</FONT></TT>
creates an array into which you can index with <TT><FONT FACE="Courier">$i</FONT></TT>.
Such is the case with scalars and even multidimensional arrays.
<H2><A NAME="ReferencestoSubroutines"><FONT SIZE=5 COLOR=#FF0000>References
to Subroutines</FONT></A></H2>
<P>
Just as you can reference individual items such as arrays and
scalar variables, you can also point to subroutines. In C, this
would be akin to pointing to a function. To construct such a reference,
you use a statement like this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$pointer_to_sub = sub { ... <I>declaration
of sub</I> ... } ;</FONT></TT>
</BLOCKQUOTE>
<P>
Note the use of the semicolon at the end of the <TT><FONT FACE="Courier">sub()</FONT></TT>
declaration. The subroutine pointed to by <TT><FONT FACE="Courier">$pointer_to_sub</FONT></TT>
points to the same function reference even if the statement is
placed in a loop. This feature in Perl lets you declare several
anonymous <TT><FONT FACE="Courier">sub()</FONT></TT> functions
in a loop without worrying about the fact that you are chewing
up memory by declaring the same function over and over as you
go about in a loop. As you come around the loop and reassign a
scalar to the <TT><FONT FACE="Courier">sub</FONT></TT>, Perl simply
assigns to the same subroutine declared with the first use of
the <TT><FONT FACE="Courier">sub()</FONT></TT> statement.
<P>
To call a referenced subroutine, use this syntax:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">&$pointer_to_sub( parameters );</FONT></TT>
</BLOCKQUOTE>
<P>
This code works because you are dereferencing the <TT><FONT FACE="Courier">$pointer_to_sub</FONT></TT>
and using it with the ampersand (<TT><FONT FACE="Courier">&</FONT></TT>)
as a pointer to a function. The parameters portion may or may
not be empty, depending on how your function is defined. The code
within a sub is simply a declaration created with this statement.
The code within the sub is not executed immediately; however,
it is compiled and set for each use. Consider the script shown
in Listing 3.8.
<HR>
<BLOCKQUOTE>
<B>Listing 3.8. Using references to subroutines.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 <BR>
3 sub print_coor{<BR>
4
my ($x,$y,$z) = @_;<BR>
5
print "$x $y $z \n";<BR>
6
return $x;};<BR>
7 <BR>
8 $k = 1;<BR>
9 $j = 2;<BR>
10 $m = 4;<BR>
11 $this = print_coor($k,$j,$m);<BR>
12 <BR>
13 $that = print_coor(4,5,6);</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
When you execute this listing, you get the following output:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$ <B>test<BR>
</B>1 2 4<BR>
4 5 6</FONT></TT>
</BLOCKQUOTE>
<P>
This output tells you that assignments of <TT><FONT FACE="Courier">$x</FONT></TT>,
<TT><FONT FACE="Courier">$y</FONT></TT>, and <TT><FONT FACE="Courier">$z</FONT></TT>
were done when the first declaration of <TT><FONT FACE="Courier">print_coor</FONT></TT>
was encountered as a call. Each reference to <TT><FONT FACE="Courier">$this</FONT></TT>
and <TT><FONT FACE="Courier">$that</FONT></TT> now points to a
completely different subroutine, the arguments to which were passed
at runtime.
<H3><A NAME="UsingSubroutineTemplates">Using Subroutine Templates</A>
</H3>
<P>
Subroutines are not limited to returning only data types. They
can return references to other subroutines, too. The returned
subroutines run in the context of the calling routine but are
set up in the original routine that created them. This type of
behavior is caused by the way closure is handled in Perl. Closure
means that if you define a function in one context, it runs in
that particular context in which it was first defined. (A book
on object-oriented programming would provide more information
on closure.)
<P>
To see how closure works, look at Listing 3.9, which you can use
to set up different types of error messages. Such subroutines
are useful in creating templates of all error messages.
<HR>
<BLOCKQUOTE>
<B>Listing 3.9. Using closure.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 <BR>
3 sub errorMsg {<BR>
4 my
$lvl = shift;<BR>
5
#<BR>
6
# define the subroutine to run when called.<BR>
7
#<BR>
8 return
sub {<BR>
9 <BR>
10
my $msg = shift; # Define the error type now.<BR>
11
print "Err Level $lvl:$msg\n"; }; # print later.<BR>
12 }
<BR>
13 <BR>
14 $severe = errorMsg("Severe");<BR>
15 $fatal = errorMsg("Fatal");<BR>
16 $annoy = errorMsg("Annoying");<BR>
17 <BR>
18 &$severe("Divide by zero");<BR>
19 &$fatal("Did you forget to use a semi-colon?");
<BR>
20 &$annoy("Uninitialized variable in use");</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
The subroutine <TT><FONT FACE="Courier">errorMsg</FONT></TT> declared
here uses a local variable called <TT><FONT FACE="Courier">lvl</FONT></TT>.
After this declaration, <TT><FONT FACE="Courier">errorMsg</FONT></TT>
uses <TT><FONT FACE="Courier">$lvl</FONT></TT> in the subroutine
it returns back to the caller. Therefore, the value of <TT><FONT FACE="Courier">$lvl</FONT></TT>
is set in the context when the subroutine <TT><FONT FACE="Courier">errorMsg</FONT></TT>
is first called, even though the keyword <TT><FONT FACE="Courier">my</FONT></TT>
is used. Therefore, the following three calls set up three different
<TT><FONT FACE="Courier">$lvl</FONT></TT> variable values, each
in their own context:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$severe = errorMsg("Severe");
<BR>
$fatal = errorMsg("Fatal");<BR>
$annoy = errorMsg("Annoying");</FONT></TT>
</BLOCKQUOTE>
<P>
Now, when the reference to a subroutine is returned by the call
to the <TT><FONT FACE="Courier">errorMsg</FONT></TT> function
in each of the lines above, the value of <TT><FONT FACE="Courier">$lvl</FONT></TT>
within the <TT><FONT FACE="Courier">errorMsg</FONT></TT> function
is retained for each context in which <TT><FONT FACE="Courier">$lvl</FONT></TT>
was declared. Thus, the <TT><FONT FACE="Courier">$msg</FONT></TT>
value from the referenced call is used, but the value of <TT><FONT FACE="Courier">$lvl</FONT></TT>
is the value that was first set in the actual creation of the
function.
<P>
Sound confusing? It is. This is primarily the reason why you do
not see this type of code in most Perl programs.
<H2><A NAME="ImplementingStateMachines"><FONT SIZE=5 COLOR=#FF0000>Implementing
State Machines</FONT></A></H2>
<P>
Using arrays and pointers to subroutines, you can come up with
some nifty applications. Consider using an array of pointers to
subroutines to implement a state machine. Listing 3.10 provides
an example of a simple, asynchronous state machine.
<HR>
<BLOCKQUOTE>
<B>Listing 3.10. A simple, asynchronous state machine.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 # --------------------------------------------------------------
<BR>
3 # Define each state
as subroutine. Then create a<BR>
4 # reference to each subroutine. We have four states here.
<BR>
5 # --------------------------------------------------------------
<BR>
6 $s0 = sub {<BR>
7 local
$a = $_[0];<BR>
8 print
"State 0 processing $a \n";<BR>
9 if
($a eq '0') { return(0); }<BR>
10 if
($a eq '1') { return(1); }<BR>
11 if
($a eq '2') { return(2); }<BR>
12 if
($a eq '3') { return(3); }<BR>
13 return
0;<BR>
14 };
<BR>
15 # --------------------------------------------------------------
<BR>
16 $s1 = sub {<BR>
17 local
$a = shift @_;<BR>
18 print
"State 1 processing $a \n";<BR>
19 if
($a eq '0') { return(0); }<BR>
20 if
($a eq '1') { return(1); }<BR>
21 if
($a eq '2') { return(2); }<BR>
22 if
($a eq '3') { return(3); }<BR>
23 return
1;<BR>
24 };
<BR>
25 # --------------------------------------------------------------
<BR>
26 $s2 = sub {<BR>
27 local
$a = $_[0];<BR>
28 print
"State 2 processing $a \n";<BR>
29 if
($a eq '0') { return(0); }<BR>
30 if
($a eq '1') { return(1); }<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -