my class List does Iterable does Positional { }

List 按顺序存储项目,并且可能延迟存储。

默认情况下,列表和数组的索引从 0 开始。

如果列表元素是容器,则可以对其进行赋值。使用数组将列表的每个值存储在容器中。

List 实现 Positional,因此支持 下标

不可变性§

列表是不可变对象,即列表中的元素数量及其元素本身都不能更改。因此,无法使用会更改列表结构本身的操作,例如 shiftunshiftpushpopsplice绑定

(123).shift;      # Error Cannot call 'shift' on an immutable 'List' 
(123).unshift(0); # Error Cannot call 'unshift' on an immutable 'List' 
(123).push(4);    # Error Cannot call 'push' on an immutable 'List' 
(123).pop;        # Error Cannot call 'pop' on an immutable 'List' 
(123)[0]:delete;  # Error Cannot remove elements from a List 
(123)[0:= 0;    # Error Cannot use bind operator with this left-hand side 
(123)[0= 0;     # Error Cannot modify an immutable Int 

List 不会 容器化 其元素,但如果任何元素碰巧位于 Scalar 容器中,则可以通过赋值替换元素的内容。

my $a = 'z';
my $list = ($a$'b');
 
say $list[0].VAR.^name# OUTPUT: «Scalar␤», containerized 
say $list[1].VAR.^name# OUTPUT: «Scalar␤», containerized 
say $list[2].VAR.^name# OUTPUT: «Str␤», non-containerized 
 
$list[0= 'a'# OK! 
$list[1= 'c'# OK! 
$list[2= 'd'# Error: Cannot modify an immutable List 

项目、扁平化和符号§

在 Raku 中,将 List 赋值给标量变量不会丢失信息。区别在于,迭代通常将标量中的列表(或任何其他类列表的对象,如 SeqArray)视为单个元素。

my $s = (123);
for $s { }      # one iteration 
for $s.list { } # three iterations 
 
my $t = [123];
for $t { }      # one iteration 
for $t.list { } # three iterations 
 
my @a = 123;
for @a { }      # three iterations 
for @a.item { } # one iteration

此操作称为项目化置于项目上下文中.item 为对象执行此操作,以及 $( ... ),在数组变量上为 $@a

列表通常不会插值(扁平化)到其他列表中,除非它们位于列表上下文中且是操作(如 append)的单个参数。

my $a = (123);
my $nested = ($a$a);  # two elements 
 
my $flat = $nested.map({ .Slip });  # six elements, with explicit Slip 
 
my @b = <a b>;
@b.append: $a.list;     # The array variable @b has 5 elements, because 
                        # the list $a is the sole argument to append 
 
say @b.elems;           # OUTPUT: «5␤» 
 
my @c = <a b>;
@c.append: $a.list7;  # The array variable @c has 4 elements, because 
                        # the list $a wasn't the only argument and thus 
                        # wasn't flatten by the append operation 
 
say @c.elems;           # OUTPUT: «4␤» 
 
my @d = <a b>;
@d.append: $a;          # The array variable @d has 3 elements, because 
                        # $a is in an item context and as far as append is 
                        # concerned a single element 
 
say @d.elems;           # OUTPUT: «3␤»

相同的扁平化行为适用于所有执行 Iterable 角色的对象,特别是 Hash

my %h = => 1=> 2;
my @b = %h;   say @b.elems;     # OUTPUT: «2␤» 
my @c = %h, ; say @c.elems;     # OUTPUT: «1␤» 
my @d = $%h;  say @d.elems;     # OUTPUT: «1␤»

Slurpy 参数(*@a)扁平化非项目化子列表

sub fe(*@flat{ @flat.elems }
say fe(<a b>, <d e>);           # OUTPUT: «4␤» 
say fe(<a b>, <d e>.item);      # OUTPUT: «3␤»

空列表使用 () 创建。针对空列表进行智能匹配将检查元素是否存在。

my @a;
for @a@a.list@a.Seq -> \listoid {
    say listoid ~~ ()
}
# OUTPUT: «True␤True␤True␤»

从空列表中检索值将始终返回 Nil

say ()[33.rand]; # OUTPUT: «Nil␤»

强制转换为 Bool 也表示 List 是否获取任何元素。

my @a;
say [@a.elems@a.Bool?@a]; # OUTPUT: «[0 False False]␤» 
@a.push: 42;
say [@a.elems@a.Bool?@a]; # OUTPUT: «[1 True True]␤» 
say 'empty' unless @a;        # no output

方法§

方法 ACCEPTS§

multi method ACCEPTS(List:D: $topic)

如果 $topic 是一个 Iterable,则根据两个 Iterable 的内容是否匹配返回 TrueFalse。调用者中的 Whatever 元素匹配 $topic Iterable 中对应位置的任何元素。一个 HyperWhatever 匹配任意数量的任何元素,包括没有元素

say (123)       ~~ (1,  *3);  # OUTPUT: «True␤» 
say (123)       ~~ (9,  *5);  # OUTPUT: «False␤» 
say (123)       ~~ (   **3);  # OUTPUT: «True␤» 
say (123)       ~~ (   **5);  # OUTPUT: «False␤» 
say (13)          ~~ (1**3); # OUTPUT: «True␤» 
say (12453~~ (1**3); # OUTPUT: «True␤» 
say (12456~~ (1**5); # OUTPUT: «False␤» 
say (12456~~ (   **   ); # OUTPUT: «True␤» 
say ()              ~~ (   **   ); # OUTPUT: «True␤»

此外,如果调用者或 $topic 是一个惰性 Iterable,则返回 False,除非 $topic 与调用者是同一个对象,在这种情况下返回 True

如果 $topic 不是一个 Iterable,则如果调用者没有元素或其第一个元素是一个 Match 对象(此行为支持 m:g// 智能匹配),则返回调用者,否则返回 False

例程 list§

multi        list(+list)
multi method list(List:D:)

该方法仅返回调用者 self。该子例程遵守 单参数规则:如果使用单个参数调用,该参数是一个非 项化 Iterable,则它返回一个基于参数 迭代器List;否则它仅返回参数列表。

例如

my $tuple = (12);         # an itemized List 
put $tuple.list.raku;       # OUTPUT: «(1, 2)␤» 
put list($tuple).raku;      # OUTPUT: «($(1, 2),)␤» 
put list(|$tuple).raku;     # OUTPUT: «(1, 2)␤»

最后一条语句使用 prefix:<|> 运算符将元组展平为参数列表,因此它等效于

put list(12).raku;        # OUTPUT: «(1, 2)␤»

还有其他方法可以列出项化单参数的元素。例如,你可以 解容器化 参数或使用 @ 列表上下文化器

put list($tuple<>).raku;    # OUTPUT: «(1, 2)␤» 
put list(@$tuple).raku;     # OUTPUT: «(1, 2)␤» 

请注意,将类型对象转换为列表可能不会产生你期望的结果

put List.list.raku;         # OUTPUT: «(List,)␤»

这是因为接受类型对象作为调用者的 .list 候选者由 Any 提供。该候选者返回一个包含一个元素的列表:类型对象 self。如果你正在开发一个集合类型,其类型对象应为空集合的有效表示,你可能希望为未定义的调用者提供你自己的候选者或使用“only”方法覆盖 Any: 候选者。例如

my class LinkedList {
    has $.value;            # the value stored in this node 
    has LinkedList $.next;  # undefined if there is no next node 
 
    method values--> Seq:D{
        my $node := self;
        gather while $node {
            take $node.value;
            $node := $node.next;
        }
    }
 
    method list--> List:D{
        self.values.list;
    }
}
 
my LinkedList $nodes;       # an empty linked list 
put $nodes.list.raku;       # OUTPUT: «()␤» 

例程 elems§

sub    elems($list --> Int:D)
method elems(List:D: --> Int:D)

返回列表中的元素数量。

say (1,2,3,4).elems# OUTPUT: «4␤»

例程 end§

sub    end($list --> Int:D)
method end(List:D: --> Int:D)

返回最后一个元素的索引。

say (1,2,3,4).end# OUTPUT: «3␤»

例程 keys§

sub    keys($list --> Seq:D)
method keys(List:D: --> Seq:D)

返回列表中索引的序列(例如,0..(@list.elems-1))。

say (1,2,3,4).keys# OUTPUT: «0..3␤»

例程 values§

sub    values($list --> Seq:D)
method values(List:D: --> Seq:D)

按顺序返回列表元素的序列。

say (1,2,3,4).^name;        # OUTPUT: «List␤» 
say (1,2,3,4).values.^name# OUTPUT: «Seq␤»

例程 kv§

sub    kv($list --> Seq:D)
method kv(List:D: --> Seq:D)

返回索引和值的交错序列。例如

say <a b c>.kv# OUTPUT: «(0 a 1 b 2 c)␤»

例程 pairs§

sub    pairs($list --> Seq:D)
method pairs(List:D: --> Seq:D)

返回一个对序列,其中索引作为键,列表值作为值。

say <a b c>.pairs;   # OUTPUT: «(0 => a 1 => b 2 => c)␤»

例程 antipairs§

method antipairs(List:D: --> Seq:D)

返回一个Seq对,其中值作为键,索引作为值,即与pairs相反。

say <a b c>.antipairs;  # OUTPUT: «(a => 0 b => 1 c => 2)␤»

例程 invert§

method invert(List:D: --> Seq:D)

假设 List 的每个元素都是Pair。将所有元素作为Seq返回Pair,其中键和值已交换。如果Pair的值是Iterable,则它会将该Iterable的值扩展为单独的对。

my $l = List.new('a' => (23), 'b' => 17);
say $l.invert;   # OUTPUT: «(2 => a 3 => a 17 => b)␤»

例程 join§

sub    join($separator*@list)
method join(List:D: $separator = "")

通过对每个元素调用.Str,将列表的元素视为字符串,用$separator交错它们,并将所有内容连接成一个字符串。

示例

say join '', <a b c>;             # OUTPUT: «a, b, c␤»

方法形式还允许你省略分隔符

say <a b c>.join;               # OUTPUT: «abc␤»

请注意,方法形式不会展开子列表

say (1, <a b c>).join('|');     # OUTPUT: «1|a b c␤»

子例程形式表现得懒惰,将第一个之后的全部参数展开为一个列表

say join '|'1, <a b c>;       # OUTPUT: «1|a|b|c␤»

在这种情况下,列表<a b c>懒惰展开,这与将join作为方法调用时的情况不同。

如果列表中的一个元素碰巧是Junction,那么join也将返回一个Junction,其中尽可能多地进行连接

say ("a"|"b","c","d").join;     # OUTPUT: «any(acd,bcd)␤»

例程 map§

multi method map(\SELF: &block)
multi        map(&code+values)

应用于列表的示例在此包含,目的是为了说明。

对于列表,它为每个元素调用&code,并按顺序收集返回值并返回。这是懒惰的,即仅在访问返回值时才调用&code。示例

say ('hello'122/742'world').map: { .^name } # OUTPUT: «(Str Int Rat Int Str)␤» 
say map *.Str.chars'hello'122/742'world'# OUTPUT: «(5 1 8 2 5)␤» 

map检查代码对象的元数,并尝试向其中传递尽可能多的参数

sub b($a$b{ "$a before $b" };
say <a b x y>.map(&b).join('');   # OUTPUT: «a before b, x before y␤»

一次迭代列表中的两个项目。

请注意,map不会展开嵌入的列表和数组,因此

((12), <a b>).map({ .join(',')})

依次将(1, 2)<a b>传递给块,导致总共两次迭代和结果序列"1,2", "a,b"

如果 &codeBlock 循环相位器将被执行,循环控制语句将被视为循环控制流。请注意,return 在其定义的上下文中执行。它不是块的返回语句,而是周围的例程。使用 Routine 也将处理循环控制语句和循环相位器。任何 Routine 特定的控制语句或相位器都将在该 Routine 的上下文中处理。

sub s {
    my &loop-block = {
        return # return from sub s 
    };
    say 'hi';
    (1..3).map: &loop-block;
    say 'oi‽' # dead code 
};
s 
# OUTPUT: «hi␤»

方法 flatmap§

method flatmap(List:D: &code --> Seq:D)

便捷方法,类似于 .map(&block).flat

方法 gist§

multi method gist(List:D: --> Str:D)

返回包含 List 的括号内“要点”的字符串,最多列出前 100 个元素,用空格分隔,如果 List 有超过 100 个元素,则追加省略号。如果 List is-lazy,则返回字符串 '(...)'

put (123).gist;   # OUTPUT: «(1 2 3)␤» 
put (1..∞).List.gist# OUTPUT: «(...)␤» 
 
put (1..200).List.gist;
# OUTPUT: 
# (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 
# 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 
# 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 
# 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 
# 96 97 98 99 100 ...) 

例程 grep§

sub    grep(Mu $matcher*@elems:$k:$kv:$p:$v --> Seq:D)
method grep(List:D:  Mu $matcher:$k:$kv:$p:$v --> Seq:D)

返回 $matcher 智能匹配的一系列元素。元素按它们在原始列表中出现的顺序返回。

示例

say ('hello'122/742'world').grep: Int;              # OUTPUT: «(1 42)␤» 
say grep { .Str.chars > 3 }'hello'122/742'world'# OUTPUT: «(hello 3.142857 world)␤»

请注意,如果你想 grep 不匹配的元素,可以使用 none-Junction

say <a b 6 d 8 0>.grep(none Int);           # OUTPUT: «(a b d)␤» 
say <a b c d e f>.grep(none /<[aeiou]>/);   # OUTPUT: «(b c d f)␤»

grep 不匹配元素的另一个选项是使用块

say <a b c d e f>.grep({! /<[aeiou]>/})     # OUTPUT: «(b c d f)␤»

上面示例有效的原因是,布尔上下文的正则表达式适用于 $_。在这种情况下,!/<[aeiou]>/ 正则表达式布尔化并否定结果。针对 Callable(在这种情况下是 Block)进行智能匹配会返回该可调用对象返回的值,因此正则表达式的布尔化结果用于决定当前值是否应保留在 grep 的结果中。

可选命名参数 :k:kv:p:v 提供与切片相同的功能

  • k

仅按顺序返回匹配元素的索引值。

  • kv

按顺序同时返回索引和匹配的元素。

  • p

按顺序将索引和匹配的元素作为 Pair 返回。

  • v

仅返回匹配的元素(与根本不指定任何命名参数相同)。

示例

say ('hello'122/742'world').grep: Int:k;
# OUTPUT: «(1 3)␤» 
say grep { .Str.chars > 3 }:kv'hello'122/742'world';
# OUTPUT: «(0 hello 2 3.142857 4 world)␤» 
say grep { .Str.chars > 3 }:p'hello'122/742'world';
# OUTPUT: «(0 => hello 2 => 3.142857 4 => world)␤»

例程 first§

sub    first(Mu $matcher*@elems:$k:$kv:$p:$end)
method first(List:D:  Mu $matcher?:$k:$kv:$p:$end)

返回列表中第一个与 $matcher 智能匹配的项,当没有值匹配时返回 Nil。可选命名参数 :end 表示搜索应从列表的末尾开始,而不是从开头开始。

示例

say (122/742300).first: * > 5;                  # OUTPUT: «42␤» 
say (122/742300).first: * > 5:end;            # OUTPUT: «300␤» 
say ('hello'122/742'world').first: Complex;   # OUTPUT: «Nil␤»

可选命名参数 :k:kv:p 提供与切片相同的功能

  • k

返回匹配元素的索引值。无论是否指定了 :end 命名参数,索引始终从列表的开头计数。

  • kv

同时返回索引和匹配的元素。

  • p

返回索引和匹配元素作为 Pair

示例

say (122/742300).first: * > 5:k;        # OUTPUT: «2␤» 
say (122/742300).first: * > 5:p;        # OUTPUT: «2 => 42␤» 
say (122/742300).first: * > 5:kv:end# OUTPUT: «(3 300)␤»

在方法形式中,可以省略 $matcher,在这种情况下,将返回第一个可用项(或如果设置 :end,则返回最后一个)。另请参阅 headtail 方法。

方法 head§

multi method head(Any:D:is raw
multi method head(Any:D: Callable:D $w)
multi method head(Any:D: $n)

此方法直接继承自 Any,它返回列表的第一个$n 项,如果 $n <= 0,则返回一个空列表,或者在没有参数的情况下返回第一个元素。采用 Callable 的版本使用 WhateverCode 来指定所有元素,从第一个开始,但最后一个除外。

示例

say <a b c d e>.head ;     # OUTPUT: «a␤» 
say <a b c d e>.head(2);   # OUTPUT: «(a b)␤» 
say <a b c d e>.head(*-3); # OUTPUT: «(a b)␤»

方法 tail§

multi method tail(List:D:)
multi method tail(List:D: $n --> Seq:D)

返回一个包含列表的最后一个$n 项的 Seq。如果 $n <= 0,则返回一个空的 Seq。如果没有指定参数,则默认为最后一个元素。如果列表是惰性的,则会引发异常。

示例

say <a b c d e>.tail(*-3);# OUTPUT: «(d e)␤» 
say <a b c d e>.tail(2);  # OUTPUT: «(d e)␤» 
say <a b c d e>.tail;     # OUTPUT: «e␤» 

在第一种情况下,$n 采用 WhateverCode 的形式,以指示将排除的从头开始的元素数。$n 可以是 Callable,在这种情况下,它将使用值 0 调用,或者可以转换为数字的任何其他内容,在这种情况下,它将使用该值作为输出 Seq 中的元素数。

say <a b c d e>.tail{ $_ - 2 } ); # OUTPUT: «(c d e)␤»

例程 categorize§

multi method categorize()
multi method categorize(Whatever)
multi method categorize($test:$into!:&as)
multi method categorize($test:&as)
multi        categorize($test+items:$into!*%named )
multi        categorize($test+items*%named )

这些方法直接继承自 Any;有关更多示例,请参阅 Any.list

此例程将值列表转换为哈希,表示根据 $test 对这些值进行分类,该 $test 对列表中的每个元素调用一次;每个哈希键表示一个或多个传入列表值的一种可能的分类,并且相应的哈希值包含由 $test 分类为关联键的类别的那些列表值数组,就像映射器一样。

请注意,与 classify 不同,后者假设映射器的返回值是一个值,categorize 总是假设映射器的返回值是适合当前值的类别列表。

示例

sub mapper(Int $ireturns List {
    $i %% 2 ?? 'even' !! 'odd',
    $i.is-prime ?? 'prime' !! 'not prime'
}
say categorize &mapper, (17632);
# OUTPUT: «{even => [6 2], not prime => [1 6], odd => [1 7 3], prime => [7 3 2]}␤»

例程 classify§

multi method classify($test:$into!:&as)
multi method classify($test:&as)
multi        classify($test+items:$into!*%named )
multi        classify($test+items*%named )

将值列表转换为表示这些值分类的哈希;每个哈希键表示一个或多个传入列表值的分类,并且相应的哈希值包含已分类为关联键类别的那些列表值数组。$test 将是一个表达式,它将根据元素将要分类的哈希键生成。

示例

say classify { $_ %% 2 ?? 'even' !! 'odd' }, (17632);
# OUTPUT: «{even => [6 2], odd => [1 7 3]}␤» 
say ('hello'122/742'world').classify: { .Str.chars };
# OUTPUT: «{1 => [1], 2 => [42], 5 => [hello world], 8 => [3.142857]}␤»

它还可以将 :as 作为命名参数,在对其进行分类之前转换该值

say <Innie Minnie Moe>.classify{ $_.chars }:as{ lc $_ });
# OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}␤»

此代码按字符数进行分类,这是已作为 $test 参数传递的表达式,但 :as 块在执行转换之前将其小写。命名参数 :into 也可用于将分类到新定义的变量中

<Innie Minnie Moe>.classify{ $_.chars }:as{ lc $_ }:intomy %words{Int} ) );
say %words# OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}␤»

我们正在动态声明 %words{Int} 的作用域,其键实际上是整数;它通过分类结果创建。

方法 Bool§

method Bool(List:D: --> Bool:D)

如果列表至少有一个元素,则返回 True,如果列表为空,则返回 False

say ().Bool;  # OUTPUT: «False␤» 
say (1).Bool# OUTPUT: «True␤»

方法 Str§

method Str(List:D: --> Str:D)

将列表的元素字符串化并用空格连接它们(与 .join(' ') 相同)。

say (1,2,3,4,5).Str# OUTPUT: «1 2 3 4 5␤»

方法 Int§

method Int(List:D: --> Int:D)

返回列表中的元素数量(与 .elems 相同)。

say (1,2,3,4,5).Int# OUTPUT: «5␤»

方法 Numeric§

method Numeric(List:D: --> Int:D)

返回列表中的元素数量(与 .elems 相同)。

say (1,2,3,4,5).Numeric# OUTPUT: «5␤»

方法 Capture§

method Capture(List:D: --> Capture:D)

返回一个 Capture,其中 <List 中的每个 Pair(如果有)已转换为命名参数( of Pair 已字符串化)。List 中的所有其他元素都按其出现的顺序转换为位置参数,即列表中的第一个非对项目成为第一个位置参数,其索引为 0,第二个非对项目成为第二个位置参数,其索引为 1 等。

my $list = (75=> 2=> 17);
my $capture = $list.Capture;
say $capture.keys;                                # OUTPUT: «(0 1 a b)␤» 
my-sub(|$capture);                                # OUTPUT: «7, 5, 2, 17␤» 
 
sub my-sub($first$second:$a:$b{
    say "$first$second$a$b"
}

一个更高级的示例演示了将返回的 CaptureSignature 匹配。

my $list = (75=> 2=> 17);
say so $list.Capture ~~ :($ where * == 7,$,:$a,:$b); # OUTPUT: «True␤» 
 
$list = (85=> 2=> 17);
say so $list.Capture ~~ :($ where * == 7,$,:$a,:$b); # OUTPUT: «False␤»

例程 pick§

multi        pick($count*@list --> Seq:D)
multi method pick(List:D: $count --> Seq:D)
multi method pick(List:D: --> Mu)
multi method pick(List:D: Callable $calculate --> Seq:D)

如果提供了 $count:从调用者中随机选择 $count 个元素,且不重复。如果将 * 传递为 $count,或者 $count 大于或等于列表的大小,则调用者列表中的所有元素都将按随机顺序返回;即它们被返回为混洗状态。

如果在 方法 形式中省略了 $count:从列表中返回一个随机项,如果列表为空,则返回 Nil

示例

say <a b c d e>.pick;           # OUTPUT: «b␤» 
say <a b c d e>.pick: 3;        # OUTPUT: «(c a e)␤» 
say <a b c d e>.pick: *;        # OUTPUT: «(e d a b c)␤»

从 Rakudo 编译器的 2021.06 版本开始,还可以将 **(又名 HyperWhatever)指定为计数。

在这种情况下,.pick 将在原始列表用尽后再次开始挑选,如此反复,无限期地进行。

say <a b c>.pick(**).head(10);  # OUTPUT: «((a c b c a b b c a b))␤»

例程 roll§

multi        roll($count*@list --> Seq:D)
multi method roll(List:D: $count --> Seq:D)
multi method roll(List:D: --> Mu)

如果提供了 $count:返回一个 $count 个元素的序列,每个元素都是从列表中随机选择的。每个随机选择都是独立进行的,就像一个单独的骰子掷法,其中每个骰子面都是一个列表元素。如果将 * 传递为 $count,则返回一个惰性无限序列,其中包含从原始列表中随机选择的元素。

如果省略了 $count:从列表中返回一个随机项,如果列表为空,则返回 Nil

示例

say <a b c d e>.roll;       # 1 random letter 
say <a b c d e>.roll: 3;    # 3 random letters 
say roll 8, <a b c d e>;    # 8 random letters 
 
my $random-digits := (^10).roll(*);
say $random-digits[^15];    # 15 random digits

例程 eager§

multi method eager(List:D: --> List:D)

急切地计算 List 中的所有元素,并将其作为 List 返回。

my  \ll = (lazy 1..5).cache;
 
say ll[];     # OUTPUT: «(...)␤» 
say ll.eager  # OUTPUT: «(1 2 3 4 5)␤»

例程 reverse§

multi        reverse(*@list  --> Seq:D)
multi method reverse(List:D: --> Seq:D)

返回一个 Seq,其中元素按相反的顺序排列。

请注意,reverse 始终指反转列表的元素;要反转字符串中的字符,请使用 flip

示例

say <hello world!>.reverse;     # OUTPUT: «(world! hello)␤» 
say reverse ^10;                # OUTPUT: «(9 8 7 6 5 4 3 2 1 0)␤»

例程 rotate§

multi        rotate(@list,  Int:D $n = 1 --> Seq:D)
multi method rotate(List:D: Int:D $n = 1 --> Seq:D)

$n 为正数时,返回一个包含向左旋转的列表元素的 Seq;否则向右旋转。

示例

say <a b c d e>.rotate(2);   # OUTPUT: (c d e a b) 
say <a b c d e>.rotate(-1);  # OUTPUT: (e a b c d)

注意:在 Rakudo 2020.06 版本之前,返回的是一个新的 List,而不是 Seq

例程 sort§

multi        sort(*@elems      --> Seq:D)
multi        sort(&custom-routine-to-use*@elems --> Seq:D)
multi method sort(List:D:      --> Seq:D)
multi method sort(List:D: &custom-routine-to-use  --> Seq:D)

对列表进行排序,最小的元素排在最前面。默认情况下,使用 infix:<cmp> 对列表元素进行比较。

如果提供了 &custom-routine-to-use,并且它接受两个参数,则会对列表元素对调用它,并且应该返回 Order::LessOrder::SameOrder::More

如果 &custom-routine-to-use 只接受一个参数,则根据 custom-routine-to-use($a) cmp custom-routine-to-use($b) 对列表元素进行排序。&custom-routine-to-use 的返回值会被缓存,以便每个列表元素只调用 &custom-routine-to-use 一次。

示例

say (3-47-120).sort;                  # OUTPUT: «(-4 -1 0 2 3 7)␤» 
say (3-47-120).sort: *.abs;           # OUTPUT: «(0 -1 2 3 -4 7)␤» 
say (3-47-120).sort: { $^b leg $^a }# OUTPUT: «(7 3 2 0 -4 -1)␤»

此外,如果 &custom-routine-to-use 返回一个 List,则元素将基于多个值进行排序,如果先前元素之间的比较结果为 Order::Same,则 List 中的后续值将用于打破平局。

my @resistance = (
    %first-name => 'Kyle',  last-name => 'Reese'  ),
    %first-name => 'Sarah'last-name => 'Connor' ),
    %first-name => 'John',  last-name => 'Connor' ),
);
.say for @resistance.sort: { .<last-name>.<first-name> };
 
#`(
OUTPUT:
  {first-name => John, last-name => Connor}
  {first-name => Sarah, last-name => Connor}
  {first-name => Kyle, last-name => Reese}
)

此排序可以基于单个元素的特征

say <ddd aaa bbb bb ccc c>.sort{.chars.Str} );
# OUTPUT: «(c bb aaa bbb ccc ddd)␤» 

在这种情况下,数组的元素按升序排序,首先按字符串长度(.chars)排序,如果长度完全相同,则按实际字母顺序(.Str)排序。

可以在此使用任意数量的条件

say <01 11 111 2 20 02>.sort{ .Int.comb.sum.Str } );
# OUTPUT: «(01 02 2 11 20 111)␤» 

从 Rakudo 编译器的 2022.07 版本开始,不带任何参数调用 sort 子例程已成为运行时错误

sort;   # ERROR: «Must specify something to sort␤» 

从 Rakudo 编译器的 2023.08 版本开始,还可以指定一个 :k 命名参数。这将导致结果成为排序过程的索引列表。

say <a c b d e>.sort(:k); # OUTPUT: «(0 2 1 3 4)␤» 
say sort <a c b d e>:k# OUTPUT: «(0 2 1 3 4)␤»

例程 reduce§

multi method reduce(Any:D: &with)
multi        reduce (&with+list)

通过迭代应用知道如何组合两个值的例程,从任意多个值的列表中返回一个“组合”值。除了子例程和列表之外,还可以提供一个初始值来初始化归约,如果列表为空,则该值最终成为返回值。因此,reduce f, init, list 从左到右组合列表的元素,如下面的伪代码所示

result0 = init
result1 = f(result0, list[0])
result2 = f(result1, list[1])
...
resultn = f(resultn-1, list[n-1])

resultn 是 n 个元素列表的最终结果。

say reduce &infix:<+>, (123); # OUTPUT: «6␤» 
say (123).reduce: &infix:<+># OUTPUT: «6␤» 
say reduce &max, (59121);   # OUTPUT: «12␤»

如果 list 只包含一个元素,则如果可能,该运算符将应用于该元素;如果不能,则返回元素本身。

say reduce &infix:<->, (10,);     # OUTPUT: «10␤»

当列表不包含任何元素时,会引发异常,除非 &with 是具有已知标识值的运算符(例如,infix:<+> 的标识值为 0)。因此,建议你在输入列表前加上一个初始值(或显式标识值)

my \strings = "One good string!""And one another good string!";
say reduce { $^a ~ $^b }''|strings;               # like strings.join 
 
my \numbers = 12345;
say reduce { $^a > $^b ?? $^a !! $^b }0|numbers# like numbers.max 
 
sub count-and-sum-evens( (Int \countInt \sum), Int \x ) {
    x %% 2 ?? (count+1, sum+x!! (countsum)
}
 
say reduce &count-and-sum-evens, (00), |numbers;    # OUTPUT: «(2 6)␤»

在最后一个示例中,由于 reduce 仅支持一个初始值,因此我们使用一个包含两个值的 List,它本身是一个单一值。count-and-sum-evens 子例程采用两个位置值:一个包含两个 IntList 和一个 Int,并返回一个 List,其中存储累积的偶数的计数和总和。

如果 &with 是一个 操作符 的代码对象,则会保留其固有的标识值和结合性 - 换句话说,(VAL1, VAL2, VAL3).reduce(&infix:<OP>)VAL1 OP VAL2 OP VAL3 相同,即使对于不左结合的操作符也是如此

# Raise 2 to the 81st power, because 3 to the 4th power is 81 
(2,3,4).reduce(&infix:<**>).lsb.say;  # OUTPUT: «81␤» 
(2**(3**4)).lsb.say;                  # OUTPUT: «81␤» 
(2**3**4).lsb.say;                    # OUTPUT: «81␤» 
 
# Subtract 4 from -1, because 2 minus 3 is -1 
(2,3,4).reduce(&infix:<->).say;       # OUTPUT: «-5␤» 
((2-3)-4).say;                        # OUTPUT: «-5␤» 
(2-3-4).say;                          # OUTPUT: «-5␤»

由于使用中缀操作符进行归约是一件常见的事情,因此 归约元操作符 [ ] 提供了一个语法快捷方式。因此,不必将操作符的代码对象传递给 reduce,只需将操作符直接传递给 [ ]。要改用用户定义的子例程,请在子例程的代码对象周围提供一层额外的方括号

say [*] (1234);       # OUTPUT: «24␤» 
say [min] (4213);     # OUTPUT: «1␤» 
 
sub mult { $^a * $^b };
say [[&mult]] (1234); # OUTPUT: «24␤»

在语义上,所有以下操作都执行相同的事情

my \numbers = 12345;
say reduce { $^a + $^b }0|numbers;
say reduce * + *0|numbers;
say reduce &[+], numbers# operator does not need explicit identity value 
say [+numbers;

由于 reduce 是一个隐式循环,它会通过 &with 中的 归约 子例程进行迭代,因此它会响应 nextlastredo 语句

sub last-after-seven { last if $^a > 7$^a + $^b };
say (2345).reduce: &last-after-seven# OUTPUT: «9␤»

reduce 是从左向右还是从右向左累积元素取决于操作符。在函数式编程世界中,此操作通常称为 折叠。对于右结合操作符,它是一个 右折叠,否则(通常)它是一个 左折叠。在 Raku 中,您可以使用 is assoc 指定操作符的结合性。

sub infix:<foo>($a$bis assoc<right> { "($a$b)" }
say [foo1234# OUTPUT: «(1, (2, (3, 4)))␤» 
 
sub infix:<bar>($a$bis assoc<left> { "($a$b)" }
say [bar1234# OUTPUT: «(((1, 2), 3), 4)␤»

实际示例 1: 在此示例中,我们使用 reduce 生成一个随机数学公式(例如,“(4 + ((3 * x) + 11) / 6))”)。

my @ops = [Z] (<+ - * />1..20.roll(4);
 
say ('x'|@ops).reduce: -> $formula, [$op$number{
    Bool.pick ?? "($formula $op $number)"
              !! "($number $op $formula)"
}

实际示例 2: 假设我们有一个表示为整数系数列表的多项式,c[n-1]、c[n-2]、...、c[0],其中 c[i] 是 xi 的系数。我们可以使用 mapreduce 如下对其进行求值

sub evaluate(List:D \c where c.all ~~ IntRat:D \x --> Rat:D{
    my \xi = (c.elems ^... 0).map: -> \i { x ** i }# [x^(n-1), ..., x^0] 
    my \axi = [+c Z* xi;                           # [c[n-1]*x^(n-1), ..., c[*]x^0] 
    [+axi;                                         # sum of axi 
}
 
my \c = 231;       # 2x² + 3x + 1 
say evaluate c3.0;   # OUTPUT: «28␤» 
say evaluate c10.0;  # OUTPUT: «231␤»

例程 produce§

multi        produce(&with*@values)
multi method produce(List:D: &with)

通过迭代应用一个知道如何组合 两个 值的函数,生成所有中间“组合”值以及最终结果的列表。

如果 @values 只包含一个元素,则立即返回一个包含该元素的列表。如果它不包含任何元素,则会引发异常,除非 &with 是具有已知标识值的 操作符

如果 &with 是一个 操作符 的函数对象,则会保留其固有的标识值和结合性 - 换句话说,(VAL1, VAL2, VAL3).produce(&[OP])VAL1 OP VAL2 OP VAL3 相同,即使对于不左结合的操作符也是如此

# Raise 2 to the 81st power, because 3 to the 4th power is 81 
[2,3,4].produce(&[**]).say;        # OUTPUT: «(4 81 2417851639229258349412352)␤» 
say produce &[**], (2,3,4);        # OUTPUT: «(4 81 2417851639229258349412352)␤» 
say [\**] (2,3,4);                 # OUTPUT: «(4 81 2417851639229258349412352)␤» 
 
# Subtract 4 from -1, because 2 minus 3 is -1 
[2,3,4].produce(&[-]).say;         # OUTPUT: «(2 -1 -5)␤» 
say produce &[-], (2,3,4);         # OUTPUT: «(2 -1 -5)␤» 
say [\-] (2,3,4);                  # OUTPUT: «(2 -1 -5)␤»

三角元操作符 [\ ] 为使用中缀操作符进行生成提供了一个语法快捷方式

# The following all do the same thing... 
my @numbers = (1,2,3,4,5);
say produce { $^a + $^b }@numbers;
say produce * + *@numbers;
say produce &[+], @numbers# operator does not need explicit identity 
say [\+@numbers;          # most people write it this way

三角形 [\ 的视觉效果并非偶然。要生成三角形列表列表,可以使用“三角形逗号”

[\,] 1..5;
# ( 
# (1) 
# (1 2) 
# (1 2 3) 
# (1 2 3 4) 
# (1 2 3 4 5) 
# )

由于 produce 是一个隐式循环,因此它会响应 &with 中的 nextlastredo 语句

say (2,3,4,5).produce: { last if $^a > 7$^a + $^b }# OUTPUT: «(2 5 9)␤»

例程 combinations§

multi        combinations($from$of = 0..*             --> Seq:D)
multi method combinations(List:D: Int() $of             --> Seq:D)
multi method combinations(List:D: Iterable:D $of = 0..* --> Seq:D)

返回一个Seq,其中包含调用者列表的所有$of组合。$of可以是数字Range,在这种情况下,将返回它表示的项目编号范围的组合(即2.6 .. 4将返回 2、3 和 4 个项目的组合)。否则,$of将强制转换为Int

.say for <a b c>.combinations: 2;
# OUTPUT: 
# (a b) 
# (a c) 
# (b c)

上面有三种可能的方法来组合原始列表中的 2 个项目列表,这是我们在输出中接收到的。如果你想要排列而不是组合,请参见permutations

使用Range参数,我们同时得到三个 2 个项目的组合和一个 3 个项目的组合

.say for <a b c>.combinations: 2..3;
# OUTPUT: 
# (a b) 
# (a c) 
# (b c) 
# (a b c)

如果$of为负数或大于给定列表中的项目数,则将返回一个空列表。如果$of为零,则将返回一个包含空列表的 1 个项目列表(有且仅有一种方法可以不选择任何项目)。

子例程形式等同于在第一个参数($from)上调用的方法形式,但如果$from不是Iterable,则会将其强制转换为Int,并且组合是由使用0..^$from构造的Range而不是

.say for combinations 32
# OUTPUT: 
# (0 1) 
# (0 2) 
# (1 2)

注意:某些实现可能会限制非Iterable$from的最大值。在 Rakudo 上,64 位系统限制为2³¹-1,32 位系统限制为2²⁸-1

例程 permutations§

multi        permutations(Int()    $from --> Seq:D)
multi        permutations(Iterable $from --> Seq:D)
multi method permutations(List:D:        --> Seq:D)

将列表的所有可能排列作为Seq列表返回

.say for <a b c>.permutations;
# OUTPUT: 
# (a b c) 
# (a c b) 
# (b a c) 
# (b c a) 
# (c a b) 
# (c b a)

permutations将所有元素视为唯一,因此(1, 1, 2).permutations返回一个包含 6 个元素的列表,即使只有三个不同的排列,这是由于前两个元素相同。

子例程形式的行为与方法形式相同,从其第一个参数$from计算排列。如果$from不是Iterable,则将$from强制转换为Int,并从使用0..^$from构造的Range中进行选择

.say for permutations 3;
# OUTPUT: 
# (0 1 2) 
# (0 2 1) 
# (1 0 2) 
# (1 2 0) 
# (2 0 1) 
# (2 1 0)

例程 rotor§

method rotor(*@cycleBool() :$partial --> Seq:D)

返回一个列表序列,其中每个子列表由调用者的元素组成。

在最简单的情况下,@cycle只包含一个整数,在这种情况下,调用者列表将被分成子列表,其元素数量与整数指定的一样。如果:$partial为 True,则即使最终块不满足长度要求,也会将其包括在内

say ('a'..'h').rotor(3).join('|');              # OUTPUT: «a b c|d e f␤» 
say ('a'..'h').rotor(3:partial).join('|');    # OUTPUT: «a b c|d e f|g h␤»

如果@cycle的元素是一个Pair,则该对的键指定返回子列表的长度,而值指定子列表之间的间隙;负间隙产生重叠

say ('a'..'h').rotor(2 => 1).join('|');         # OUTPUT: «a b|d e|g h␤» 
say ('a'..'h').rotor(3 => -1).join('|');        # OUTPUT: «a b c|c d e|e f g␤»

如果@cycle包含多个元素,rotor将循环遍历它以查找每个子列表的元素数量

say ('a'..'h').rotor(23).join('|');           # OUTPUT: «a b|c d e|f g␤» 
say ('a'..'h').rotor(1 => 13).join('|');      # OUTPUT: «a|c d e|f␤»

组合多个循环和:partial也适用

say ('a'..'h').rotor(1 => 13 => -1:partial).join('|');
# OUTPUT: «a|c d e|e|g h␤»

请参阅这篇博客文章,以更详细地阐述 rotor

multi rotor(Int:D $batch, \sourceBool() :$partial --> Seq:D)
multi rotor(*@cycle, \sourceBool() :$partial --> Seq:D)

在 6.e 语言版本中可用(早期实现存在于 Rakudo 编译器 2022.02+ 中)。

say rotor(3'a'..'h').join('|');             # OUTPUT: «a b c|d e f␤» 
say rotor(3'a'..'h':partial).join('|');   # OUTPUT: «a b c|d e f|g h␤» 
say rotor(2 => 1'a'..'h').join('|');        # OUTPUT: «a b|d e|g h␤» 
say rotor(3 => -1'a'..'h').join('|');       # OUTPUT: «a b c|c d e|e f g␤» 
say rotor(1 => 13 => -1'a'..'h':partial).join('|');
# OUTPUT: «a|c d e|e|g h␤» 

方法 batch§

multi method batch(Int:D $batch --> Seq)
multi method batch(Int:D :$elems --> Seq)

返回一个列表序列,其中除最后一个列表外的每个列表都保证包含一个元素数等于 $batch$elems 分别指定のバッチサイズ。如果调用者具有一个元素数不是バッチサイズの整数倍数,则返回序列中的最后一个列表将包含任何剩余元素,因此少于 $batch$elems 个元素。因此,.batch($batch).rotor($batch, :partial) 的简写。

例程 cross§

sub cross(+@e:&with --> Seq:D)

计算两个或更多个列表或 Iterable 的笛卡尔积。这返回一个列表序列,其中每个列表中的第一个项目是来自第一个可迭代对象的一个项目,第二个项目来自给定的第二个可迭代对象,依此类推。每个项目将与所有其他列表中的每个其他项目配对。

say cross(<a b c>, <d e f>).map(*.join).join(",")
# OUTPUT: «ad,ae,af,bd,be,bf,cd,ce,cf␤»

cross 例程也有一个中缀同义词,名为 X

say (<a b c> X <d e f>).map(*.join).join(",")
# output is the same as the previous example

如果传递了可选的 with 参数,则将其用作对每个笛卡尔积项目应用的规约操作。

say cross([123], [456], :with(&infix:<*>)).join(",");
# OUTPUT: «4,5,6,8,10,12,12,15,18␤»

X 运算符也可以与另一个运算符组合为元运算符,以执行规约

say ([123X* [456]).join(",")
# same output as the previous example

例程 zip§

sub zip(+@e:&with --> Seq:D)

从多个输入列表或其他 Iterable 构建一个“列表列表”,作为序列返回。

zip 同步遍历每个输入列表,将它们“压缩”在一起,以便元素根据其输入列表索引分组,按照提供列表的顺序分组。

say zip(<a b c>, <d e f>, <g h i>);
# OUTPUT: «((a d g) (b e h) (c f i))␤»

zip 有一个中缀同义词,Z 运算符

say <a b c> Z <d e f> Z <g h i>;                   # same output

zip 可以为 for 循环提供输入

for <a b c> Z <d e f> Z <g h i> -> [$x,$y,$z{say ($x,$y,$z).join(",")}
# OUTPUT: «a,d,g␤b,e,h␤c,f,i␤»

,或更简洁地

say .join(","for zip <a b c>, <d e f>, <g h i>;  # same output

请注意,如果输入列表具有不等数量的元素,则 zip 在最短的输入列表用尽后终止,并且来自较长输入列表的尾部元素将被丢弃。

say <a b c> Z <d e f m n o p> Z <g h i>;
# ((a d g) (b e h) (c f i))

在可能裁剪数据但不需要裁剪数据的情况下,请考虑使用 roundrobin 而不是 zip

可选的 with 参数还将规约压缩的列表。例如,以下内容将相应的元素相乘以返回一个乘积列表。

.say for zip <1 2 3>, [123], (123), :with(&infix:<*>);
# OUTPUT: «1␤8␤27␤»

Z 形式也可以用于通过使用元运算符隐式设置 with 参数来执行规约

.say for <1 2 3> Z* [123Z* (123);        # same output

例程 roundrobin§

sub roundrobin(+list-of-lists --> Seq)

从多个输入列表或其他 Iterable 构建一个“列表列表”,作为序列返回。roundrobin 返回与 zip 相同的结果,但当允许输入列表具有不等数量的元素时除外。

say roundrobin <a b c>, <d e f>, <g h i>;
# OUTPUT: «((a d g) (b e h) (c f i))␤» 
 
say .join(","for roundrobin([12], [23], [34]);
# OUTPUT: «1,2,3␤2,3,4␤»

一旦一个或多个输入列表用尽,roundrobin 不会终止,而是继续,直到所有列表中的所有元素都得到处理。

say roundrobin <a b c>, <d e f m n o p>, <g h i j>;
# OUTPUT: «((a d g) (b e h) (c f i) (m j) (n) (o) (p))␤» 
 
say .join(","for roundrobin([12], [235777], [34102]);
# OUTPUT: «1,2,3␤2,3,4␤57,102␤77␤»

因此,在“压缩”操作中不会丢失任何数据值。但是,无法从结果序列中收集到哪个输入列表提供了哪个元素的记录。

roundrobin 可用于组合混乱的数据,以便随后可以执行手动后处理步骤。

sub roundrobin(+list-of-lists:$slip --> Seq)

从 Rakudo 编译器的 2022.02 版本开始,还可以指定一个名为 :slip 的命名参数。如果指定为真值,将滑移产生的值。

say roundrobin <a b c>, <d e f m n o p>, <g h i j>:slip;
# OUTPUT: «(a d g b e h c f i m j n o p)␤»

例程 sum§

sub    sum($list  )
method sum(List:D:)

返回列表中所有元素的总和,如果列表为空,则返回 0。如果无法将元素强制转换为数字,则抛出异常。

say (13pi).sum;       # OUTPUT: «7.14159265358979␤» 
say (1"0xff").sum;      # OUTPUT: «256␤» 
say sum(0b11115);       # OUTPUT: «20␤»

如果列表包含一个Junction,则结果将相应地是一个Junction

say ( 1|2, 3).sum;            # OUTPUT: «any(4, 5)␤» 

当在原生整数数组上调用它时,还可以指定一个名为 :wrap 的命名参数。这将把值作为原生整数相加,如果它们超过原生整数的大小,则进行环绕。如果您确定不会超过该值,或者您不介意,使用 :wrap 将使计算速度提高约 20 倍。

my int @a = ^1_000_000;
say @a.sum(:wrap);        # OUTPUT: «499999500000␤» 

方法 fmt§

method fmt($format = '%s'$separator = ' ' --> Str:D)

返回一个字符串,其中列表中的每个元素都已根据 $format 进行格式化,并且每个元素都用 $separator 分隔。如果列表包含嵌套的子列表,则 fmt 将在格式化每个元素之前将其展平。因此,fmt[1, 2, [3, 4]] 视为具有 4 个元素的列表,而不是 3 个元素的列表。

有关格式字符串的更多信息,请参见sprintf

my @a = 8..11;
say @a.fmt('%03d'',');  # OUTPUT: «008,009,010,011␤»

方法 from§

假设列表包含Match 对象,并返回对列表第一个元素调用的 .from 的值。

'abcdefg' ~~ /(c)(d)/;
say $/.list.from;         # OUTPUT: «2␤» 
 
"abc123def" ~~ m:g/\d/;
say $/.list.from;         # OUTPUT: «3␤»

方法 to§

"abc123def" ~~ m:g/\d/;
say $/.to# OUTPUT: «6␤»

假设 List 包含Match,例如在正则表达式中使用 :g 修饰符时,$/ 变量是一个 List。返回对列表最后一个元素调用的 .to 的值。

方法 sink§

method sink(--> Nil{ }

它不执行任何操作,并返回Nil,正如定义中明确显示的那样。

sink [1,2,Failure.new("boo!"),"still here"]; # OUTPUT: «»

方法 Set§

通常,创建一个集合,其中包含列表元素作为成员。

say <æ ß þ €>.Set;  # OUTPUT: «Set(ß æ þ €)␤»

但是,如果列表包含非标量数据结构,则可能会有一些意外的更改。例如,对于Pairs

my @a = (:42a, :33b);
say @a;                # OUTPUT: «[a => 42 b => 33]␤» 
say @a.Set;            # OUTPUT: «Set(a b)␤»

集合将由键值不为 0 的 Pair 的key组成,从而消除所有值。请查看Set 文档以获取更多示例和更全面的解释。

运算符§

中缀 cmp§

multi infix:<cmp>(List @aList @b)

通过比较元素 @a[$i]@b[$i](对于某些 Int $i,从 0 开始)并返回 Order::LessOrder::SameOrder::More 来评估 Lists,具体取决于值是否不同以及如何不同。如果操作评估为 Order::Same,则将 @a[$i + 1]@b[$i + 1] 进行比较。重复此操作,直到一个大于另一个或所有元素都用尽为止。

如果 List 的长度不同,最多只进行 $n 次比较(其中 $n = @a.elems min @b.elems)。如果所有这些比较都评估为 Order::Same,则最终值将根据哪个 List 更长而选择。

say (123cmp (123);   # OUTPUT: «Same␤» 
say (456cmp (457);   # OUTPUT: «Less␤» 
say (789cmp (788);   # OUTPUT: «More␤» 
 
say (12)    cmp (123);   # OUTPUT: «Less␤» 
say (123cmp (12);      # OUTPUT: «More␤» 
say (9).List  cmp (^10).List;  # OUTPUT: «More␤»

Typegraph§

List 的类型关系
raku-type-graph List List Cool Cool List->Cool Positional Positional List->Positional Iterable Iterable List->Iterable Mu Mu Any Any Any->Mu Cool->Any Slip Slip Slip->List Array Array Array->List

展开上面的图表