📄 ch3.htm
字号:
31 if
($a eq '2') { return(2); }<BR>
32 if
($a eq '3') { return(3); }<BR>
33 return
2;<BR>
34 };
<BR>
35 # --------------------------------------------------------------
<BR>
36 $s3 = sub {<BR>
37 my $a
= shift @_;<BR>
38 print
"State 3 processing $a \n";<BR>
39 if
($a eq '0') { return(0); }<BR>
40 if
($a eq '1') { return(1); }<BR>
41 if
($a eq '2') { return(2); }<BR>
42 if
($a eq '3') { return(3); }<BR>
43 return
3;<BR>
44 };
<BR>
45 # --------------------------------------------------------------
<BR>
46 # Create an array of pointers to subroutines. The index<BR>
47 # into this array is the current state.<BR>
48 # --------------------------------------------------------------
<BR>
49 @stateTable = ($s0, $s1, $s2, $s3);<BR>
50 # --------------------------------------------------------------
<BR>
51 # Initialize the state to 0.<BR>
52 # --------------------------------------------------------------
<BR>
53 $this = 0;<BR>
54 # --------------------------------------------------------------
<BR>
55 # Implement the state machine.<BR>
56 # set current state to 0<BR>
57 # forever<BR>
58 # get response
<BR>
59 # set current
state to next state based on response.<BR>
60 # --------------------------------------------------------------
<BR>
61 while (1)<BR>
62 {
<BR>
63 print
"\n This state is : $this -> what next? ";<BR>
64 $reply
= <STDIN>;<BR>
65 chop($reply);
<BR>
66 #
<BR>
67 #
Stop the machine here<BR>
68 #
<BR>
69 if
($reply eq 'q') { exit(0); }<BR>
70 print
" Reply = $reply \n";<BR>
71 #
<BR>
72 #
Get the present state function.<BR>
73 #
<BR>
74 $state
= $stateTable[$this];<BR>
75 #
<BR>
76 #
Get the next state from this state.<BR>
77 #
<BR>
78 $next
= &$state($reply);<BR>
79 printf
"Next state = $next from this state $this\n";<BR>
80 #
<BR>
81 #
Now advance present state to next state<BR>
82 #
<BR>
83 $this
= $next;<BR>
84 }</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
Let's see how each function implements the state transitions.
All input into each state consists of removing the initial state
as the first parameter into the subroutine. In Perl, the <TT><FONT FACE="Courier">@_
</FONT></TT>variable is the array of input parameters into a subroutine
and is always defined in each subroutine. In line 37, the <TT><FONT FACE="Courier">shift</FONT></TT>
command forces the first item from the list of input parameters
into <TT><FONT FACE="Courier">$a</FONT></TT>. The value of <TT><FONT FACE="Courier">$a</FONT></TT>
is then used as the current state of the program.
<P>
There are four states in this state machine: <TT><FONT FACE="Courier">S0</FONT></TT>,
<TT><FONT FACE="Courier">S1</FONT></TT>, <TT><FONT FACE="Courier">S2</FONT></TT>,
and <TT><FONT FACE="Courier">S3</FONT></TT>. Each state accepts
input in the form of a number. Each number is used to get the
next state to go to. Note how <TT><FONT FACE="Courier">$a</FONT></TT>
is declared in each state function using the <TT><FONT FACE="Courier">my</FONT></TT>
and <TT><FONT FACE="Courier">local</FONT></TT> types. So if <TT><FONT FACE="Courier">$a</FONT></TT>
has a value of <TT><FONT FACE="Courier">2 </FONT></TT>and receives
an input of <TT><FONT FACE="Courier">3</FONT></TT>, the current
state is <TT><FONT FACE="Courier">2</FONT></TT>, and the program
will do a state transition from <TT><FONT FACE="Courier">2</FONT></TT>
to <TT><FONT FACE="Courier">3</FONT></TT>. After the function
returns, the current state will be <TT><FONT FACE="Courier">3</FONT></TT>.
<P>
Lines 6 through 14 define a subroutine that defines the functionality
of a state. State <TT><FONT FACE="Courier">S0</FONT></TT> transitions
to states <TT><FONT FACE="Courier">S1</FONT></TT> on receiving
a <TT><FONT FACE="Courier">1</FONT></TT>, <TT><FONT FACE="Courier">S2</FONT></TT>
on receiving a <TT><FONT FACE="Courier">2</FONT></TT>, and <TT><FONT FACE="Courier">S3</FONT></TT>
on receiving a <TT><FONT FACE="Courier">3</FONT></TT>. All other
input will not cause a state transition. The other states, <TT><FONT FACE="Courier">{S1,S2,S3}</FONT></TT>,
behave in an analogous way.
<P>
The <TT><FONT FACE="Courier">stateTable</FONT></TT> array is used
to store pointers to each of the functions of the state machine.
The four entries are set in line 49. The initial state is set
to <TT><FONT FACE="Courier">0</FONT></TT>.
<P>
Lines 61 through 84 implement the code for transitioning through
the state machine by accepting input from <TT><FONT FACE="Courier"><STDIN></FONT></TT>
and calling the present state function to handle the input. Line
74 is where you get the pointer to the function handling all input
for each state in the state machine, and line 78 is where the
state-handling function is called. The next state value returned
by the function is set to the present state (<TT><FONT FACE="Courier">$this</FONT></TT>)
in line 83.
<H2><A NAME="PassingMoreThanOneArrayintoaSubro"><FONT SIZE=5 COLOR=#FF0000>Passing
More Than One Array into a Subroutine</FONT></A></H2>
<P>
Having arrays is great for collecting relevant information. Now
you'll see how to work with multiple arrays via subroutines. Passing
one or more arrays into Perl subroutines is done by reference.
However, you have to keep in mind a few subtle things about using
the <TT><FONT FACE="Courier">@_</FONT></TT> symbol when processing
these arrays in the subroutine.
<P>
The <TT><FONT FACE="Courier">@_</FONT></TT> symbol is an array
of all the items in a subroutine. So, if you have a call to a
subroutine as follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$a = 2;<BR>
@b = ("x","y","z"); <BR>
@c = ("cat","mouse","chase"); <BR>
&simpleSub($a,@b,@c); </FONT></TT>
</BLOCKQUOTE>
<P>
the <TT><FONT FACE="Courier">@_</FONT></TT> array within the subroutine
will be (<TT><FONT FACE="Courier">2</FONT></TT>, <TT><FONT FACE="Courier">"x"</FONT></TT>,
<TT><FONT FACE="Courier">"y"</FONT></TT>, <TT><FONT FACE="Courier">"z"</FONT></TT>,
<TT><FONT FACE="Courier">"cat"</FONT></TT>, <TT><FONT FACE="Courier">"mouse"</FONT></TT>,
<TT><FONT FACE="Courier">"chase"</FONT></TT>). That
is, the contents of all the elements will be glued together to
form one long array.
<P>
Obviously, this ability to glue together arrays will be a problem
to deal with if you want to do operations on two distinct arrays
sequentially. For example, if you have a list of names and a list
of phone numbers, you would want to take the first item from the
names array and the first item from the number array and print
an item. Then take the next name and the next number and print
a combination, and so on. If you pass in the contents of the arrays
to a function that simply uses <TT><FONT FACE="Courier">@_</FONT></TT>,
the subroutine will see one long array, the first half of which
will be a list of strings (names) and the second half of which
will be a list of numbers.
<P>
The subroutine would have to split the <TT><FONT FACE="Courier">@_</FONT></TT>
in half into two distinct arrays before it can start processing.
The problem gets more complicated if you were to pass three or
four arrays such as those containing items like address and ZIP
code. Now the subroutine will have to manipulate <TT><FONT FACE="Courier">@_
</FONT></TT>even more to get the required number of arrays.
<P>
The simplest way to handle the passing of multiple arrays into
a subroutine is to use references to arrays in the argument list
to the subroutine. That is, you pass in a reference to each array
that the subroutine will be using. The references will be ordered
in the <TT><FONT FACE="Courier">@_</FONT></TT> array within the
subroutine. The code in the subroutine can dereference each item
in the <TT><FONT FACE="Courier">@_</FONT></TT> to the type of
array being referenced. This procedure is known as <I>passing
by reference</I>. The value of what is being referenced can be
changed by the subroutine. When an explicit value is sent to a
subroutine, (that is, you are <I>passing by value</I>), only the
copy of what is sent on the stack is changed, not the actual value.
In Perl, values are passed by reference unless you send in a constant
number. For example, from the following code:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub doit {<BR>
$_[0] *= 3.141; <BR>
}<BR>
$\="\n"; <BR>
$x = 3; <BR>
print $x;<BR>
doit ($x); <BR>
print $x;<BR>
# The following line will cause an error since you will attempt
to <BR>
# modify a read-only value:<BR>
# doit(3);<BR>
</FONT></TT>
</BLOCKQUOTE>
<P>
you will see the following values being printed:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">3<BR>
9.423</FONT></TT>
</BLOCKQUOTE>
<P>
The second number is the new value of <TT><FONT FACE="Courier">$x</FONT></TT>
after the call to the <TT><FONT FACE="Courier">doit</FONT></TT>
subroutine. Calling the <TT><FONT FACE="Courier">doit</FONT></TT>
subroutine with a constant value such as shown in the commented
lines above will result in an exception with an error message
indicating that your program attempted to modify a read-only value.
The preceeding test confirms that Perl indeed passes values of
variables by reference and not by value. <P>
<CENTER>
<TABLE BORDERCOLOR=#000000 BORDER=1 WIDTH=80%>
<TR VALIGN=TOP><TD ><B>Note</B></TD></TR>
<TR VALIGN=TOP><TD >
<BLOCKQUOTE>
The value of the <TT><FONT FACE="Courier">$\</FONT></TT> system variable is the output separator. In the preceding example, it is set to a newline. By setting the value of <TT><FONT FACE="Courier">$\</FONT></TT> to <TT><FONT FACE="Courier">\n</FONT></TT>,
the <TT><FONT FACE="Courier">print</FONT></TT> statements did not have to prepend a <TT><FONT FACE="Courier">\n</FONT></TT> to any string being printed. It's a matter of style, of course, and you do not have to use the <TT><FONT
FACE="Courier">$\</FONT></TT> variable if you do not want to. The default value of this <TT><FONT FACE="Courier">$\</FONT></TT> variable is null. The <TT><FONT FACE="Courier">$\ </FONT></TT>is useful in instances when you are writing special text records
with the <TT><FONT FACE="Courier">print</FONT></TT> statement that have to have a special record separator such as <TT><FONT FACE="Courier">END\n</FONT></TT> and<TT><FONT FACE="Courier"> RECORDEND\n\n</FONT></TT>.
</BLOCKQUOTE>
</TD></TR>
</TABLE></CENTER>
<P>
<P>
Listing 3.11 provides a sample subroutine that expects a list
of names and a list of phone numbers.
<HR>
<BLOCKQUOTE>
<B>Listing 3.11. Passing multiple arrays into a subroutine.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 #!/usr/bin/perl<BR>
2 <BR>
3 @names = (mickey,
goofy, daffy );<BR>
4 @phones = (5551234, 5554321, 666 );<BR>
5 $i = 0;<BR>
6 sub listem {<BR>
7
my (@a,@b) = @_;<BR>
8
foreach (@a) {<BR>
9
print "a[$i] = ". $a[$i] . " " . "\tb[$i]
= " . $b[$i] ."\n";<BR>
10 &
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -