List — 항목을 순서대로 저장하는 리스트

List — 항목을 순서대로 저장하는 리스트

List는 항목들을 순서대로, 그리고 잠재적으로 지연(lazy)하며 저장해요.

리스트와 배열의 인덱스는 기본적으로 0부터 시작해요.

리스트 요소가 컨테이너라면 그 요소에 할당할 수 있어요. 리스트의 모든 값을 컨테이너에 저장하고 싶다면 배열(Array)을 쓰면 돼요.

ListPositional을 구현하므로 첨자를 지원해요.

출처: Raku Documentation — List

본문

불변성 (Immutability)

리스트는 불변 객체예요. 즉 리스트의 요소 수나 요소 자체를 바꿀 수 없어요. 따라서 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는 요소를 containerize하지 않아요. 하지만 어떤 요소가 우연히 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

항목화, 평탄화, 시길 (Items, flattening and sigils)

Raku에서 List를 스칼라 변수에 할당해도 정보는 잃지 않아요. 차이는 반복(iteration)이 스칼라 안의 리스트(또는 SeqArray 같은 리스트류 객체)를 기본적으로 하나의 요소로 취급한다는 점이에요.

my $s = (1, 2, 3);
for $s { }      # one iteration
for $s.list { } # three iterations

my $t = [1, 2, 3];
for $t { }      # one iteration
for $t.list { } # three iterations

my @a = 1, 2, 3;
for @a { }      # three iterations
for @a.item { } # one iteration

이 연산을 항목화(itemization) 또는 항목 컨텍스트에 넣기라고 불러요. .item이 객체에게 그 일을 해주고, $( ... )가, 그리고 배열 변수에 대해서는 $@a도 같은 일을 해요.

리스트는 일반적으로 다른 리스트로 보간(평탄화)되지 않아요. 단, 리스트 컨텍스트에 있고 append 같은 연산의 유일한 인자일 때는 예외예요.

my $a = (1, 2, 3);
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.list, 7;  # The array variable @c has 4 elements, because
                        # the list $a wasn't the only argument and thus
                        # wasn't flattened 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 = a => 1, b => 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␤»

