class Seq is Cool does Iterable does Sequence { }

Seq 表示可以生成值序列的任何内容。Seq 处于迭代它将消耗值的的状态中。对 Seq 调用 .cache 将使其存储生成的用于以后访问的值。

生成 Seq 的高级结构是 gather/take,以及许多内置方法,如 mapgrep,低级构造函数可从迭代器或循环构造中创建 Seq

还可以使用 序列运算符 ... 或其变体之一构造 Seq

my $s = (1...5);
say $s;              # OUTPUT: «(1 2 3 4 5)␤» 
say $s.^name;        # OUTPUT: «Seq␤»

Seq 的值分配给数组会消耗一个非惰性的 Seq。使用 lazy 语句前缀以避免在分配期间迭代 Seq

# The Seq created by gather ... take is consumed on the spot here. 
my @a = gather do { say 'consuming...'take 'one' };  # OUTPUT: «consuming...␤» 
 
# The Seq here is only consumed as we iterate over @a later. 
my @a = lazy gather do { say 'consuming...'take 'one' };  # outputs nothing. 
.say for @a;  # OUTPUT: «consuming...␤one␤» 

典型的用例是 IO::Handle 中的方法 lines,如果它存储从文件中读取的所有行,则可能使用大量内存。所以

for open('README.md').lines -> $line {
    say $line;
}

不会在内存中保留文件中的所有行。

这意味着您不能两次迭代同一个 Seq 对象(否则它无法丢弃旧值),因此这将导致死亡

my @a = 123;
my @b = <a b c>;
my \c = @a Z=> @b;
.say for c;
.say for c# fails 
CATCH { default { put .^name''.Str } };
# OUTPUT: «X::Seq::Consumed: This Seq has already been iterated, and its values consumed 
# (you might solve this by adding .cache on usages of the Seq, or 
# by assigning the Seq into an array)»

注意:即使程序没有缓存,任何程序也不应假设 Seq 只能迭代一次。缓存是一种不稳定的状态,作为优化向开发人员公开。Seq 可能被许多操作缓存,包括对 Seq 调用 .raku(2019.11 版之前的 .perl)(如果在非缓存迭代之前调用)。从 6.d 版开始,可以在已消耗的 Seq 上调用 .raku(同样,2019.11 版之前的 .perl)。如果程序假设 Seq 只能迭代一次,但后来在循环期间更改为调用其中一个操作,则该假设将失败。

在缓存的 Seq 上,当调用 &infix:<eqv>.Slip.join.List.list.eager.Array.is-lazy 时,将使用缓存的列表。

即使是无限的,您也可以使用 Seq 智能匹配正则表达式

