22 May 2005
This post may be outdated due to it was written on 2005. The links may be broken. The code may be not working anymore. Leave comments if needed.

Bubble

以前在 @Examples[0] is Perl6 里一开始就稍微讲了点子程序,而在 my Perl6 @Examples[3] 中也讲了子程序参数和返回类型。
现在接着讲 sub/子程序

Perl 5 的代码在 Perl 6 中会怎么样?

虽然说 Perl 5 这样的代码在 Perl 6 中也是可行的:
sub say { print qq{"@_"\n}; }
但要注意一点,这里的 @_ 是只读的。下面的代码会出错:
sub cap { $_ = uc $_ for @_ }   # Error: elements of @_ are constant
如果你想要更新 @_, 用 is rw 来指定。
sub swap (*@_ is rw) { @_[0,1] = @_[1,0] }
而预先声明却不定义一个子程序,Perl 5 中可以这么写:
sub foo;
这在 Perl 6 中会报错,Perl 6 必须得这么写:(那三个点是语法的一部分。)
sub foo {...}

如何定义一个全局子程序

我们在 say q:2 '@*Examples.[4] &Perl6()'; 中说用 * 来声明一个真正的全局变量,而子程序也一样:
# from S06.pod
    $*next_id = 0; # 全局变量
    sub *saith($text)  { print "Yea verily, $text" } # 全局子程序

    module A {
        my $next_id = 2;    # hides any global or package $next_id / 隐藏任何全局或包变量 $next_id
        saith($next_id);    # print the lexical $next_id; / 输出词汇变量 $next_id,这里为 2
        saith($*next_id);   # print the global $next_id; / 输出全局变量 $next_id, 这里为 0
    }

    module B {
        saith($next_id);    # Unambiguously the global $next_id / 显然是全局变量 $next_id,这里为 0
    }

参数分配

    在 Perl 5 中因为没有名字参数,所以传递任意几个参数给子程序都是可以的。
    但 Perl 6 不同,如果你定义了两个命名参数却只有传递一个,编译器就会报错。如果你定义了要数组参数而传递了标量也会报错。
  • 而有时候我们不能确定某命名参数是否传递的话,需要在前面加一个 ?. 例子:
    sub func ($a, ?$b) {
        say $a, $b;
    }
    这样调用 &func(1) 和 &func(1, 2) 都不会报错。
  • 有时候你不想通过传递一个带顺序的参数列表给子程序,而想指定某些参数的名字用以传递,在 Perl 6 中这是可以的。我们用 + 来指定一个 Named Parameters/有名参数。例如:
    sub func ($a, ?$b, +$c) {
        say $a, '-', $b, '-', $c;
    }
    &func(1,c => 2); # 输出 1--2
    注意以下几点:
    1. 带名参数是可选的。所以上面用 &func(1) 不会报错,输出 1--
      不过按 S06 的说法,我们可以使用 is required 使之必须。
    2. 带名参数必须在位置参数(顺序参数)的后面。(S06 中虽然这么写,但是我在 pugs 里运行了是不用一定在后面的)
    3. 如果没有提供带名参数,那就按顺序参数来操作。如 &func(1,2,3) 输出 1-2-3。再举个稍微复杂点的例子:
      sub func ($a, ?$b, +$c, ?$d) {
          say $a, '-', $b, '-', $c, '-', $d;
      }
      &func(1,2,3);
      没有提供带名参数,按顺序来操作,结果为 1-2-3-
      而 &func(1,2,3,:c<4>); 的话,结果为 1-2-4-3
    4. 带名参数也可以用 Pair 写法:&func(1,:c<3>); 输出 1--3
    5. + 可以不写。(我在 pugs 里试过不写是可以的。)
  • 对于可选和带名参数来说,可以使用 = 来提供默认值:
    sub func ($a, ?$b = 2) {
        say $a, '-', $b;
    }
    &func(1); # 1-2
  • 还记不记得最上面的 sub swap (*@_ is rw),* 在此的作用是接收所有未被分配的参数。例如:
    sub func ($a, ?$b, [email protected]) {
        say $a, '-', $b, '-', @c, '-';
    }
    
    &func(1,2,3,4,5); # 1-2-345
    这里的 @c 将接收 3,4,5; 如果没有 * 会报错。

is rw/is copy

  • 默认命名参数是原传递参数的别名,并且该命名参数是只读的。如:
    sub func ($a) {
        # $a = 2 会报错,因为默认是只读的
        say $a;
    }
    my $o = 1;
    &func($o);
    这里 $a 是 $o 的别名($a := $o;),且 $a 是只读的。
  • 如果想在子程序里改变 $a 的值并且同时改变 $o 的值。可以使用 is rw.
    sub func ($a is rw) {
        say $a;
        $a = 2;
    }
    my $o = 1;
    &func($o); # 输出 1
    say $o; # $o 变为了 2
  • 如果想在子程序里改变 $a 的值但对应改变 $o 的值,可以使用 is copy.
    sub func ($a is copy) {
        $a = 2;
        say $a;
    }
    my $o = 1;
    &func($o); # 输出 2
    say $o; # $o 还是为 1
  • 可以说 is rw 是按址传递,而 is copy 是按值传递。

OUTER::

如果你在子程序里定义了 $x, 又想获得子程序外面的 $x 的话,可以使用 OUTER::
{ my $a = 1; {
   my $a=2; {
      my $a=3;
      say $a; # 3
      say $OUTER::a; # 2
      say $OUTER::OUTER::a; # 1
}}}

want

在 Perl 5 中如果区别返回值是数组和标量时我们用 wantarray, 在 Perl 6 中将不再有这东西,而用更强大的 want 来代替。例子:
given want { 
   when Scalar {...} # 返回一个标量 
   when List   {...} # 要一个列表
   when 2      {...} # 要两个值
}

Correct me when I'm wrong

Exercise more.


blog comments powered by Disqus