슬러피 파라미터(*@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

Methods

method ACCEPTS

multi method ACCEPTS(List:D: $topic)

$topicIterable이면, 두 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␤»

추가로, 호출자나 $topiclazy Iterable이면 False를 반환해요. 단, $topic이 호출자와 같은 객체라면 True를 반환해요.

$topicIterable아니면, 호출자에 요소가 없거나 첫 번째 요소가 Match 객체면(m:g// 스마트매치가 이 동작을 씀) 호출자를 반환하고, 아니면 False를 반환해요.

routine list

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

메서드는 호출자 self를 그대로 반환해요. 서브루틴은 단일 인자 규칙을 따르는데, 단일 인자가 itemized되지 않은 Iterable이면 그 인자의 iterator에 기반한 List를 반환하고, 아니면 인자 리스트를 그대로 반환해요.

예를 들어:

my $tuple = (1, 2);         # 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(1, 2).raku;        # OUTPUT: «(1, 2)␤»

단일 인자로 항목화된 요소들을 나열하는 다른 방법도 있어요. 예를 들어 인자를 decontainerize하거나 @ 리스트 컨텍스트화를 쓸 수 있어요.

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

타입 객체를 리스트로 변환하면 예상치 못한 결과가 나올 수 있다는 점을 주의하세요.

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

이것은 타입 객체를 호출자로 받는 .list 후보가 Any에서 제공되기 때문이에요. 그 후보는 요소가 하나(타입 객체 자체)인 리스트를 반환해요. 타입 객체가 빈 컬렉션의 유효한 표현이 되기를 원하는 컬렉션 타입을 개발한다면, 정의되지 않은 호출자용 후보를 직접 제공하거나 Any: 후보를 "only" 메서드로 덮어쓸 수 있어요. 예를 들어:

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: «()␤»

routine elems

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

리스트의 요소 수를 반환해요.

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

routine end

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

마지막 요소의 인덱스를 반환해요.

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

routine keys

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

리스트 안의 인덱스 시퀀스(예: 0...(@list.elems-1))를 반환해요.

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

routine 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␤»

routine kv

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

인덱스와 값이 번갈아 나오는 시퀀스를 반환해요. 예를 들어

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

routine pairs

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

인덱스를 키로, 리스트 값을 값으로 하는 pair들의 시퀀스를 반환해요.

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

routine antipairs

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

값을 키로, 인덱스를 값으로 하는 Seq of pairs를 반환해요. 즉 pairs의 정반대예요.

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

routine invert

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

List의 모든 요소가 Pair라고 가정해요. 키와 값이 교환된 모든 요소를 Pair들의 Seq로 반환해요. Pair의 값이 Iterable이면, 그 Iterable의 값들을 별도의 pair들로 확장해요.

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

routine 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␤»

서브루틴 형태는 slurpy하게 동작해, 첫 번째 이후의 모든 인자를 하나의 리스트로 평탄화해요.

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

이 경우 <a b c> 리스트는 메서드로 join을 호출할 때와 달리 slurp되어 평탄화돼요.

리스트의 요소 중 하나가 Junction이면 join도 가능한 만큼 연결이 이루어진 Junction을 반환해요.

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

routine map

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

설명을 돕기 위해 리스트에 적용한 예시를 여기 포함해요.

리스트에 대해, 각 요소에 &code를 호출하고 반환 값들을 시퀀스에 모아 반환해요. 이는 지연(lazy) 되므로, &code는 반환 값들이 접근될 때만 호출돼요.

예시:

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

map은 코드 객체의 arity를 검사해, 기대하는 만큼의 인자를 넘기려 시도해요.

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

이는 리스트를 한 번에 두 항목씩 반복해요.

map은 내장된 리스트와 배열을 평탄화하지 않는다는 점을 주의하세요. 따라서

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

는 블록에 (1, 2)<a b>를 차례로 넘겨, 총 두 번의 반복과 결과 시퀀스 "1,2", "a,b"를 만들어요.

&codeBlock이면 루프 phaser가 실행되고 루프 제어 문장이 루프 제어 흐름처럼 취급돼요. return은 그 정의 컨텍스트에서 실행된다는 점을 주의하세요. 그것은 블록의 return이 아니라 주변 Routine의 return이에요. Routine을 쓰면 루프 제어 문장과 루프 phaser도 처리돼요. Routine 특유의 제어 문장이나 phaser는 그 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␤»

method flatmap

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

.map(&block) 뒤에 .flat을 붙인 것과 유사한 편의 메서드예요.

method gist

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

List의 괄호로 감싼 "gist"를 담은 문자열을 반환해요. 처음 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 ...)

routine 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', 1, 22/7, 42, 'world').grep: Int;              # OUTPUT: «(1 42)␤»
say grep { .Str.chars > 3 }, '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({! /<[aeiou]>/})     # OUTPUT: «(b c d f)␤»

위 예시가 동작하는 이유는 Boolean 컨텍스트의 정규식이 $_에 스스로 적용되기 때문이에요. 이 경우 !/<[aeiou]>/ 정규식을 boolify하고 결과를 부정해요. Callable(여기선 Block)에 스마트매치하면 그 callable이 반환한 값이 반환되므로, 정규식의 boolify된 결과가 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 { .Str.chars > 3 }, :kv, 'hello', 1, 22/7, 42, 'world';
# OUTPUT: «(0 hello 2 3.142857 4 world)␤»
say grep { .Str.chars > 3 }, :p, 'hello', 1, 22/7, 42, 'world';
# OUTPUT: «(0 => hello 2 => 3.142857 4 => world)␤»

routine first

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

$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가 설정돼 있으면 마지막 항목)이 반환돼요. headtail 메서드도 참고하세요.

method 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)␤»

method tail

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

