• 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的时候,都要考虑参数列表是空列表的情况哦。
  • 相关阅读:
    解决pip3的ImportError: cannot import name 'main'
    linux 安装Python3.6
    Linux安装redis和部署
    redis密码管理
    CentOS7使用firewalld打开关闭防火墙与端口
    scrapy 从Windwos平台移植到 Linux平台之实操
    Linux 环境下安装Maven
    解决:安装Jenkins时web界面出现该jenkins实例似乎已离线
    持续集成工具Jenkins结合SVN的安装和使用
    Linux下的SVN服务器搭建
  • 原文地址:https://www.cnblogs.com/super119/p/1989373.html
Copyright © 2020-2023  润新知