首页 > 解决方案 > 如何将 .lines Seq 分配给变量并对其进行迭代?

问题描述

将迭代器分配给变量显然会改变其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用来获得相同的输出,但我不明白已分配和未分配的迭代器/序列之间的区别。

标签: raku

解决方案


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;

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

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

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

根本不是“投入”操作;它只是使符号i准确地引用lines返回的内容。这比将其放入Scalar容器中然后再次取出要清楚得多。这对读者来说也更容易一些,因为 amy \foo = ...永远不会被反弹,因此读者不需要在以后查找代码中的任何潜在更改。

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


推荐阅读