리스트의 마지막 $n개 항목을 포함하는 Seq를 반환해요. $n <= 0이면 빈 Seq를 반환해요. 인자를 지정하지 않으면 마지막 요소로 기본 설정돼요. 리스트가 lazy하면 예외를 던져요.

예시:

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␤»

첫 번째 경우 $nWhateverCode 형태를 취해 처음부터 제외할 요소 수를 나타내요. $n은 Callable일 수 있으며(그 경우 0 값으로 호출), 숫자로 변환될 수 있는 다른 무엇이든 될 수 있고(그 경우 그 값을 출력 Seq의 요소 수로 사용), 입니다.

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

routine 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.categorize를 참고하세요.

이 루틴은 값들의 리스트를 $test에 따른 그 값들의 범주화(categorizations) 를 나타내는 해시로 변환해요. $test는 리스트의 각 요소에 대해 한 번씩 호출돼요. 각 해시 키는 들어오는 리스트 값 하나 이상에 대한 가능한 범주 하나를 나타내고, 대응하는 해시 값은 $test에 의해 관련 키의 범주로 매퍼처럼 분류된 그 리스트 값들의 배열을 담아요.

classify와 달리(이것은 매퍼의 반환 값이 단일 값이라고 가정), categorize는 매퍼의 반환 값이 현재 값에 적절한 범주들의 리스트라고 항상 가정한다는 점을 주의하세요.

예시:

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

routine classify

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

값들의 리스트를 그 값들의 분류(classification) 를 나타내는 해시로 변환해요. 각 해시 키는 들어오는 리스트 값 하나 이상에 대한 분류를 나타내고, 대응하는 해시 값은 관련 키의 범주로 분류된 그 리스트 값들의 배열을 담아요. $test는 요소들이 분류될 해시 키를 만들어낼 표현식이에요.

예시:

say classify { $_ %% 2 ?? 'even' !! 'odd' }, (1, 7, 6, 3, 2);
# OUTPUT: «{even => [6 2], odd => [1 7 3]}␤»
say ('hello', 1, 22/7, 42, '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 $_ }, :into( my %words{Int} ) );
say %words; # OUTPUT: «{3 => [moe], 5 => [innie], 6 => [minnie]}␤»

우리는 %words{Int}의 스코프를 즉석에서 선언하고 있는데, 키는 실제로 정수예요. 분류 결과로 생성되죠.

method Bool

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

리스트에 요소가 하나 이상이면 True, 빈 리스트면 False를 반환해요.

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

method Str

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

리스트의 요소들을 문자열화하고 공백으로 이어 붙여요(.join(' ')과 같음).

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

method Int

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

리스트의 요소 수를 반환해요(.elems과 같음).

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

method Numeric

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

리스트의 요소 수를 반환해요(.elems과 같음).

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

method Capture

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

List의 각 Pair가(있다면) (Pairkey를 문자열화해) 명명 인자로 변환된 Capture를 반환해요. List의 다른 모든 요소는 발견된 순서대로 위치 인자로 변환돼요. 즉 리스트의 첫 번째 non-pair 항목이 첫 번째 위치 인자가 되어 인덱스 0을, 두 번째 non-pair 항목이 두 번째 위치 인자가 되어 인덱스 1을 얻는 식이에요.

my $list = (7, 5, a => 2, b => 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 = (7, 5, a => 2, b => 17);
say so $list.Capture ~~ :($ where * == 7,$,:$a,:$b); # OUTPUT: «True␤»

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

routine 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가 리스트 크기보다 크거나 같으면, 호출자 리스트의 모든 요소가 무작위 시퀀스로 돌아와요. 즉 섞여서(shuffled) 반환돼요.

메서드 형태에서 $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 릴리스부터는 count로 **(aka HyperWhatever)도 지정할 수 있어요.

그 경우 .pick은 원본 리스트가 소진된 후 다시, 계속해서 무한히 뽑기 시작해요.

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

routine roll

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

$count가 제공되면: 각각 리스트에서 무작위로 선택된 $count개의 요소 시퀀스를 반환해요. 각 무작위 선택은 주사위를 개별적으로 굴려 각 면이 리스트 요소인 것처럼 독립적으로 이루어져요. $count*를 넘기면 원본 리스트에서 무작위로 선택된 요소들의 lazy한 무한 시퀀스를 반환해요.

$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

routine eager

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

List의 모든 요소를 즉시(eagerly) 평가하고, 그것들을 List로 반환해요.

my  \ll = (lazy 1..5).cache;

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

routine 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)␤»

routine 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 릴리스 이전에는 Seq 대신 새 List가 반환됐어요.

routine 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:가 사용돼요.

&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의 반환 값은 캐시되어, 리스트 요소당 한 번만 호출돼요.

예시:

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: { $^b leg $^a }; # OUTPUT: «(7 3 2 0 -4 -1)␤»

추가로, &custom-routine-to-useList를 반환하면, 요소들은 여러 값에 기반해 정렬돼요. 이전 요소들 사이의 비교가 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)␤»

