如何在 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(如 ListSeq),它会传递原始参数,但是当传递一个不可迭代的参数时,它会将它提升为一个元素的 List,这样函数体就可以依赖于始终获得 Iterable:

sub show (+seq) { .say for seq }

(这是大多数内置列表处理例程,如 grepzip 使用的方法。)

当然,如果您更喜欢使用引入 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 $ 对可迭代性和分离性都有影响。