如何将.lines Seq赋值给变量并迭代它?

mat*_*ull 10 perl6 raku

为变量分配迭代器显然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;
Run Code Online (Sandbox Code Playgroud)

结果是:

(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"
Run Code Online (Sandbox Code Playgroud)

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

Jon*_*ton 13

Perl 6中的赋值始终是将某些内容放入其他内容中.

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

my $i = '/etc/lsb-release'.IO.lines;
.say for $i<>;
Run Code Online (Sandbox Code Playgroud)

滑动操作员("压平")也将实现相同的目的:

my $i = '/etc/lsb-release'.IO.lines;
.say for |$i;
Run Code Online (Sandbox Code Playgroud)

分配到Array遗嘱 - 除非右侧标记为懒惰 - 迭代它并将每个元素存储到遗嘱中Array.从而:

my @i = '/etc/lsb-release'.IO.lines;
.say for @i;
Run Code Online (Sandbox Code Playgroud)

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

my @i = lazy '/etc/lsb-release'.IO.lines;
.say for @i;
Run Code Online (Sandbox Code Playgroud)

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

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

my \i = '/etc/lsb-release'.IO.lines;
.say for i;
Run Code Online (Sandbox Code Playgroud)

根本不是"投入"运作; 它只是使符号i指的是什么lines返回.这比将它放入Scalar容器只是为了再次取出它更清楚.它对读者来说也更容易一些,因为my \foo = ...它永远不会被反弹,因此读者不需要在代码中查找任何可能的更改.

作为最后一点,值得知道my \foo = ...表单实际上是一个绑定,而不是一个赋值.Perl 6允许我们用=运算符而不是强制来编写它:=,即使在这种情况下语义是:=语义.这只是许多情况中的一种情况,其中具有初始化程序的声明与正常赋值略有不同,例如,has $!foo = rand实际上在每个对象实例化上运行赋值,而state $foo = rand仅在我们处于当前闭包的第一个条目时才运行它克隆.


Sci*_*mon 6

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

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

或者您可以告诉迭代器您希望将给定的东西视为可迭代的:

.say for @$i

或者您可以将其滑入迭代器的列表中:

.say for |$i