인자 없이 sort sub를 호출하는 것은 Rakudo 2022.07 릴리스부터 런타임 오류가 됐어요.

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)␤»

routine 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:<+>, (1, 2, 3); # OUTPUT: «6␤»
say (1, 2, 3).reduce: &infix:<+>; # OUTPUT: «6␤»
say reduce &max, (5, 9, 12, 1);   # 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 = 1, 2, 3, 4, 5;
say reduce { $^a > $^b ?? $^a !! $^b }, 0, |numbers; # like numbers.max

sub count-and-sum-evens( (Int \count, Int \sum), Int \x ) {
    x %% 2 ?? (count+1, sum+x) !! (count, sum)
}

say reduce &count-and-sum-evens, (0, 0), |numbers;    # OUTPUT: «(2 6)␤»

마지막 예시에서 reduce는 초기 값 하나만 지원하므로, 두 값의 List(그 자체로 단일 값)를 사용해요. count-and-sum-evens 서브루틴은 두 IntListInt라는 두 위치 값을 받고, 누적된 짝수 정수의 개수와 합을 저장한 List를 반환해요.

&with연산자의 코드 객체이면, 그 고유 항등 값과 결합성(associativity)이 존중돼요. 즉 (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␤»

infix 연산자로 reduce하는 것은 흔한 일이라, reduction 메타연산자 [ ]가 문법적 단축키를 제공해요. 따라서 연산자의 코드 객체를 reduce에 넘기는 대신, 연산자를 [ ]에 직접 넘기면 돼요. 사용자 정의 서브루틴을 쓸 때는 서브루틴의 코드 객체 주위에 대괄호 한 겹을 더 추가하면 돼요.

say [*] (1, 2, 3, 4);       # OUTPUT: «24␤»
say [min] (4, 2, 1, 3);     # OUTPUT: «1␤»

sub mult { $^a * $^b };
say [[&mult]] (1, 2, 3, 4); # OUTPUT: «24␤»

의미론적으로, 다음 모두는 같은 일을 해요.

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

reduce는 그 reducing 서브루틴으로 반복하는 암시적 루프이므로, &with 안의 next, last, redo 문장에 응답해요.

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

reduce가 요소를 왼쪽에서부터 누적할지 오른쪽에서부터 누적할지는 연산자에 달려 있어요. 함수형 프로그래밍 세계에서 이 연산은 보통 fold라고 불러요. 오른쪽 결합 연산자와 함께하면 right fold, 아니면(그리고 보통은) left fold예요. Raku에서 연산자의 결합성은 is assoc 트레잇으로 지정할 수 있어요.

sub infix:<foo>($a, $b) is assoc<right> { "($a, $b)" }
say [foo] 1, 2, 3, 4; # OUTPUT: «(1, (2, (3, 4)))␤»

sub infix:<bar>($a, $b) is assoc<left> { "($a, $b)" }
say [bar] 1, 2, 3, 4; # 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]는 x^i의 계수)이 있다고 가정해요. mapreduce로 이렇게 평가할 수 있어요.

sub evaluate(List:D \c where c.all ~~ Int, Rat: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 = 2, 3, 1;       # 2x² + 3x + 1
say evaluate c, 3.0;   # OUTPUT: «28␤»
say evaluate c, 10.0;  # OUTPUT: «231␤»

routine 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)␤»

삼각 메타연산자 [\ ]가 infix 연산자로 produce하는 문법적 단축키를 제공해요.

# 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 안의 next, last, redo 문장에 응답해요.

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

routine 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)

