分类:
2009-04-10 11:32:18
Call a subroutine and pass a variable by value
$value = "foo";
$newvalue = add_bar($value);
print "$value becomes $newvalue";
sub add_bar {
my ($copy) = @_;
$copy .= "bar";
return $copy;
}
RESULT: foo becomes foobar
DISCUSSION:
In any Perl subroutine the special @_ array will always contain aliases
to the variables that are passed in, so it really isn't possible to
pass by value. The trap to avoid is that a subroutine which operates
directly on the @_ elements will actually change the variables aliased
by those elements. If you wish to operate only on the values you must
copy the @_ values into local variables, operate on those local
variables and then return the new result. This will leave the original
variable unchanged.
Call a subroutine and pass a variable by reference
$value = "foo";
add_bar($value);
print "$value";
sub add_bar {
@_[0] .= "bar";
}
RESULT: foobar
DISCUSSION:
In contrast to the previous trick this one takes advantage of Perl's
default behaviour of passing aliases, to change the original variable
more directly. The @_ array is sometimes described as "magical" but it
is really no different from other languages which pass arguments by
reference. There is no need to return the value since the original
variable is directly changed as the subroutine operates on its alias.
Call a subroutine and pass a variable by reference explicitly
$value = "foo";
add_bar(\$value);
print "$value";
sub add_bar {
my ($ref) = @_;
$$ref .= "bar"; #注意这里的调用方式。如果是$ref的话,修改是函数里面的变量,$$ref修改的是函数外面的变量
}
RESULT: foobar
DISCUSSION:
If you prefer to pass your references more explicitly Perl allows you
to use the "\" symbol to reference a variable (such as \$, \@, \&,
or \%). The reference can be stored in a scalar just like any other
value. Dereference that reference back into the original variable
within the subroutine.
Call a subroutine and pass a filehandle
open FH, "/us/docs/independence.txt";
printfile(*FH);
sub printfile {
my $handle = shift;
while (<$handle>) {print};
}
RESULT: When, in the course of human events...
DISCUSSION:
Unlike the last trick, creating a reference to a filehandle is not so
obvious since filehandles do not look like ordinary variables. Whereas
most variables names are preceded with a special punctuation character
which indicates the variables "type" ($ means scalar, for example)
filehandles seem to be missing this characteristic. Perl's typeglob comes to the rescue. The special * ("star") type symbol allows you to refer to filehandles as if they were scalars.
Call a subroutine and pass arguments by name
print_many(text=>'hooray', times=>3);
sub print_many {
my %arg = @_;
while ($arg{times}--) {print "$arg{text} "};
}
RESULT: hooray hooray hooray
DISCUSSION:
You can pass a hash to your subroutine, and thereby name your
arguments. Perl, however will still place your arguments into the @_
array so you must coerce that array back into a hash by assigning it to
a local hash variable.
Call a subroutine and use default values as arguments
print_many(text=>'hooray');
sub print_many {
my %arg = (text=>"", times=>1, @_);
while ($arg{times}--) {print "$arg{text} "};
}
RESULT: hooray
DISCUSSION:
Particularly when using named arguments it is helpful for a subroutine
to allow for the caller to omit some values. In these cases your
subroutine can use default values that are then overwritten by the
actual passed values (if any).
Call a subroutine embedded a in double-quoted string
print "Page requested at @{[get_time()]}";
sub get_time {
return scalar(gmtime);
}
RESULT: Page requested at Thu Apr 27 01:59:08 2000
DISCUSSION: One of Perl's most seductive features is its ability to interpolate
variables within double-quoted strings. This easily allows you to print
out the values of scalars, arrays or hashes embedded within
double-quoted strings like this: "Tonight at the Apollo: $show_name
playing at @show_times." Unfortunately this won't work with
subroutines. You could store the result of the subroutine call in a
temporary scalar variable or split your string up, but a cleaner
(albeit odd-looking) solution is the @{[ ]} wrapper.
Return...
Return multiple values from a subroutine
($ret1, $ret2) = add_bar("foo", "fez");
print "$ret1 and $ret2";
sub add_bar {
my @ret;
foreach (@_) {push @ret, $_."bar"}
return @ret;
}
RESULT: foobar and fezbar
DISCUSSION:
Subroutines can be very flexible about the number of arguments received
and the number of values returned. This is due to the fact that
arguments are passed in using an array, and the fact that Perl can
return an arbitrarily long list of results.
Return a value from a subroutine based on calling context
($ret1, $ret2) = add_bar("foo", "fez");
$ret3 = add_bar("fee", "foz");
print "$ret1 and $ret2, $ret3";
sub add_bar {
my @ret;
foreach (@_) {push @ret, $_."bar"}
wantarray()? return @ret : return "@ret";
}
RESULT: foobar and fezbar, feebar fozbar
DISCUSSION: Using the wantarray
function it is possible for a subroutine to detect if the caller is
expecting to assign the results to an array, or a scalar variable. This
allows the sub to alter its return value for each case. In this example
the sub coerces its array into a string if the caller is assigning the
result to a scalar. How's that for flexible?
Create...
Create a subroutine reference
$say_hi = sub {
my $to_whom = shift;
print "Hello, $to_whom.\n";
};
&$say_hi("Ada");
RESULT: Hello, Ada.
DISCUSSION: Normally when a subroutine is declared with the sub
keyword the next thing you expect to see is the subroutine's name. In
this case however a name is unnecessary since all we really want is to
store a reference to this subroutine in a scalar (which already has a
name). This trick is known as creating an anonymous
subroutine. The scalar reference can be easily coerced back into its
original subroutine by prepending it with the "&" symbol. Take heed
of the required semicolon after the anonymous sub block. That semicolon
is necessary to end the assignment statement, which, in spite of the
formatting is what that "line" of code is really doing.
Create subroutines on-the-fly
$to_do = "print uc";
$greeting = "Hello, Charles!";
eval("$to_do \"$greeting\"");
print $@ if ($@);
RESULT: HELLO, CHARLES!
DISCUSSION: Using the eval
function it is possible to execute a string as if it were a code block.
Here the print and upper-case functions are run on the quoted string
just as if they were part of the program itself. Unlike regular code
blocks however eval will not kill the entire program if you give it
incorrect code. Instead it will silently store its complaints in the
special $@ variable, which can then be checked afterwards.
Create a subroutine that overrides a built-in function
use subs 'int';
sub int{
my $arg = shift;
return CORE::int($arg)*10;
}
print int("5.7");
RESULT: 50
DISCUSSION: Perl's built-in subroutines, like int can be redefined with your own subroutines if you wish. Normally int
would return the integer portion of a number (in this case 5), but for
some reason I wish to return the integer times ten. In order to avoid a
recursive call back to my own subroutine I first call the original int in the CORE
package, and then multiply that by 10 before returning the final
result. You could have accomplished the same thing without using "int"
as the name of your subroutine -- "int_times_10" might seem like a good
idea, but this trick would be just the what you need to easily
dynamically alter existing functionality at runtime.