如何在 Perl 6 中将序列作为参数传递?
How can I pass a sequence as a parameter in Perl 6?
在 Perl 6 中,我可以迭代一个文字序列:
.say for 0 ... 3;
我可以绑定到一个标量并迭代它:
my $s := 0 ... 3;
.say for $s;
但我无法绑定到标量,将其作为参数传递,然后迭代:
my $t := 0 ... 3;
show( $t );
sub show ( $seq ) { .say for $seq }
子例程获取类型为 Seq 的单个元素,但不会对其进行迭代。
0
1
2
3
0
1
2
3
(0 1 2 3)
准备参数的过程中是否已经迭代了所有内容?
Scalar 容器中保存的值不会 自动迭代
虽然$s
和$seq
都是标量(又名"variables"),但$s
直接绑定到Seq值,而你的 $seq
绑定到一个中介 Scalar(注意大写 S
)"container",后者又包含 Seq。当与 for
.
等功能一起使用时,标量容器中保存的值 不会 自动迭代
更详细:
my $s := 0 ... 3;
.say for $s;
因为my
变量声明使用直接绑定运算符:=
进行初始化,$s
是直接绑定到单个Seq值0 ... 3
.
这意味着 for
语句看到单个 Seq 值,确定它执行 Iterable 角色,并将其展平(迭代)。
现在考虑一下:
my $s := 0 ... 3;
my $container = $s;
.say for $container;
因为第二个 my
声明使用赋值运算符 =
进行初始化,所以新变量 $container
首先绑定到一个新的标量容器,然后 "contains" 无论如何被分配。
为了与语言范围保持一致 Slurpy Conventions(特别是:"An Iterable inside a Scalar container does not count"),for
语句不会迭代标量容器中保存的值,因此 .say for $container
行只做一个 say
.
类似的情况适用于您原来的 show
例程,因为默认情况下变量参数声明(语义上)是容器。
一种选择是向 $seq
参数添加 is raw
特征:
sub show ( $seq is raw ) { .say for $seq }
这会阻止 $seq
作为调用 show
.
的一部分自动绑定到标量容器(后者又包含 Seq 值)
另一种选择是让 $seq
绑定到标量容器,但在 show
例程的主体中使用前缀 |
:
sub show ( $seq ) { .say for |$seq }
而不是 raiph 建议的 is raw
参数特征,你也可以使用一个 sigil-less 变量作为参数,它不引入 Scalar
容器:
sub show (\seq) { .say for seq }
(您也可以将此形式用于普通变量,如 my \a = 5; say a;
,但请注意它们仅是单赋值。)
它的一个变体是 +
形式,如果它是 Iterable
(如 List
或 Seq
),它会传递原始参数,但是当传递一个不可迭代的参数时,它会将它提升为一个元素的 List
,这样函数体就可以依赖于始终获得 Iterable
:
sub show (+seq) { .say for seq }
(这是大多数内置列表处理例程,如 grep
和 zip
使用的方法。)
当然,如果您更喜欢使用引入 Scalar
容器的 $
参数,您可以在遍历它之前再次 "decontainerize" 它,方法是调用 .list
方法:
sub show ($seq) { .say for $seq.list } # explicit
sub show ($seq) { .say for @$seq } # short-hand syntax
(更新:嗯,.list
实际上将 Seq
变成了 List
,也就是说,在 Seq
很大的情况下,它不会节省内存. 使用 |$seq
就像你已经在自己的答案中发现的那样,没有这个问题。)
来自 docs ...
sub foo($bar) { say $bar } # $bar is a parameter
foo(42); # 42 is an argument
还有here ...
Sigil Binds to Default behavior
$ Scalar Generate new Scalar, use instead of Scalar in argument, if any
@ Positional Bind directly to the argument
@ PositionalBindFailover If binding failed, call argument's .cache method, bind to result
% Associative Bind directly to the argument
& Callable Bind directly to the argument
\ (anything) Bind directly to the argument, keep existing Scalar, if any
对于 Sigil $,调用者标量容器与被调用者标量容器不同,并且调用执行隐式赋值。 [尽管如此,'\' 和 'is raw' 是禁用此功能并强制绑定的方法(如果您愿意的话)。]
据我了解,其目的是让容器默认位于块本地,以帮助内存分离和并行化。所有其他 Sigil 直接绑定 - 但它们可以通过其他方式并行化。
因此 Sigil $ 对可迭代性和分离性都有影响。
在 Perl 6 中,我可以迭代一个文字序列:
.say for 0 ... 3;
我可以绑定到一个标量并迭代它:
my $s := 0 ... 3;
.say for $s;
但我无法绑定到标量,将其作为参数传递,然后迭代:
my $t := 0 ... 3;
show( $t );
sub show ( $seq ) { .say for $seq }
子例程获取类型为 Seq 的单个元素,但不会对其进行迭代。
0
1
2
3
0
1
2
3
(0 1 2 3)
准备参数的过程中是否已经迭代了所有内容?
Scalar 容器中保存的值不会 自动迭代
虽然$s
和$seq
都是标量(又名"variables"),但$s
直接绑定到Seq值,而你的 $seq
绑定到一个中介 Scalar(注意大写 S
)"container",后者又包含 Seq。当与 for
.
更详细:
my $s := 0 ... 3;
.say for $s;
因为my
变量声明使用直接绑定运算符:=
进行初始化,$s
是直接绑定到单个Seq值0 ... 3
.
这意味着 for
语句看到单个 Seq 值,确定它执行 Iterable 角色,并将其展平(迭代)。
现在考虑一下:
my $s := 0 ... 3;
my $container = $s;
.say for $container;
因为第二个 my
声明使用赋值运算符 =
进行初始化,所以新变量 $container
首先绑定到一个新的标量容器,然后 "contains" 无论如何被分配。
为了与语言范围保持一致 Slurpy Conventions(特别是:"An Iterable inside a Scalar container does not count"),for
语句不会迭代标量容器中保存的值,因此 .say for $container
行只做一个 say
.
类似的情况适用于您原来的 show
例程,因为默认情况下变量参数声明(语义上)是容器。
一种选择是向 $seq
参数添加 is raw
特征:
sub show ( $seq is raw ) { .say for $seq }
这会阻止 $seq
作为调用 show
.
另一种选择是让 $seq
绑定到标量容器,但在 show
例程的主体中使用前缀 |
:
sub show ( $seq ) { .say for |$seq }
而不是 raiph 建议的 is raw
参数特征,你也可以使用一个 sigil-less 变量作为参数,它不引入 Scalar
容器:
sub show (\seq) { .say for seq }
(您也可以将此形式用于普通变量,如 my \a = 5; say a;
,但请注意它们仅是单赋值。)
它的一个变体是 +
形式,如果它是 Iterable
(如 List
或 Seq
),它会传递原始参数,但是当传递一个不可迭代的参数时,它会将它提升为一个元素的 List
,这样函数体就可以依赖于始终获得 Iterable
:
sub show (+seq) { .say for seq }
(这是大多数内置列表处理例程,如 grep
和 zip
使用的方法。)
当然,如果您更喜欢使用引入 Scalar
容器的 $
参数,您可以在遍历它之前再次 "decontainerize" 它,方法是调用 .list
方法:
sub show ($seq) { .say for $seq.list } # explicit
sub show ($seq) { .say for @$seq } # short-hand syntax
(更新:嗯,.list
实际上将 Seq
变成了 List
,也就是说,在 Seq
很大的情况下,它不会节省内存. 使用 |$seq
就像你已经在自己的答案中发现的那样,没有这个问题。)
来自 docs ...
sub foo($bar) { say $bar } # $bar is a parameter
foo(42); # 42 is an argument
还有here ...
Sigil Binds to Default behavior
$ Scalar Generate new Scalar, use instead of Scalar in argument, if any
@ Positional Bind directly to the argument
@ PositionalBindFailover If binding failed, call argument's .cache method, bind to result
% Associative Bind directly to the argument
& Callable Bind directly to the argument
\ (anything) Bind directly to the argument, keep existing Scalar, if any
对于 Sigil $,调用者标量容器与被调用者标量容器不同,并且调用执行隐式赋值。 [尽管如此,'\' 和 'is raw' 是禁用此功能并强制绑定的方法(如果您愿意的话)。]
据我了解,其目的是让容器默认位于块本地,以帮助内存分离和并行化。所有其他 Sigil 直接绑定 - 但它们可以通过其他方式并行化。
因此 Sigil $ 对可迭代性和分离性都有影响。