호출자 리스트의 모든 $of-조합을 담은 Seq를 반환해요. $of는 숫자 Range일 수 있고, 그 경우 그것이 나타내는 항목 수 범위의 조합들이 반환돼요(즉 2.6 .. 4는 2-, 3-, 4-항목 조합을 반환해요). 그 외에는 $ofInt로 강제 변환돼요.

.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가 0이면 빈 리스트를 담은 1-항목 리스트가 반환돼요(항목을 하나도 고르지 않는 방법은 정확히 하나 있기 때문).

서브루틴 형태는 첫 번째 인자($from)에 호출된 메서드 형태와 동등해요. 단, $fromIterable이 아니면 Int로 강제 변환되고 0..^$from으로 만든 Range에서 조합이 만들어져요.

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

참고: 일부 구현은 non-Iterable $from의 최대 값을 제한할 수 있어요. Rakudo에서 64비트 시스템은 2³¹-1, 32비트 시스템은 2²⁸-1 한도가 있어요.

routine 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에서 순열을 계산해요. $fromIterable이 아니면 $fromInt로 강제 변환하고 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)

routine rotor

method rotor(+@cycle, Bool() :$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이면, 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␤»

rotor에 대한 더 자세한 설명은 이 블로그 글을 참고하세요.

6.e 언어 버전부터 서브루틴 rotor도 있습니다 (조기 구현은 Rakudo 2022.02+ 컴파일러에 존재).

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

기술적으로 위치 파라미터 \thing 앞에 slurpy **@cycle을 두는 것은 불가능해서, 두 번째 multi의 실제 시그니처는 Rakudo에서 다릅니다. 하지만 실무에서는 여기 제시된 시그니처에서 기대하는 대로 동작해요.

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␤»

method batch

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

각 리스트가 마지막 리스트를 제외하면 $batch 또는 $elems가 각각 지정한 배치 크기와 같은 수의 요소를 보장하는 리스트들의 시퀀스를 반환해요. 호출자가 배치 크기의 정수 배수가 아닌 수의 요소를 가지면, 반환 시퀀스의 마지막 리스트가 나머지 요소들을 담아 $batch 또는 $elems보다 적은 요소를 가져요. 따라서 .batch($batch).rotor($batch, :partial)의 줄임말이에요.

routine cross

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

둘 이상의 리스트 또는 Iterable교차곱(cross-product) 을 계산해요. 각 리스트의 첫 번째 항목은 첫 번째 iterable의 항목, 두 번째는 두 번째 iterable의 항목인 식의 리스트들의 시퀀스를 반환해요. 모든 항목이 다른 모든 리스트의 모든 다른 항목과 짝지어져요.

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

cross 루틴에는 X라는 infix 동의어도 있어요.

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

선택 with 파라미터를 넘기면, 교차곱 항목 각각에 적용할 reduction 연산으로 사용돼요.

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

X 연산자는 다른 연산자와 메타연산자로 결합되어 reduction을 수행할 수도 있어요.

say ([1, 2, 3] X* [4, 5, 6]).join(",")
# same output as the previous example

routine zip

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

여러 입력 리스트 또는 다른 Iterable들로부터, 시퀀스로 반환되는 '리스트들의 리스트'를 만들어요.

zip은 각 입력 리스트를 동기적으로 반복해서, 리스트들이 제공된 순서대로 입력 리스트 인덱스에 따라 요소들을 그룹화하며 'Zipping'해요.

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

zip에는 Z 연산자라는 infix 동의어가 있어요.

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))

