my does Iterable does Positional
List
按顺序存储项目,并且可能延迟存储。
默认情况下,列表和数组的索引从 0 开始。
如果列表元素是容器,则可以对其进行赋值。使用数组将列表的每个值存储在容器中。
List
实现 Positional
,因此支持 下标。
不可变性§
列表是不可变对象,即列表中的元素数量及其元素本身都不能更改。因此,无法使用会更改列表结构本身的操作,例如 shift、unshift、push、pop、splice 和 绑定。
(1, 2, 3).shift; # Error Cannot call 'shift' on an immutable 'List'(1, 2, 3).unshift(0); # Error Cannot call 'unshift' on an immutable 'List'(1, 2, 3).push(4); # Error Cannot call 'push' on an immutable 'List'(1, 2, 3).pop; # Error Cannot call 'pop' on an immutable 'List'(1, 2, 3)[0]:delete; # Error Cannot remove elements from a List(1, 2, 3)[0] := 0; # Error Cannot use bind operator with this left-hand side(1, 2, 3)[0] = 0; # Error Cannot modify an immutable Int
List
不会 容器化 其元素,但如果任何元素碰巧位于 Scalar
容器中,则可以通过赋值替换元素的内容。
my = 'z';my = (, $, 'b');say [0].VAR.^name; # OUTPUT: «Scalar», containerizedsay [1].VAR.^name; # OUTPUT: «Scalar», containerizedsay [2].VAR.^name; # OUTPUT: «Str», non-containerized[0] = 'a'; # OK![1] = 'c'; # OK![2] = 'd'; # Error: Cannot modify an immutable List
项目、扁平化和符号§
在 Raku 中,将 List
赋值给标量变量不会丢失信息。区别在于,迭代通常将标量中的列表(或任何其他类列表的对象,如 Seq
或 Array
)视为单个元素。
my = (1, 2, 3);for # one iterationfor .list # three iterationsmy = [1, 2, 3];for # one iterationfor .list # three iterationsmy = 1, 2, 3;for # three iterationsfor .item # one iteration
此操作称为项目化或置于项目上下文中。.item
为对象执行此操作,以及 $( ... )
,在数组变量上为 $@a
。
列表通常不会插值(扁平化)到其他列表中,除非它们位于列表上下文中且是操作(如 append
)的单个参数。
my = (1, 2, 3);my = (, ); # two elementsmy = .map(); # six elements, with explicit Slipmy = <a b>;.append: .list; # The array variable @b has 5 elements, because# the list $a is the sole argument to appendsay .elems; # OUTPUT: «5»my = <a b>;.append: .list, 7; # The array variable @c has 4 elements, because# the list $a wasn't the only argument and thus# wasn't flatten by the append operationsay .elems; # OUTPUT: «4»my = <a b>;.append: ; # The array variable @d has 3 elements, because# $a is in an item context and as far as append is# concerned a single elementsay .elems; # OUTPUT: «3»
相同的扁平化行为适用于所有执行 Iterable
角色的对象,特别是 Hash
。
my = a => 1, b => 2;my = ; say .elems; # OUTPUT: «2»my = , ; say .elems; # OUTPUT: «1»my = $; say .elems; # OUTPUT: «1»
Slurpy 参数(*@a
)扁平化非项目化子列表
sub fe(*)say fe(<a b>, <d e>); # OUTPUT: «4»say fe(<a b>, <d e>.item); # OUTPUT: «3»
空列表使用 ()
创建。针对空列表进行智能匹配将检查元素是否存在。
my ;for , .list, .Seq -> \listoid# OUTPUT: «TrueTrueTrue»
从空列表中检索值将始终返回 Nil
say ()[33.rand]; # OUTPUT: «Nil»
强制转换为 Bool
也表示 List
是否获取任何元素。
my ;say [.elems, .Bool, ?]; # OUTPUT: «[0 False False]».push: 42;say [.elems, .Bool, ?]; # OUTPUT: «[1 True True]»say 'empty' unless ; # no output
方法§
方法 ACCEPTS§
multi method ACCEPTS(List: )
如果 $topic
是一个 Iterable
,则根据两个 Iterable
的内容是否匹配返回 True
或 False
。调用者中的 Whatever
元素匹配 $topic
Iterable
中对应位置的任何元素。一个 HyperWhatever
匹配任意数量的任何元素,包括没有元素
say (1, 2, 3) ~~ (1, *, 3); # OUTPUT: «True»say (1, 2, 3) ~~ (9, *, 5); # OUTPUT: «False»say (1, 2, 3) ~~ ( **, 3); # OUTPUT: «True»say (1, 2, 3) ~~ ( **, 5); # OUTPUT: «False»say (1, 3) ~~ (1, **, 3); # OUTPUT: «True»say (1, 2, 4, 5, 3) ~~ (1, **, 3); # OUTPUT: «True»say (1, 2, 4, 5, 6) ~~ (1, **, 5); # OUTPUT: «False»say (1, 2, 4, 5, 6) ~~ ( ** ); # OUTPUT: «True»say () ~~ ( ** ); # OUTPUT: «True»
此外,如果调用者或 $topic
是一个惰性 Iterable
,则返回 False
,除非 $topic
与调用者是同一个对象,在这种情况下返回 True
。
如果 $topic
不是一个 Iterable
,则如果调用者没有元素或其第一个元素是一个 Match
对象(此行为支持 m:g//
智能匹配),则返回调用者,否则返回 False
。
例程 list§
multi list(+list)multi method list(List:)
该方法仅返回调用者 self。该子例程遵守 单参数规则:如果使用单个参数调用,该参数是一个非 项化 Iterable
,则它返回一个基于参数 迭代器 的 List
;否则它仅返回参数列表。
例如
my = (1, 2); # an itemized Listput .list.raku; # OUTPUT: «(1, 2)»put list().raku; # OUTPUT: «($(1, 2),)»put list(|).raku; # OUTPUT: «(1, 2)»
最后一条语句使用 prefix:<|>
运算符将元组展平为参数列表,因此它等效于
put list(1, 2).raku; # OUTPUT: «(1, 2)»
还有其他方法可以列出项化单参数的元素。例如,你可以 解容器化 参数或使用 @
列表上下文化器
put list(<>).raku; # OUTPUT: «(1, 2)»put list(@).raku; # OUTPUT: «(1, 2)»
请注意,将类型对象转换为列表可能不会产生你期望的结果
put List.list.raku; # OUTPUT: «(List,)»
这是因为接受类型对象作为调用者的 .list
候选者由 Any
提供。该候选者返回一个包含一个元素的列表:类型对象 self。如果你正在开发一个集合类型,其类型对象应为空集合的有效表示,你可能希望为未定义的调用者提供你自己的候选者或使用“only”方法覆盖 Any:
候选者。例如
mymy LinkedList ; # an empty linked listput .list.raku; # OUTPUT: «()»
例程 elems§
sub elems( --> Int)method elems(List: --> Int)
返回列表中的元素数量。
say (1,2,3,4).elems; # OUTPUT: «4»
例程 end§
sub end( --> Int)method end(List: --> Int)
返回最后一个元素的索引。
say (1,2,3,4).end; # OUTPUT: «3»
例程 keys§
sub keys( --> Seq)method keys(List: --> Seq)
返回列表中索引的序列(例如,0..(@list.elems-1)
)。
say (1,2,3,4).keys; # OUTPUT: «0..3»
例程 values§
sub values( --> Seq)method values(List: --> Seq)
按顺序返回列表元素的序列。
say (1,2,3,4).^name; # OUTPUT: «List»say (1,2,3,4).values.^name; # OUTPUT: «Seq»
例程 kv§
sub kv( --> Seq)method kv(List: --> Seq)
返回索引和值的交错序列。例如
say <a b c>.kv; # OUTPUT: «(0 a 1 b 2 c)»
例程 pairs§
sub pairs( --> Seq)method pairs(List: --> Seq)
返回一个对序列,其中索引作为键,列表值作为值。
say <a b c>.pairs; # OUTPUT: «(0 => a 1 => b 2 => c)»
例程 antipairs§
method antipairs(List: --> Seq)
返回一个Seq
对,其中值作为键,索引作为值,即与pairs相反。
say <a b c>.antipairs; # OUTPUT: «(a => 0 b => 1 c => 2)»
例程 invert§
method invert(List: --> Seq)
假设 List 的每个元素都是Pair
。将所有元素作为Seq
返回Pair
,其中键和值已交换。如果Pair
的值是Iterable
,则它会将该Iterable
的值扩展为单独的对。
my = List.new('a' => (2, 3), 'b' => 17);say .invert; # OUTPUT: «(2 => a 3 => a 17 => b)»
例程 join§
sub join(, *)method join(List: = "")
通过对每个元素调用.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: )multi map(, +values)
应用于列表的示例在此包含,目的是为了说明。
对于列表,它为每个元素调用&code
,并按顺序收集返回值并返回。这是懒惰的,即仅在访问返回值时才调用&code
。示例
say ('hello', 1, 22/7, 42, 'world').map: # OUTPUT: «(Str Int Rat Int Str)»say map *.Str.chars, 'hello', 1, 22/7, 42, 'world'; # OUTPUT: «(5 1 8 2 5)»
map
检查代码对象的元数,并尝试向其中传递尽可能多的参数
sub b(, ) ;say <a b x y>.map().join(', '); # OUTPUT: «a before b, x before y»
一次迭代列表中的两个项目。
请注意,map
不会展开嵌入的列表和数组,因此
((1, 2), <a b>).map()
依次将(1, 2)
和<a b>
传递给块,导致总共两次迭代和结果序列"1,2", "a,b"
。
如果 &code
是 Block
循环相位器将被执行,循环控制语句将被视为循环控制流。请注意,return
在其定义的上下文中执行。它不是块的返回语句,而是周围的例程。使用 Routine
也将处理循环控制语句和循环相位器。任何 Routine
特定的控制语句或相位器都将在该 Routine
的上下文中处理。
sub s;s# OUTPUT: «hi»
方法 flatmap§
method flatmap(List: --> Seq)
便捷方法,类似于 .map(&block)
.flat
。
方法 gist§
multi method gist(List: --> Str)
返回包含 List 的括号内“要点”的字符串,最多列出前 100 个元素,用空格分隔,如果 List 有超过 100 个元素,则追加省略号。如果 List is-lazy
,则返回字符串 '(...)'
put (1, 2, 3).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 , *, :, :, :, : --> Seq)method grep(List: Mu , :, :, :, : --> Seq)
返回 $matcher
智能匹配的一系列元素。元素按它们在原始列表中出现的顺序返回。
示例
say ('hello', 1, 22/7, 42, 'world').grep: Int; # OUTPUT: «(1 42)»say grep , 'hello', 1, 22/7, 42, '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() # OUTPUT: «(b c d f)»
上面示例有效的原因是,布尔上下文的正则表达式适用于 $_
。在这种情况下,!
将 /<[aeiou]>/
正则表达式布尔化并否定结果。针对 Callable
(在这种情况下是 Block
)进行智能匹配会返回该可调用对象返回的值,因此正则表达式的布尔化结果用于决定当前值是否应保留在 grep 的结果中。
可选命名参数 :k
、:kv
、:p
、:v
提供与切片相同的功能
k
仅按顺序返回匹配元素的索引值。
kv
按顺序同时返回索引和匹配的元素。
p
按顺序将索引和匹配的元素作为 Pair
返回。
v
仅返回匹配的元素(与根本不指定任何命名参数相同)。
示例
say ('hello', 1, 22/7, 42, 'world').grep: Int, :k;# OUTPUT: «(1 3)»say grep , :kv, 'hello', 1, 22/7, 42, 'world';# OUTPUT: «(0 hello 2 3.142857 4 world)»say grep , :p, 'hello', 1, 22/7, 42, 'world';# OUTPUT: «(0 => hello 2 => 3.142857 4 => world)»
例程 first§
sub first(Mu , *, :, :, :, :)method first(List: Mu ?, :, :, :, :)
返回列表中第一个与 $matcher
智能匹配的项,当没有值匹配时返回 Nil
。可选命名参数 :end
表示搜索应从列表的末尾开始,而不是从开头开始。
示例
say (1, 22/7, 42, 300).first: * > 5; # OUTPUT: «42»say (1, 22/7, 42, 300).first: * > 5, :end; # OUTPUT: «300»say ('hello', 1, 22/7, 42, 'world').first: Complex; # OUTPUT: «Nil»
可选命名参数 :k
、:kv
、:p
提供与切片相同的功能
k
返回匹配元素的索引值。无论是否指定了 :end
命名参数,索引始终从列表的开头计数。
kv
同时返回索引和匹配的元素。
p
返回索引和匹配元素作为 Pair
。
示例
say (1, 22/7, 42, 300).first: * > 5, :k; # OUTPUT: «2»say (1, 22/7, 42, 300).first: * > 5, :p; # OUTPUT: «2 => 42»say (1, 22/7, 42, 300).first: * > 5, :kv, :end; # OUTPUT: «(3 300)»
在方法形式中,可以省略 $matcher
,在这种情况下,将返回第一个可用项(或如果设置 :end
,则返回最后一个)。另请参阅 head
和 tail
方法。
方法 head§
multi method head(Any:) is rawmulti method head(Any: Callable )multi method head(Any: )
此方法直接继承自 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:)multi method tail(List: --> Seq)
返回一个包含列表的最后一个$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( ); # OUTPUT: «(c d e)»
例程 categorize§
multi method categorize()multi method categorize(Whatever)multi method categorize(, :!, :)multi method categorize(, :)multi categorize(, +items, :!, * )multi categorize(, +items, * )
这些方法直接继承自 Any
;有关更多示例,请参阅 Any.list
。
此例程将值列表转换为哈希,表示根据 $test
对这些值进行分类,该 $test
对列表中的每个元素调用一次;每个哈希键表示一个或多个传入列表值的一种可能的分类,并且相应的哈希值包含由 $test
分类为关联键的类别的那些列表值数组,就像映射器一样。
请注意,与 classify 不同,后者假设映射器的返回值是一个值,categorize
总是假设映射器的返回值是适合当前值的类别列表。
示例
sub mapper(Int ) returns Listsay categorize , (1, 7, 6, 3, 2);# OUTPUT: «{even => [6 2], not prime => [1 6], odd => [1 7 3], prime => [7 3 2]}»
例程 classify§
multi method classify(, :!, :)multi method classify(, :)multi classify(, +items, :!, * )multi classify(, +items, * )
将值列表转换为表示这些值分类的哈希;每个哈希键表示一个或多个传入列表值的分类,并且相应的哈希值包含已分类为关联键类别的那些列表值数组。$test
将是一个表达式,它将根据元素将要分类的哈希键生成。
示例
say classify , (1, 7, 6, 3, 2);# OUTPUT: «{even => [6 2], odd => [1 7 3]}»say ('hello', 1, 22/7, 42, 'world').classify: ;# OUTPUT: «{1 => [1], 2 => [42], 5 => [hello world], 8 => [3.142857]}»
它还可以将 :as
作为命名参数,在对其进行分类之前转换该值
say <Innie Minnie Moe>.classify( , :as);# OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}»
此代码按字符数进行分类,这是已作为 $test
参数传递的表达式,但 :as
块在执行转换之前将其小写。命名参数 :into
也可用于将分类到新定义的变量中
<Innie Minnie Moe>.classify( , :as, :into( my ) );say ; # OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}»
我们正在动态声明 %words{Int}
的作用域,其键实际上是整数;它通过分类结果创建。
方法 Bool§
method Bool(List: --> Bool)
如果列表至少有一个元素,则返回 True
,如果列表为空,则返回 False
。
say ().Bool; # OUTPUT: «False»say (1).Bool; # OUTPUT: «True»
方法 Str§
method Str(List: --> Str)
将列表的元素字符串化并用空格连接它们(与 .join(' ')
相同)。
say (1,2,3,4,5).Str; # OUTPUT: «1 2 3 4 5»
方法 Int§
method Int(List: --> Int)
返回列表中的元素数量(与 .elems
相同)。
say (1,2,3,4,5).Int; # OUTPUT: «5»
方法 Numeric§
method Numeric(List: --> Int)
返回列表中的元素数量(与 .elems
相同)。
say (1,2,3,4,5).Numeric; # OUTPUT: «5»
方法 Capture§
method Capture(List: --> Capture)
返回一个 Capture
,其中 <List
中的每个 Pair
(如果有)已转换为命名参数(键 of Pair
已字符串化)。List
中的所有其他元素都按其出现的顺序转换为位置参数,即列表中的第一个非对项目成为第一个位置参数,其索引为 0
,第二个非对项目成为第二个位置参数,其索引为 1
等。
my = (7, 5, a => 2, b => 17);my = .Capture;say .keys; # OUTPUT: «(0 1 a b)»my-sub(|); # OUTPUT: «7, 5, 2, 17»sub my-sub(, , :, :)
一个更高级的示例演示了将返回的 Capture
与 Signature
匹配。
my = (7, 5, a => 2, b => 17);say so .Capture ~~ :($ where * == 7,$,:,:); # OUTPUT: «True»= (8, 5, a => 2, b => 17);say so .Capture ~~ :($ where * == 7,$,:,:); # OUTPUT: «False»
例程 pick§
multi pick(, * --> Seq)multi method pick(List: --> Seq)multi method pick(List: --> Mu)multi method pick(List: Callable --> Seq)
如果提供了 $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(, * --> Seq)multi method roll(List: --> Seq)multi method roll(List: --> Mu)
如果提供了 $count
:返回一个 $count
个元素的序列,每个元素都是从列表中随机选择的。每个随机选择都是独立进行的,就像一个单独的骰子掷法,其中每个骰子面都是一个列表元素。如果将 *
传递为 $count
,则返回一个惰性无限序列,其中包含从原始列表中随机选择的元素。
如果省略了 $count
:从列表中返回一个随机项,如果列表为空,则返回 Nil
示例
say <a b c d e>.roll; # 1 random lettersay <a b c d e>.roll: 3; # 3 random letterssay roll 8, <a b c d e>; # 8 random lettersmy := (^10).roll(*);say [^15]; # 15 random digits
例程 eager§
multi method eager(List: --> List)
急切地计算 List
中的所有元素,并将其作为 List
返回。
my \ll = (lazy 1..5).cache;say ll[]; # OUTPUT: «(...)»say ll.eager # OUTPUT: «(1 2 3 4 5)»
例程 reverse§
multi reverse(* --> Seq)multi method reverse(List: --> Seq)
返回一个 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(, Int = 1 --> Seq)multi method rotate(List: Int = 1 --> Seq)
当 $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(* --> Seq)multi sort(, * --> Seq)multi method sort(List: --> Seq)multi method sort(List: --> Seq)
对列表进行排序,最小的元素排在最前面。默认情况下,使用 infix:<cmp>
对列表元素进行比较。
如果提供了 &custom-routine-to-use
,并且它接受两个参数,则会对列表元素对调用它,并且应该返回 Order::Less
、Order::Same
或 Order::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, -4, 7, -1, 2, 0).sort; # OUTPUT: «(-4 -1 0 2 3 7)»say (3, -4, 7, -1, 2, 0).sort: *.abs; # OUTPUT: «(0 -1 2 3 -4 7)»say (3, -4, 7, -1, 2, 0).sort: ; # OUTPUT: «(7 3 2 0 -4 -1)»
此外,如果 &custom-routine-to-use
返回一个 List
,则元素将基于多个值进行排序,如果先前元素之间的比较结果为 Order::Same
,则 List
中的后续值将用于打破平局。
my = (%( first-name => 'Kyle', last-name => 'Reese' ),%( first-name => 'Sarah', last-name => 'Connor' ),%( first-name => 'John', last-name => 'Connor' ),);.say for .sort: ;#`(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( );# OUTPUT: «(c bb aaa bbb ccc ddd)»
在这种情况下,数组的元素按升序排序,首先按字符串长度(.chars
)排序,如果长度完全相同,则按实际字母顺序(.Str
)排序。
可以在此使用任意数量的条件
say <01 11 111 2 20 02>.sort( );# 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: )multi reduce (, +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 :<+>, (1, 2, 3); # OUTPUT: «6»say (1, 2, 3).reduce: :<+>; # OUTPUT: «6»say reduce , (5, 9, 12, 1); # OUTPUT: «12»
如果 list
只包含一个元素,则如果可能,该运算符将应用于该元素;如果不能,则返回元素本身。
say reduce :<->, (10,); # OUTPUT: «10»
当列表不包含任何元素时,会引发异常,除非 &with
是具有已知标识值的运算符(例如,infix:<+>
的标识值为 0)。因此,建议你在输入列表前加上一个初始值(或显式标识值)
my \strings = "One good string!", "And one another good string!";say reduce , '', |strings; # like strings.joinmy \numbers = 1, 2, 3, 4, 5;say reduce , 0, |numbers; # like numbers.maxsub count-and-sum-evens( (Int \count, Int \sum), Int \x )say reduce , (0, 0), |numbers; # OUTPUT: «(2 6)»
在最后一个示例中,由于 reduce
仅支持一个初始值,因此我们使用一个包含两个值的 List
,它本身是一个单一值。count-and-sum-evens
子例程采用两个位置值:一个包含两个 Int
的 List
和一个 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(:<**>).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(:<->).say; # OUTPUT: «-5»((2-3)-4).say; # OUTPUT: «-5»(2-3-4).say; # OUTPUT: «-5»
由于使用中缀操作符进行归约是一件常见的事情,因此 归约元操作符 [ ]
提供了一个语法快捷方式。因此,不必将操作符的代码对象传递给 reduce
,只需将操作符直接传递给 [ ]
。要改用用户定义的子例程,请在子例程的代码对象周围提供一层额外的方括号
say [*] (1, 2, 3, 4); # OUTPUT: «24»say [min] (4, 2, 1, 3); # OUTPUT: «1»sub mult ;say [[]] (1, 2, 3, 4); # OUTPUT: «24»
在语义上,所有以下操作都执行相同的事情
my \numbers = 1, 2, 3, 4, 5;say reduce , 0, |numbers;say reduce * + *, 0, |numbers;say reduce &[+], numbers; # operator does not need explicit identity valuesay [+] numbers;
由于 reduce
是一个隐式循环,它会通过 &with
中的 归约 子例程进行迭代,因此它会响应 next
、last
和 redo
语句
sub last-after-seven ;say (2, 3, 4, 5).reduce: ; # OUTPUT: «9»
reduce
是从左向右还是从右向左累积元素取决于操作符。在函数式编程世界中,此操作通常称为 折叠。对于右结合操作符,它是一个 右折叠,否则(通常)它是一个 左折叠。在 Raku 中,您可以使用 is assoc
指定操作符的结合性。
sub infix:<foo>(, ) is assoc<right>say [foo] 1, 2, 3, 4; # OUTPUT: «(1, (2, (3, 4)))»sub infix:<bar>(, ) is assoc<left>say [bar] 1, 2, 3, 4; # OUTPUT: «(((1, 2), 3), 4)»
实际示例 1: 在此示例中,我们使用 reduce
生成一个随机数学公式(例如,“(4 + ((3 * x) + 11) / 6))”)。
my = [Z] (<+ - * />, 1..20)».roll(4);say ('x', |).reduce: -> , [, ]
实际示例 2: 假设我们有一个表示为整数系数列表的多项式,c[n-1]、c[n-2]、...、c[0],其中 c[i] 是 xi 的系数。我们可以使用 map
和 reduce
如下对其进行求值
sub evaluate(List \c where c.all ~~ Int, Rat \x --> Rat)my \c = 2, 3, 1; # 2x² + 3x + 1say evaluate c, 3.0; # OUTPUT: «28»say evaluate c, 10.0; # OUTPUT: «231»
例程 produce§
multi produce(, *)multi method produce(List: )
通过迭代应用一个知道如何组合 两个 值的函数,生成所有中间“组合”值以及最终结果的列表。
如果 @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 = (1,2,3,4,5);say produce , ;say produce * + *, ;say produce &[+], ; # operator does not need explicit identitysay [\+] ; # 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
中的 next
、last
和 redo
语句
say (2,3,4,5).produce: ; # OUTPUT: «(2 5 9)»
例程 combinations§
multi combinations(, = 0..* --> Seq)multi method combinations(List: Int() --> Seq)multi method combinations(List: Iterable = 0..* --> Seq)
返回一个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 3, 2# OUTPUT:# (0 1)# (0 2)# (1 2)
注意:某些实现可能会限制非Iterable
$from
的最大值。在 Rakudo 上,64 位系统限制为2³¹-1
,32 位系统限制为2²⁸-1
。
例程 permutations§
multi permutations(Int() --> Seq)multi permutations(Iterable --> Seq)multi method permutations(List: --> Seq)
将列表的所有可能排列作为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(*, Bool() : --> Seq)
返回一个列表序列,其中每个子列表由调用者的元素组成。
在最简单的情况下,@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(2, 3).join('|'); # OUTPUT: «a b|c d e|f g»say ('a'..'h').rotor(1 => 1, 3).join('|'); # OUTPUT: «a|c d e|f»
组合多个循环和:partial
也适用
say ('a'..'h').rotor(1 => 1, 3 => -1, :partial).join('|');# OUTPUT: «a|c d e|e|g h»
multi rotor(Int , \source, Bool() : --> Seq)
multi rotor(*, \source, Bool() : --> Seq)
在 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 => 1, 3 => -1, 'a'..'h', :partial).join('|');# OUTPUT: «a|c d e|e|g h»
方法 batch§
multi method batch(Int --> Seq)multi method batch(Int : --> Seq)
返回一个列表序列,其中除最后一个列表外的每个列表都保证包含一个元素数等于 $batch
或 $elems
分别指定のバッチサイズ。如果调用者具有一个元素数不是バッチサイズの整数倍数,则返回序列中的最后一个列表将包含任何剩余元素,因此少于 $batch
或 $elems
个元素。因此,.batch($batch)
是 .rotor($batch, :partial)
的简写。
例程 cross§
sub cross(+, : --> Seq)
计算两个或更多个列表或 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([1, 2, 3], [4, 5, 6], :with(:<*>)).join(",");# OUTPUT: «4,5,6,8,10,12,12,15,18»
X
运算符也可以与另一个运算符组合为元运算符,以执行规约
say ([1, 2, 3] X* [4, 5, 6]).join(",")# same output as the previous example
例程 zip§
sub zip(+, : --> Seq)
从多个输入列表或其他 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> -> [,,]# OUTPUT: «a,d,gb,e,hc,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>, [1, 2, 3], (1, 2, 3), :with(:<*>);# OUTPUT: «1827»
Z
形式也可以用于通过使用元运算符隐式设置 with
参数来执行规约
.say for <1 2 3> Z* [1, 2, 3] Z* (1, 2, 3); # 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([1, 2], [2, 3], [3, 4]);# OUTPUT: «1,2,32,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([1, 2], [2, 3, 57, 77], [3, 4, 102]);# OUTPUT: «1,2,32,3,457,10277»
因此,在“压缩”操作中不会丢失任何数据值。但是,无法从结果序列中收集到哪个输入列表提供了哪个元素的记录。
roundrobin
可用于组合混乱的数据,以便随后可以执行手动后处理步骤。
sub roundrobin(+list-of-lists, : --> 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( )method sum(List:)
返回列表中所有元素的总和,如果列表为空,则返回 0。如果无法将元素强制转换为数字,则抛出异常。
say (1, 3, pi).sum; # OUTPUT: «7.14159265358979»say (1, "0xff").sum; # OUTPUT: «256»say sum(0b1111, 5); # OUTPUT: «20»
如果列表包含一个Junction
,则结果将相应地是一个Junction
say ( 1|2, 3).sum; # OUTPUT: «any(4, 5)»
当在原生整数数组上调用它时,还可以指定一个名为 :wrap
的命名参数。这将把值作为原生整数相加,如果它们超过原生整数的大小,则进行环绕。如果您确定不会超过该值,或者您不介意,使用 :wrap
将使计算速度提高约 20 倍。
my int = ^1_000_000;say .sum(:wrap); # OUTPUT: «499999500000»
方法 fmt§
method fmt( = '%s', = ' ' --> Str)
返回一个字符串,其中列表中的每个元素都已根据 $format
进行格式化,并且每个元素都用 $separator
分隔。如果列表包含嵌套的子列表,则 fmt
将在格式化每个元素之前将其展平。因此,fmt
将 [1, 2, [3, 4]]
视为具有 4 个元素的列表,而不是 3 个元素的列表。
有关格式字符串的更多信息,请参见sprintf。
my = 8..11;say .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(ß æ þ €)»
但是,如果列表包含非标量数据结构,则可能会有一些意外的更改。例如,对于Pair
s
my = (:42a, :33b);say ; # OUTPUT: «[a => 42 b => 33]»say .Set; # OUTPUT: «Set(a b)»
集合将由键值不为 0 的 Pair 的key
组成,从而消除所有值。请查看Set
文档以获取更多示例和更全面的解释。
运算符§
中缀 cmp
§
multi infix:<cmp>(List , List )
通过比较元素 @a[$i]
与 @b[$i]
(对于某些 Int $i
,从 0 开始)并返回 Order::Less
、Order::Same
或 Order::More
来评估 Lists
,具体取决于值是否不同以及如何不同。如果操作评估为 Order::Same
,则将 @a[$i + 1]
与 @b[$i + 1]
进行比较。重复此操作,直到一个大于另一个或所有元素都用尽为止。
如果 List
的长度不同,最多只进行 $n
次比较(其中 $n = @a.elems min @b.elems
)。如果所有这些比较都评估为 Order::Same
,则最终值将根据哪个 List
更长而选择。
say (1, 2, 3) cmp (1, 2, 3); # OUTPUT: «Same»say (4, 5, 6) cmp (4, 5, 7); # OUTPUT: «Less»say (7, 8, 9) cmp (7, 8, 8); # OUTPUT: «More»say (1, 2) cmp (1, 2, 3); # OUTPUT: «Less»say (1, 2, 3) cmp (1, 2); # OUTPUT: «More»say (9).List cmp (^10).List; # OUTPUT: «More»