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