데이터 잘림이 가능하지만 바람직하지 않은 경우에는 zip 대신 roundrobin을 고려하세요.

선택 with 파라미터는 zipped된 리스트들을 추가로 reduction해요. 예를 들어 다음은 대응하는 요소들을 곱해 단일 곱들의 리스트를 반환해요.

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

Z 형태는 메타연산자로 with 파라미터를 암시적으로 설정해 reduction을 수행할 수도 있어요.

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

routine 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,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([1, 2], [2, 3, 57, 77], [3, 4, 102]);
# OUTPUT: «1,2,3␤2,3,4␤57,102␤77␤»

따라서 'zipping' 연산에서 데이터 값이 손실되지 않아요. 다만 어떤 입력 리스트가 어떤 요소를 제공했는지에 대한 기록은 결과 시퀀스에서 알 수 없어요.

roundrobin은 어수선한 데이터를 수동 후처리 단계를 수행할 수 있는 지점까지 결합하는 데 유용해요.

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

Rakudo 2022.02 릴리스부터 :slip 명명 인자를 지정할 수도 있어요. 참값으로 지정하면 생성된 값들을 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)␤»

routine sum

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

리스트에 있는 모든 요소의 합계를 반환하거나, 리스트가 비어 있으면 0을 반환해요. 어떤 요소가 Numeric으로 강제 변환될 수 없으면 예외를 던져요.

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 명명 인자도 지정할 수 있어요. 이는 값을 네이티브 정수로 더해, 네이티브 정수 크기를 초과하면 감싸서(wrapping) 처리해요. 그 값을 초과하지 않을 거라 확신하거나 상관없다면, :wrap을 쓰면 계산이 약 20배 빨라져요.

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

method fmt

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

리스트의 각 요소가 $format에 따라 형식화되고 각 요소가 $separator로 구분된 문자열을 반환해요. 리스트에 중첩 하위 리스트가 포함되면, fmt는 각 요소를 형식화하기 전에 그것들을 평탄화해요. 따라서 fmt[1, 2, [3, 4]]를 3개가 아닌 4개의 요소를 가진 리스트로 취급해요.

형식 문자열에 대한 자세한 내용은 sprintf를 참고하세요.

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

method from

리스트가 Match 객체들을 포함한다고 가정하고, 리스트의 첫 번째 요소에 .from을 호출한 값을 반환해요.

'abcdefg' ~~ /(c)(d)/;
say $/.list.from;         # OUTPUT: «2␤»

"abc123def" ~~ m:g/\d/;
say $/.list.from;         # OUTPUT: «3␤»

method to

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

ListMatch들을 포함한다고 가정해요. 정규식에서 :g 수정자를 쓸 때 $/ 변수가 List인 경우처럼요. 리스트의 마지막 요소.to를 호출한 값을 반환해요.

method sink

method sink(--> Nil) { }

정의에서 명확히 보이듯, 아무것도 하지 않고 Nil을 반환해요.

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

method Set

일반적으로 리스트의 요소들을 멤버로 가진 집합(set) 을 만들어요.

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

다만 리스트에 비스칼라 데이터 구조가 포함되면 예상치 못한 변경이 있을 수 있어요. 예를 들어 Pair의 경우:

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

집합은 값이 0이 아닌 Pair의 key들로 구성되어, 모든 값을 제거해요. 더 많은 예시와 자세한 설명은 Set 문서를 확인하세요.

Operators

infix cmp

multi infix:<cmp>(List @a, List @b)

요소 @a[$i](Int $i는 0부터)와 @b[$i]를 비교해, 값이 어떻게 다르냐에 따라 Order::Less, Order::Same, Order::More를 반환함으로써 List를 평가해요. 연산이 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␤»