Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4201246
  • 博文数量: 176
  • 博客积分: 10059
  • 博客等级: 上将
  • 技术积分: 4681
  • 用 户 组: 普通用户
  • 注册时间: 2006-03-24 12:27
文章分类

全部博文(176)

文章存档

2012年(1)

2011年(4)

2010年(14)

2009年(71)

2008年(103)

分类:

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.

 

阅读(9496) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~