Learning Perl 4ed Reading Notes - Chapter4 Subroutines

1. 有关function和subroutine。本书中的function指的是Perl built-in的函数,subroutine指的是user-defined 函数,本质上来说,function和subroutine是一回事。和Pascal不一样,Pascal中的function和subroutine是 不一样的,function有返回值,而subroutine是没有返回值的。

2. Defining a Subroutine. 很简单:

Code: Select all
sub marine {
  $n += 1;  # Global variable $n
  print "Hello, sailor number $n!\n";
}


用 关键字sub就定义了一个名为marine的subroutine,注意subroutine中的n变量是一个global variable,后面会介绍如何定义一个局部变量。Perl中的subroutine的代码可以放置在代码的任何地方,一般我们都会把 subroutine的代码放在开头部分(和C的习惯一样)。如果我们在代码中定义了两个同名的subroutine,那么后定义的subroutine 会覆盖前面那个subroutine。

3. Invoking a Subroutine. 很简单,直接用&就表示调用一个subroutine:

&marine; # says Hello, sailor number 1!
&marine; # says Hello, sailor number 2!
&marine; # says Hello, sailor number 3!
&marine; # says Hello, sailor number 4!

4. Return Values. 这一点Perl和其他的语言很不一样。Perl默认会把subroutine中最后一句被执行的代码的返回值/执行结果作为整个subroutine的return value,除非我们显式的用return语句。

Code: Select all
sub sum_of_fred_and_barney {
  print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
  $fred + $barney;  # That's the return value
}


上例中,$fred + $barney的结果就是整个subroutine的返回值。所以,千万注意最后一句代码,免得返回值出错哦,比如:

Code: Select all
sub sum_of_fred_and_barney {
  print "Hey, you called the sum_of_fred_and_barney subroutine!\n";
  $fred + $barney;  # That's not really the return value!
  print "Hey, I'm returning a value now!\n";      # Oops!
}


最后多了一句print,所以,整个subroutine的return value就发生变化了,就变成print的返回值了,print在成功的时候返回1(true),打印失败返回0。

再看这个例子:

Code: Select all
sub larger_of_fred_or_barney {
  if ($fred > $barney) {
    $fred;
  } else {
    $barney;
  }
}


subroutine的返回值是最后一句被执行的代码,不是最后一句代码哦,所以上例中,subroutine会返回两个变量中较大的那个。

5. Arguments. 本节讲述如何定义和使用subroutine arguments。Perl中给subroutine传递参数是通过List的方式做的:

$n = &max(10, 15); # This sub call has two parameters

这 样就把List (10, 15)传递给了max subroutine,此时Perl会把这个List存在Perl的默认Array @_中,然后我们用$_[0], $_[1]...这样的方式就可以引用这些arguments。注意这里其实变量是_,加上了@符号就表示引用整个List,用$符号就表示引用List 中的一个元素,这在第三章已经详细讨论过了。第三章中讲foreach结构的时候我们还讲述了$_这个default variable,意思和这里的是一样的。

Code: Select all
sub max {
  # Compare this to &larger_of_fred_or_barney
  if ($_[0] > $_[1]) {
    $_[0];
  } else {
    $_[1];
  }
}


于 是代码就变成了上面的样子。但是上面的代码无疑是可读性较差的,下一节会讲述如何改变这种状况。而且上面的代码还有一个问题,就是调用max的时候给了三 个参数怎么办?很显然,第三个参数会被ignore;如果调用max的时候给了一个参数的话,第二个参数就是undef。

OK, 本节最后讲一个非常重要的东西:每个subroutine都有一个自己的@_,比如我们一个subroutine中调用另外一个subroutine(带 参数的),那么这两个subroutine都有自己的@_,我们不用担心调用了其他的subroutine会影响到自己的@_ array,Perl会为我们自动处理这些事情,所以不用担心。

6. Subroutine中的局部变量(Private Variable)。如下, 使用my这个关键字:

Code: Select all
sub max {
  my($m, $n);       # new, private variables for this block
  ($m, $n) = @_;    # give names to the parameters
  if ($m > $n) { $m } else { $n }
}


上 例中,我们用my关键字定义了一个List,然后将参数array @_赋给了这个list,所以,后面我们可以用$m, $n来表示两个参数了。而且$m和$n是max subroutine的局部变量,和别人没关系。没有使用my定义的变量,Perl都把他们认为是global variable。

上面的代码中还有一个注意点,请注意最后一句代码中,$m和$n后面都没有分号结尾,这是Perl的一个规定,在if从句的最后一句代码处,可以不写分号,但是规范一点我们还是写上比较好,上面的例子只是代码非常简单,而且可以写在一行,所以就省略了分号。

将上面的代码再精简一些,就成了非常常用的代码模板了:

Code: Select all
sub max {
  my($m, $n) = @_;  # Name the subroutine parameters
  if ($m > $n) { $m } else { $n }
}


7. Variable-Length Parameter List. 本节讲述如何处理可变长度的参数列表,也就是说,本节中实现的max不限参数个数的多少,一律给出这些参数中最大值的那个。首先来看如何没有这种可变长度 的结构,我们规定max只能接受两个参数的话,那么max subroutine可能会这样写来增强容错性:

Code: Select all
sub max {
  if (@_ != 2) {
    print "WARNING! &max should get exactly two arguments!\n";
  }
  # continue as before...
  .
  .
  .
}


OK?把@_用在了Scalar Context中,@_就返回参数的个数。

然后看如何不限参数个数:

Code: Select all
$maximum = &max(3, 5, 10, 4, 6);

sub max {
  my($max_so_far) = shift @_;  # the first one is the largest yet seen
  foreach (@_) {               # look at the remaining arguments
    if ($_ > $max_so_far) {    # could this one be bigger yet?
      $max_so_far = $_;
    }
  }
  $max_so_far;
}


上述的代码比较好理解,首先我们用shift取出第一个元素,同时List少掉了一个元素,然后用foreach循环遍历Array,一个个的相 比,然后给出最大值的那个max_so_far。这里看foreach中我们没有明确定义control variable,所以,就可以用$_来表示control variable,这是第三章学的内容。用这样的代码就可以处理变长的参数列表了。

最后需要考虑的问题是,上述的代码能应付empty parameter list么?来看一下,如果是empty list,那么shift返回undef,foreach结构中的代码一次都不会被执行,最后返回的result就是undef,OK,看来这也是期望的 结果。不过这只是一个例子,我们在书写任何subroutine的时候,都要考虑参数列表是空列表的情况哦。

你可能感兴趣的:(reading)