如何将 .lines Seq 分配给变量并对其进行迭代?

How to assign the .lines Seq to a variable and iterate over it?

将迭代器分配给变量显然会改变 Seq 的行为方式。例如

use v6;

my $i = '/etc/lsb-release'.IO.lines;
say $i.WHAT;
say '/etc/lsb-release'.IO.lines.WHAT;
.say for $i;
.say for '/etc/lsb-release'.IO.lines;

结果:

(Seq)
(Seq)
(DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS")
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04
DISTRIB_CODENAME=bionic
DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS"

所以一旦分配,我只得到序列的字符串表示。我知道我可以使用 .say for $i.lines 获得相同的输出,但我不明白分配和未分配 iterator/Seq.

之间的区别

如果您希望能够遍历序列,您需要将其分配给位置:

my @i = '/etc/lsb-release'.IO.lines; .say for @i;

或者你可以告诉迭代器你想把给定的东西当作可迭代的:

.say for @$i

或者您可以将它放入迭代器的列表中:

.say for |$i

Perl 6 中的赋值总是关于将某些东西 放入 其他东西。

Assignment into a Scalar ($ sigil) 将被分配的事物存储到 Scalar 容器对象中,这意味着它将被视为单个项目;这就是 for $item { } 不会进行迭代的原因。有多种方法可以克服这个问题;概念上最简单的方法是使用 <> 后缀运算符,它会剥离任何 Scalar 容器:

my $i = '/etc/lsb-release'.IO.lines;
.say for $i<>;

还有滑动运算符 ("flatten into"),它将实现相同的效果:

my $i = '/etc/lsb-release'.IO.lines;
.say for |$i;

Array 的赋值将 - 除非右侧标记为惰性 - 迭代它并将每个元素存储到 Array 中。因此:

my @i = '/etc/lsb-release'.IO.lines;
.say for @i;

会工作,但它会在循环开始之前急切地将所有行读入 @i。这对于小文件来说没问题,但对于大文件就不太理想了,因为我们可能更喜欢懒惰地工作(也就是说,一次只将文件的一部分拉入内存)。可以尝试:

my @i = lazy '/etc/lsb-release'.IO.lines;
.say for @i;

但这无助于解决留存问题;它只是意味着数组将在迭代发生时从文件中延迟填充。当然,有时我们可能 想要 多次遍历这些行,在这种情况下分配到 Array 将是最好的选择。

相比之下,声明一个符号并将其绑定到那个:

my \i = '/etc/lsb-release'.IO.lines;
.say for i;

根本不是"put into"操作;它只是让符号 i 准确指代 lines returns。这比将它放入 Scalar 容器中只是为了再次取出它要清楚得多。在 reader 上也更容易一些,因为 my \foo = ... 永远不会反弹,因此 reader 不需要在以后查找任何潜在的变化代码。

最后一点,值得一提的是 my \foo = ... 形式实际上是一个绑定,而不是一个赋值。 Perl 6 允许我们使用 = 运算符来编写它,而不是强制使用 :=,即使在这种情况下语义是 := 语义。这只是带有初始值设定项的声明与正常赋值略有不同的许多情况之一,例如has $!foo = rand 实际上在每个对象实例化上运行赋值,而 state $foo = rand 只有当我们在当前闭包克隆的第一个入口时才运行它。