my @fib = 1,1*+* ... *;
say @fib[^1000~~ /^9999/# OUTPUT: «Nil␤»

但是,在进行匹配时,无限或惰性的 Seq 将被激活,从而可能导致无限循环,因此请务必以某种方式限制搜索。

方法§

方法 new§

proto method new(Seq: |{*}
multi method new(Seq: Iterator:D $iter)
multi method new(Seq:)

从作为单个参数传递的提供的迭代器创建一个新的 Seq 对象。如果在没有参数的情况下调用,则创建一个空的 Seq

方法 iterator§

method iterator(Seq:D:)

如果 Seq 未被缓存,则返回底层迭代器并将调用者标记为已消耗。如果对已消耗的序列调用,则抛出类型为 X::Seq::Consumed 的错误。

否则,返回缓存列表上的迭代器。

方法 is-lazy§

method is-lazy(Seq:D:)

仅当底层迭代器或缓存列表认为自己是惰性的时,返回 True。如果在已消耗的序列上调用,则抛出类型为 X::Seq::Consumed 的错误。

方法 Seq§

multi method Seq(Seq:D:)

克隆对象。

方法 Capture§

method Capture()

将对象强制转换为 List,然后将其强制转换为 Capture

方法 elems§

method elems(Seq:D:)

返回序列中的值数。如果无法预测此数字,则缓存 Seq 并对其进行评估,直到结束。

由于无限序列无法评估到结束,因此应该将此类序列声明为惰性的。对惰性 Seq 调用 .elems 会使用 X::Cannot::Lazy fail

方法 from-loop§

multi method from-loop(&body:$label)
multi method from-loop(&body&cond:$repeat!:$label)
multi method from-loop(&body&cond:$label)
multi method from-loop(&body&cond&afterwards:$label)

这些方法创建新的基于 Seq 的回调。

通常,它通过每次请求新元素时调用 &body 来生成无限 Seq,并使用 &body 的返回值作为项目。这模拟(或实现)loop { body } 构造。

当多重包含 &cond 时,它会在每次调用 &body 之前调用,如果 &cond 返回假值,则终止序列。如果 $repeat 设置为真值,则省略对 &cond 的第一次调用,并立即调用 &body。这模拟(或实现)while cond { body }repeat { body } while cond 循环。

如果存在,则在每次调用 &body 后都会调用 &afterward

方法 sink§

method sink(--> Nil)

如果它是 Iterator,则调用 sink-all;如果 Sequence 是列表,则调用 sink

say (1 ... 1000).sink# OUTPUT: «Nil␤»

您可能希望这样做,以产生这些值的副作用。

方法 skip§

multi method skip(Seq:D:)
multi method skip(Seq:D: Whatever)
multi method skip(Seq:D: Callable:D $w)
multi method skip(Seq:D: Int() $n)
multi method skip(Seq:D: $skip$produce)

返回一个 Seq,其中包含调用者在丢弃下一个可用值中的 $n 个值后剩下的任何内容。$n 的负值计为 0。还可以使用 WhateverCode 来指示从末尾跳过多少个值。在请求的值数被丢弃之前,它将在惰性 Seq 上阻塞。

say (1..5).Seq.skip;      # OUTPUT: «(2 3 4 5)␤» 
say (1..5).Seq.skip(3);   # OUTPUT: «(4 5)␤» 
say (1..5).Seq.skip(5);   # OUTPUT: «()␤» 
say (1..5).Seq.skip(-1);  # OUTPUT: «(1 2 3 4 5)␤»

使用 Whatever 调用它将返回一个空 Seq

say <1 2 3>.Seq.skip(*);  # OUTPUT: «()␤»

使用 Callable 的多重主要用于以这种方式使用

say (1..5).Seq.skip(*-3); # OUTPUT: «(3 4 5)␤»

它不会丢弃前 $n 个元素,而是丢弃所有内容除了 WhateverCode 指示的元素,在本例中丢弃所有内容,除了最后三个元素。

从语言版本 6.e 开始(早期实现存在于 Rakudo 编译器 2022.12+ 中),还可以指定多个参数值。然后将它们解释为要生成的值数,然后跳过,然后生成,依此类推。

say (1..12).Seq.skip(2,3,4); # OUTPUT: «(3 4 5 10 11 12)␤»

这首先跳过 2 个值,然后生成 3 个值(3、4、5),跳过 4 个值,然后生成其余值(10、11、12)。

如果指定的最终值处于“生成”位置,则将跳过 Seq 的其余部分。如果最终值处于“跳过”位置,则将生成 Seq 的其余部分。

say (1..10).Seq.skip(2,3,1,2); # OUTPUT: «(3 4 5 7 8)␤» 
say (1..10).Seq.skip(2,3,1);   # OUTPUT: «(3 4 5 7 8 9 10)␤»

如果在“生成”位置指定了 Whatever,则将生成 Seq 的其余部分。否则,将跳过 Seq 的其余部分。

say (1..10).Seq.skip(2,*);   # OUTPUT: «(3 4 5 6 7 8 9 10)␤» 
say (1..10).Seq.skip(2,3,*); # OUTPUT: «(3 4 5)␤»

如果您想从生成值开始,而不是跳过值,请将 0 指定为第一个值。

say (1..10).Seq.skip(0,3,4); # OUTPUT: «(1 2 3 8 9 10)␤»

如果您想要一个无限重复的跳过和生成模式,则可以将参数指定为一个无限的 Seq 本身。

say (^20).Seq.skip(|(2,3xx *); # OUTPUT: «(0 1 5 6 10 11 15 16)␤»

多方法 slice§

method slice(Seq:D: *@indices --> Seq:D)

自 Rakudo 编译器的 2021.02 版本起可用。

slice 方法获取一系列单调递增的索引,用于从调用方中生成一个新 Seq 中的值。索引可以是单个数字或范围,只要它们的值递增即可。

这提供了一种比缓存 Seq 或将其转换为 ListArray 更有效的方法来获取 Seq 中的特定值。

say (1..10).Seq.slice(03..68);  # OUTPUT: «(1 4 5 6 7 9)␤»

类型图§

Seq 的类型关系
raku-type-graph Seq Seq Cool Cool Seq->Cool Iterable Iterable Seq->Iterable Sequence Sequence Seq->Sequence Mu Mu Any Any Any->Mu Cool->Any PositionalBindFailover PositionalBindFailover Sequence->PositionalBindFailover

展开上面的图表