Any — Raku 클래스 계층의 기본 베이스 클래스

Any — Raku 클래스 계층의 기본 베이스 클래스

새 클래스를 만들 때 별달리 지정하지 않으면 상속받는 기본 클래스가 있어요. 바로 Any예요. Mu가 Raku 클래스 계층의 뿌리라면, Any는 새 클래스의 기본 베이스 클래스이자 대부분의 내장 클래스의 베이스 클래스로 쓰여요.

class Any is Mu {}

Raku는 항목(item)과 단일 요소 리스트를 의도적으로 혼동하기 때문에, Any의 대부분 메서드는 List에도 존재하고 List 또는 리스트류 타입으로 강제돼요.

본문

AnyMu처럼 모든 것의 부모는 아니에요. Mu가 순수한 뿌리라면 Any는 "값이 정의될 수 있는(defined)" 기본 성질을 담당해요. 아래에 Any에 정의된 많은 메서드들을 볼게요.

메서드

method ACCEPTS

multi method ACCEPTS(Any:D: Mu $other)

$other === self(객체 정체성 검사)이면 True를 돌려줘요. 많은 내장 타입이 더 구체적인 비교를 위해 이 메서드를 오버라이드해요.

EXPR.ACCEPTS(EXPR);

routine any

method any(        --> Junction:D)
multi  any(+values --> Junction:D)
multi  any(@values --> Junction:D)

invocant 또는 인자를 리스트로 해석해 any-Junction을 만들어요.

say so 2 == <1 2 3>.any;        # OUTPUT: «True␤»
say so 5 == any(<1 2 3>);       # OUTPUT: «False␤»

routine all

method all(        --> Junction:D)
multi  all(+values --> Junction:D)
multi  all(@values --> Junction:D)

invocant 또는 인자를 리스트로 해석해 all-Junction을 만들어요.

say so 3 < <2 3 4>.all;         # OUTPUT: «False␤»
say so 1 < all(<2 3 4>);        # OUTPUT: «True␤»

routine one

method one(        --> Junction:D)
multi  one(+values --> Junction:D)
multi  one(@values --> Junction:D)

invocant 또는 인자를 리스트로 해석해 one-Junction을 만들어요.

say so 1 == (1, 2, 3).one;      # OUTPUT: «True␤»
say so 1 == one(1, 2, 1);       # OUTPUT: «False␤»

routine none

method none(        --> Junction:D)
multi  none(+values --> Junction:D)
multi  none(@values --> Junction:D)

invocant 또는 인자를 리스트로 해석해 none-Junction을 만들어요.

say so 1 == (1, 2, 3).none;     # OUTPUT: «False␤»
say so 4 == none(1, 2, 3);      # OUTPUT: «True␤»

method list

multi method list(Any:U:)
multi method list(Any:D \SELF:)

invocant에 infix , 연산자를 적용해 결과 List를 돌려줘요.

say 42.list.^name;           # OUTPUT: «List␤»
say 42.list.elems;           # OUTPUT: «1␤»

Any의 서브클래스는 .list에서 Positional 역할을 하는 핵심 타입을 돌려줄 수 있어요. 특별히 List로 강제하려면 .List를 쓰세요.

@는 리스트/Positional 문맥화기로도 쓸 수 있어요.

my $not-a-list-yet = $[1,2,3];
say $not-a-list-yet.raku;             # OUTPUT: «$[1, 2, 3]␤»
my @maybe-a-list = @$not-a-list-yet;
say @maybe-a-list.^name;              # OUTPUT: «Array␤»

첫 경우 리스트는 itemized 돼요. 접두어 @.list를 호출해 초기 스칼라를 리스트 문맥에 넣고 Array로 바꿔요.

method push

multi method push(Any:U \SELF: |values --> Positional:D)

push는 정의되지 않은 invocant에 대해 정의되어, 그 값이 이미 Positional을 구현하지 않았다면 자동생성(autovivify)해 빈 Array로 만들어요. 그런 다음 주어진 인자를 새로 만든 Array에 push해요.

my %h;
say %h<a>;     # OUTPUT: «(Any)␤»      <-- Undefined
%h<a>.push(1); # .push on Any
say %h;        # OUTPUT: «{a => [1]}␤» <-- Note the Array

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

method sort

multi method sort()
multi method sort(&custom-routine-to-use)

cmp 또는 주어진 코드 객체로 iterable을 정렬하고 새 Seq를 돌려줘요. 선택적으로 정렬 방법을 지정하는 Callable을 위치 인자로 받아요.

say <b c a>.sort;                           # OUTPUT: «(a b c)␤»
say 'bca'.comb.sort.join;                   # OUTPUT: «abc␤»
say 'bca'.comb.sort({$^b cmp $^a}).join;    # OUTPUT: «cba␤»
say '231'.comb.sort(&infix:«<=>»).join;     # OUTPUT: «123␤»

sub by-character-count { $^a.chars <=> $^b.chars }
say <Let us impart what we have seen tonight unto young Hamlet>.sort(&by-character-count);
# OUTPUT: «(us we Let what have seen unto young impart Hamlet tonight)␤»

routine map

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

map은 invocant를 반복하며 호출마다 코드 객체의 위치 인자 수만큼 invocant에서 가져와 적용해요. 코드 객체의 반환 값들이 반환되는 Seq의 요소가 돼요.

method 형태에서 사용 가능한 추가 인자 :$label:$item은 내부적으로만 유용해요. for 루프가 map으로 변환되기 때문이에요. :$label.map 루프에 붙일 기존 Label을, :$item은 반복이 (SELF,) 위에서 일어날지(:$item 설정 시) SELF 위에서 일어날지 제어해요.

sub 형태에서는 valuescode 블록을 적용하며, 그 values가 invocant 역할을 해요.

|c, Iterable:D \\iterable, Hash:D \\hash 시그니처 형태는 X::Cannot::Map으로 실패해요. 흔한 함정을 잡아내기 위한 거예요.

sink된 for 문 안에서 map이 만든 Seq도 sink돼요:

say gather for 1 {
    ^3 .map: *.take;
} # OUTPUT: «(0 1 2)␤»

이 경우 gatherfor 문을 sink하고, Seq의 sink 결과는 요소들을 반복하며 .take를 호출하는 것이에요.

routine deepmap

method deepmap(&block --> Iterable) is nodal
sub    deepmap(&op, \\obj --> Iterable)

deepmap은 인자(또는 sub 형태의 첫 인자)를 각 요소에 적용하고, 요소가 Iterable 역할을 하지 않으면 반환 값들로 새 Iterable을 만들어요. Iterable 요소는 deepmap이 하위 리스트로 재귀적으로 내려가요.

say [[1,2,3],[[4,5],6,7]].deepmap(* + 1);
# OUTPUT: «[[2 3 4] [[5 6] 7 8]]␤»
say deepmap * + 1, [[1,2,3],[[4,5],6,7]];
# OUTPUT: «[[2 3 4] [[5 6] 7 8]]␤»

Associative의 경우 값에 적용돼요:

{ what => "is", this => "thing", a => <real list> }.deepmap( *.flip ).say;
# OUTPUT: «{a => (laer tsil), this => gniht, what => si}␤»
say deepmap *.flip, {what=>'is', this=>'thing', a=><real list>};
# OUTPUT: «{a => (laer tsil), this => gniht, what => si}␤»

routine duckmap

method duckmap(&block --> Iterable) is rw is nodal
sub    duckmap(&op, \\obj --> Iterable)

duckmap은 인자(또는 sub 형태의 첫 인자)를 적용할 수 있을 만큼 행동하는 각 요소에 그 인자를 적용해요. 실패하면 가능하면 재귀적으로 내려가고, 아니면 변환 없이 항목을 돌려줘요. 객체가 Associative면 값에 동작해요.

<a b c d e f g>.duckmap(-> $_ where <c d e>.any { .uc }).say;
# OUTPUT: «(a b C D E f g)␤»
(('d', 'e'), 'f').duckmap(-> $_ where <e f>.any { .uc }).say;
# OUTPUT: «((d E) F)␤»
{ first => ('d', 'e'), second => 'f'}.duckmap(-> $_ where <e f>.any { .uc }).say;
# OUTPUT: «{first => (d E), second => F}␤»

첫 경우에는 블록({ .uc })을 적용할 조건을 만족하는 c, d, e에 적용되고 나머지는 그대로 돌려줘요.

두 번째 경우에는 첫 항목이 리스트라 조건을 만족하지 않으니 방문되고, 그 평탄한 리스트가 첫 경우처럼 동작해요. 세 번째 경우도 마찬가지로, 각 쌍의 값만 방문돼요.

say duckmap *², [[1,2,3],[[4,5],6,7]];   # OUTPUT: «[9 9]␤»

숫자처럼 행동하는 한 뭐든 제곱할 수 있어요. 이 경우 각각 3개 요소를 가진 두 배열이 있고, 이 배열들은 숫자 3으로 변환되어 제곱돼요. 하지만 다음 경우에는:

say duckmap -> Rat $_ { $_²}, [[1,2,3],[[4,5],6.1,7.2]];
# OUTPUT: «[[1 2 3] [[4 5] 37.21 51.84]]␤»

3개 항목 리스트는 Rat이 아니므로 재귀적으로 내려가지만, 결국 Rat과 일치하는 것에만 연산을 적용해요.

표면(이름)상 duckmapdeepmap과 비슷해 보일 수 있지만, 후자는 항목의 타입과 무관하게 재귀적으로 적용돼요.

routine nodemap

method nodemap(&block --> Iterable) is nodal
sub    nodemap(&op, \\obj --> Iterable)

nodemap은 인자(또는 sub 형태의 첫 인자)를 각 요소에 적용하고, 그 Callable 인자의 반환 값들로 새 Iterable을 만들어요. deepmap과 달리 Iterable 역할을 하는 요소를 찾아도 하위 리스트로 재귀적으로 내려가지 않아요.

say nodemap *+1, [[1,2,3], [[4,5],6,7], 7];
# OUTPUT: «(4, 4, 8)␤»

say [[2, 3], [4, [5, 6]]]».nodemap(*+1)
# OUTPUT: «((3 4) (5 3))␤»

위 예시들은 map을 써도 정확히 같은 결과를 내요. 차이는 mapSlip을 평탄화하는 반면 nodemap은 그렇지 않다는 점이에요.

say [[2,3], [[4,5],6,7], 7].nodemap({.elems == 1 ?? $_ !! slip});
# OUTPUT: «(() () 7)␤»
say [[2,3], [[4,5],6,7], 7].map({.elems == 1 ?? $_ !! slip});
# OUTPUT: «(7)␤»

Associative에 적용하면 값에 동작해요:

say nodemap *.flip, { what => "is", this => "thing" };
# OUTPUT: «{this => gniht, what => si}␤»

method flat

method flat() is nodal

invocant를 리스트로 해석하고, 컨테이너화되지 않은 Iterable들을 평탄한 리스트로 평탄화해서 돌려줘요. MapHash 타입은 Iterable이라서 Pair 리스트로 평탄화되는 점을 기억하세요.

say ((1, 2), (3), %(:42a));      # OUTPUT: «((1 2) 3 {a => 42})␤»
say ((1, 2), (3), %(:42a)).flat; # OUTPUT: «(1 2 3 a => 42)␤»

Array는 기본적으로 요소를 컨테이너화하므로 flat이 평탄화하지 않아요. 하이퍼 메서드 호출로 내부 Iterable들에 .List를 호출해 de-containerize하면 flat이 평탄화할 수 있어요.

say [[1, 2, 3], [(4, 5), 6, 7]]      .flat; # OUTPUT: «([1 2 3] [(4 5) 6 7])␤»
say [[1, 2, 3], [(4, 5), 6, 7]]».List.flat; # OUTPUT: «(1 2 3 4 5 6 7)␤»

더 세밀한 옵션은 deepmap, duckmap, 시그니처 구조 분해을 보세요.

method eager

method eager() is nodal

invocant를 List로 해석해 즉시(eagerly) 평가하고 그 List를 돌려줘요.

my  $range = 1..5;
say $range;         # OUTPUT: «1..5␤»
say $range.eager;   # OUTPUT: «(1 2 3 4 5)␤»

routine elems

multi method elems(Any:U: --> 1)
multi method elems(Any:D:)
multi        elems($a)
multi        elems(array:D \a)

invocant나 인자를 리스트로 해석하고 리스트의 요소 수를 돌려줘요.

say elems 42;                   # OUTPUT: «1␤»
say <a b c>.elems;              # OUTPUT: «3␤»
say Whatever.elems ;            # OUTPUT: «1␤»

클래스에 대해서도 1을 돌려줘요.

routine end

multi method end(Any:U: --> 0)
multi method end(Any:D:)
multi        end($a)
multi        end(array:D \a)
multi        end($, *%)

invocant나 인자를 리스트로 해석하고 그 리스트의 마지막 인덱스를 돌려줘요.

say 6.end;                      # OUTPUT: «0␤»
say <a b c>.end;                # OUTPUT: «2␤»
say end ^9;                     # OUTPUT: «8␤»

method pairup

multi method pairup(Any:U:)
multi method pairup(Any:D:)

invocant가 타입 객체이면 빈 Seq를 돌려줘요.

Range.pairup.say; # OUTPUT: «()␤»

invocant를 리스트로 해석해, Hash에 할당할 때와 같은 방식으로 Pair 리스트를 만들어요. 즉 두 개의 연속 요소를 가져다 쌍을 만들되, 키 위치의 항목이 이미 Pair이면 그 Pair를 통과시키고 다음 항목이 다시 키로 간주돼요. Pair들의 Seq를 돌려줘요.

say (a => 1, 'b', 'c').pairup.raku;     # OUTPUT: «(:a(1), :b("c")).Seq␤»

method Array

method Array(--> Array:D) is nodal

invocant를 Array로 강제해요.

method List

method List(--> List:D) is nodal

list 메서드를 사용해 invocant를 List로 강제해요.

method serial

multi method serial()

이 메서드는 Rakudo 특유이며 Raku 스펙에는 포함되지 않아요. 인스턴스 자신에 대한 self-reference를 돌려줘요:

my $b;                 # defaults to Any
say $b.serial.^name;   # OUTPUT: «Any␤»
say $b.^name;          # OUTPUT: «Any␤»
my $breakfast = 'food';
$breakfast.serial.say; # OUTPUT: «food␤»

세 번째 예시에서 볼 수 있듯 이건 겉보기엔 no-op이에요. 하지만 HyperSeqRaceSeq에서는 직렬화된 Seq를 돌려주므로, hyper/race 메서드의 반대로 볼 수 있어요. 즉 자동 스레딩(autothreading) 모드가 아니라 직렬 리스트 처리 모드에 있음을 보장해요.

method Hash

multi method Hash( --> Hash:D)

invocant를 Hash로 강제해요.

method hash

multi method hash(Any:U:)
multi method hash(Any:D:)

타입 객체에서 호출하면 빈 Hash를 돌려줘요. 인스턴스에서는 invocant를 %-시길 변수에 할당하고 그걸 돌려주는 것과 동등해요. Any의 서브클래스는 .hash에서 Associative 역할을 하는 핵심 타입을 돌려줄 수 있어요. 특별히 Hash로 강제하려면 .Hash를 쓰세요.

my $d; # $d is Any
say $d.hash; # OUTPUT: {}

my %m is Map = a => 42, b => 666;
say %m.hash;  # OUTPUT: «Map.new((a => 42, b => 666))␤»
say %m.Hash;  # OUTPUT: «{a => 42, b => 666}␤»

method Slip

method Slip(--> Slip:D) is nodal

invocant를 Slip으로 강제해요.

method Map

method Map(--> Map:D) is nodal

invocant를 Map으로 강제해요.

method Seq

method Seq() is nodal

invocant를 Seq로 강제해요.

method Bag

method Bag(--> Bag:D) is nodal

invocant를 Bag으로 강제하는데, Positional은 값 리스트로 취급돼요.

method BagHash

method BagHash(--> BagHash:D) is nodal

invocant를 BagHash으로 강제해요. Positional은 값 리스트로 취급돼요.

method Set

method Set(--> Set:D) is nodal

invocant를 Set으로 강제해요. Positional은 값 리스트로 취급돼요.

method SetHash

method SetHash(--> SetHash:D) is nodal

invocant를 SetHash으로 강제해요. Positional은 값 리스트로 취급돼요.

method Mix

method Mix(--> Mix:D) is nodal

invocant를 Mix으로 강제해요. Positional은 값 리스트로 취급돼요.

method MixHash

method MixHash(--> MixHash:D) is nodal

invocant를 MixHash으로 강제해요. Positional은 값 리스트로 취급돼요.

method Supply

method Supply(--> Supply:D) is nodal

먼저 .list 메서드로 invocant를 list로 강제한 뒤 Supply로 강제해요.

routine min

multi method min(&by?, :$k, :$v, :$kv, :$p )
multi        min(+args, :&by, :$k, :$v, :$kv, :$p)

invocant를 Iterable로 강제하고 cmp 의미론으로 가장 작은 요소를 돌려줘요. MapHash의 경우 가장 낮은 값을 가진 Pair를 돌려줘요.

method 형태에는 Callable 위치 인자를 줄 수 있어요. 그 Callable이 단일 인자를 받으면, 비교 전에 정렬할 값들을 변환하는 데 쓰여요. min이 돌려주는 건 여전히 원래 값이에요.

Callable이 두 인자를 받으면 cmp 대신 비교자로 쓰여요.

sub 형태에서는 invocant가 인자로 전달되고, 모든 Callable:by named 인자로 지정해야 해요.

say (1,7,3).min();              # OUTPUT: «1␤»
say (1,7,3).min({1/$_});        # OUTPUT: «7␤»
say min(1,7,3);                 # OUTPUT: «1␤»
say min(1,7,3,:by( { 1/$_ } )); # OUTPUT: «7␤»
min( %(a => 3, b=> 7 ) ).say ;  # OUTPUT: «a => 3␤»

Rakudo 컴파일러 2023.08 릴리스부터 추가 named 인자를 지정해 최저 값과 관련된 모든 정보를 얻을 수 있어요. 이 중 어떤 named 인자를 지정해도 반환 값은 항상 List예요.

  • :k — 발견된 최저 값들의 인덱스 List.
  • :v — 발견된 최저 값들의 실제 값 List. Map/Hash에서는 Pair들.
  • :kv — 인덱스와 값을 교차한 List.
  • :p — 키가 인덱스, 값이 실제 최저 값(Map/Hash에서는 Pair)인 Pair 리스트.
say <a b c a>.min(:k);  # OUTPUT:«(0 3)␤»
say <a b c a>.min(:v);  # OUTPUT:«(a a)␤»
say <a b c a>.min(:kv); # OUTPUT:«(0 a 3 a)␤»
say <a b c a>.min(:p);  # OUTPUT:«(0 => a 3 => a)␤»

routine max

multi method max(&by?, :$k, :$v, :$kv, :$p )
multi        max(+args, :&by, :$k, :$v, :$kv, :$p)

max 메서드/루틴의 인터페이스는 min과 같아요. 다만 최저 값 대신 최고 값을 돌려줘요.

say (1,7,3).max();                # OUTPUT: «7␤»
say (1,7,3).max({1/$_});          # OUTPUT: «1␤»
say max(1,7,3,:by( { 1/$_ } ));   # OUTPUT: «1␤»
say max(1,7,3);                   # OUTPUT: «7␤»
max( %(a => 'B', b=> 'C' ) ).say; # OUTPUT: «b => C␤»

Rakudo 컴파일러 2023.08 릴리스부터:

say <a b c c>.max(:k);  # OUTPUT:«(2 3)␤»
say <a b c c>.max(:v);  # OUTPUT:«(c c)␤»
say <a b c c>.max(:kv); # OUTPUT:«(2 c 3 c)␤»
say <a b c c>.max(:p);  # OUTPUT:«(2 => c 3 => c)␤»

routine minmax

multi method minmax()
multi method minmax(&by)
multi        minmax(+args, :&by!)
multi        minmax(+args)

가장 작은 요소부터 가장 큰 요소까지의 Range를 돌려줘요. Callable 위치 인자를 주면 각 값을 필터에 통과시키고 그 반환 값을 원래 값 대신 비교해요. 반환되는 Range에는 여전히 원래 값이 쓰여요.

sub 형태에서는 invocant가 인자로 전달되고 비교 Callable:by named 인자로 지정할 수 있어요.

say (1,7,3).minmax();        # OUTPUT:«1..7␤»
say (1,7,3).minmax({-$_});   # OUTPUT:«7..1␤»
say minmax(1,7,3);           # OUTPUT: «1..7␤»
say minmax(1,7,3,:by( -* )); # OUTPUT: «7..1␤»

method minpairs

multi method minpairs(Any:D:)

.pairs를 호출하고, cmp 연산자로 판단해 최소 값을 가진 모든 Pair의 Seq를 돌려줘요.

<a b c a b c>.minpairs.raku.put; # OUTPUT: «(0 => "a", 3 => "a").Seq␤»
%(:42a, :75b).minpairs.raku.put; # OUTPUT: «(:a(42),).Seq␤»

method maxpairs

multi method maxpairs(Any:D:)

.pairs를 호출하고, cmp 연산자로 판단해 최대 값을 가진 모든 Pair의 Seq를 돌려줘요.

<a b c a b c>.maxpairs.raku.put; # OUTPUT: «(2 => "c", 5 => "c").Seq␤»
%(:42a, :75b).maxpairs.raku.put; # OUTPUT: «(:b(75),).Seq␤»

method keys

multi method keys(Any:U: --> List)
multi method keys(Any:D: --> List)

정의된 Any에 대해 list를 호출한 뒤 그 keys를 돌려주고, 아니면 list를 호출해 돌려줘요.

my $setty = Set(<Þor Oðin Freija>);
say $setty.keys; # OUTPUT: «(Þor Oðin Freija)␤»

List.keys도 참고하세요. 클래스에 같은 시도를 하면 대부분 진짜 키가 없으므로 빈 리스트가 돌아와요.

method flatmap

method flatmap(&block, :$label)

.map(&block).flat와 유사한 편의 메서드예요.

method roll

multi method roll(--> Any)
multi method roll($n --> Seq)

.list 메서드로 invocant를 list로 강제하고 List.roll을 적용해요.

my Mix $m = ("þ" xx 3, "ð" xx 4, "ß" xx 5).Mix;
say $m.roll;    # OUTPUT: «ð␤»
say $m.roll(5); # OUTPUT: «(ß ß þ ß þ)␤»

$m은 리스트로 변환된 뒤 (이 경우 가중치 있는) 주사위를 굴려요. 자세한 건 List.roll을 보세요.

method iterator

multi method iterator(Any:)

객체를 리스트로 변환한 뒤 iterator로 돌려줘요. for 문에서 호출되는 함수예요.

. say for 3; # OUTPUT: «3␤»

Any의 iterable 서브클래스는 이 메서드의 자체 구현을 제공해야 하지만, 다른 클래스는 이걸 상속받아 필요한 문맥에서 단일 항목 리스트 iterator를 제공할 수 있어요.

method pick

multi method pick(--> Any)
multi method pick($n --> Seq)

.list 메서드로 invocant를 List로 강제하고 List.pick을 적용해요.

my Range $rg = 'α'..'ω';
say $rg.pick(3); # OUTPUT: «(β α σ)␤»

routine skip

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

1항목 리스트의 iterator로 Seq를 만들고 Seq.skip을 적용해요. 실제 사용 사례는 그 문서를 참고하세요. 인자 없이 skip을 부르는 건 skip(1)과 같아요.

multi skip(\\skipper, +values)

Rakudo 컴파일러 2022.07 릴리스부터 skip의 "sub" 버전도 있어요. 반드시 skip 지정자를 첫 인자로 가져야 해요. 나머지 인자는 Seq로 바뀐 뒤 skip 메서드가 호출돼요.

method are

multi method are(Any:)
multi method are(Any: Any $type)

인자 없는 버전은 Rakudo 컴파일러 2022.02 릴리스부터, 타입 인자 버전은 6.e 언어 버전(초기 구현은 Rakudo 2024.05+)에 있어요.

인자 없이 부르면 리스트의 모든 요소가 스마트매치할 가장 엄격한 타입(또는 역할)을 돌려줘요. 빈 리스트에서는 Nil을 돌려줘요.

say (1,2,3).are;        # OUTPUT: «(Int)␤»
say <a b c>.are;        # OUTPUT: «(Str)␤»
say <42 666>.are;       # OUTPUT: «(IntStr)␤»
say (42,666e0).are;     # OUTPUT: «(Real)␤»
say (42,i).are;         # OUTPUT: «(Numeric)␤»
say ("a",42,3.14).are;  # OUTPUT: «(Cool)␤»
say ().are;             # OUTPUT: «Nil␤»

스칼라 값은 단일 요소 리스트로 해석돼요.

say 42.are;             # OUTPUT: «(Int)␤»
say Int.are;            # OUTPUT: «(Int)␤»

해시는 Pair 리스트로 해석되므로 항상 Pair 타입 객체를 만들어요. 해시 키나 값의 가장 엄격한 타입을 얻으려면 .keys·.values 메서드를 쓰세요.

my %h = a => 42, b => "bar";
say %h.keys.are;        # OUTPUT: «(Str)␤»
say %h.values.are;      # OUTPUT: «(Cool)␤»

타입 인자와 함께 부르면 invocant의 모든 타입이 주어진 타입과 스마트매치하는지 확인해요. 그러면 True를 돌려주고, 어느 하나라도 실패하면 Failure를 돌려줘요.

say (1,2,3).are(Int);         # OUTPUT: «True␤»
say <a b c>.are(Str);         # OUTPUT: «True␤»
say <42 666>.are(Int);        # OUTPUT: «True␤»
say <42 666>.are(Str);        # OUTPUT: «True␤»
say (42,666e0).are(Real);     # OUTPUT: «True␤»
say (42,i).are(Numeric);      # OUTPUT: «True␤»
say ("a",42,3.14).are(Cool);  # OUTPUT: «True␤»
say ().are(Int);              # OUTPUT: «True␤»

Int.are(Str);      # OUTPUT: «Expected 'Str' but got 'Int'␤»
(1,2,3).are(Str);  # OUTPUT: «Expected 'Str' but got 'Int' in element 0␤»

method prepend

multi method prepend(Any:U: --> Array)
multi method prepend(Any:U: @values --> Array)

빈 변수에 인자 없이 호출하면 빈 Array로 초기화해요. 인자와 함께 부르면 배열을 만들고 Array.prepend을 적용해요.

my $a;
say $a.prepend; # OUTPUT: «[]␤»
say $a;         # OUTPUT: «[]␤»
my $b;
say $b.prepend(1,2,3); # OUTPUT: «[1 2 3]␤»

method unshift

multi method unshift(Any:U: --> Array)
multi method unshift(Any:U: @values --> Array)

Any 변수를 빈 Array로 초기화하고 Array.unshift을 호출해요.

my $a;
say $a.unshift; # OUTPUT: «[]␤»
say $a;         # OUTPUT: «[]␤»
my $b;
say $b.unshift([1,2,3]); # OUTPUT: «[[1 2 3]]␤»

routine first

multi method first(Bool:D $t)
multi method first(Regex:D $test, :$end, *%a)
multi method first(Callable:D $test, :$end, *%a is copy)
multi method first(Mu $test, :$end, *%a)
multi method first(:$end, *%a)
multi        first(Bool:D $t, |)
multi        first(Mu $test, +values, *%a)

일반적으로 .list 메서드로 invocant를 list로 강제하고 List.first을 적용해요.

다만 이건 시그니처가 다른 multi이며 (약간) 다른 동작으로 구현돼요. 서브루틴으로 쓰는 건 두 번째 인자를 객체로 하는 메서드로 쓰는 것과 동등해요.

먼저, Bool을 인자로 쓰면 항상 Failure를 돌려줘요. $test를 쓰는 형태는 그와 스마트매치하는 첫 요소를 돌려주는데, :end를 쓰면 끝에서부터 시작해요.

say (3..33).first;           # OUTPUT: «3␤»
say (3..33).first(:end);     # OUTPUT: «33␤»
say (⅓,⅔…30).first( 0xF );   # OUTPUT: «15␤»
say first 0xF, (⅓,⅔…30);     # OUTPUT: «15␤»
say (3..33).first( /\d\d/ ); # OUTPUT: «10␤»

세 번째와 네 번째 예시는 Mu $test 형태로 스마트매치하며 그걸 하는 첫 요소를 돌려줘요. 마지막 예시는 두 자리 수를 위한 정규식을 테스트로 쓰므로, 그 기준을 만족하는 첫 값은 10이에요. 이 마지막 형태는 Callable multi를 사용해요:

say (⅓,⅔…30).first( * %% 11, :end, :kv ); # OUTPUT: «(65 22)␤»

또한 first 검색은 :end에서 시작하며 키/값 세트를 리스트로 돌려줘요. 여기서 Seq에서 그 위치에 해당하는 인덱스예요. 위 정의에서 %a의 일부인 :kv 인자는 first가 돌려주는 것을 수정해 키/값의 평탄한 리스트로 제공해요. 리스트류 객체에서는 키가 항상 인덱스예요.

6.d 버전부터 테스트는 Junction일 수도 있어요:

say (⅓,⅔…30).first( 3 | 33, :kv ); # OUTPUT: «(8 3)␤»

method unique

multi method unique()
multi method unique( :&as!, :&with! )
multi method unique( :&as! )
multi method unique( :&with! )

객체(또는 sub로 부르면 values)의 고유 요소 시퀀스를 만들어요.

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

:as:with 인자는 각각 같음 검사 전에 항목을 변환하는 함수와 같음을 검사하는 함수를 받아요. 기본적으로 === 연산자가 쓰여요:

("1", 1, "1 ", 2).unique( as => Int, with => &[==] ).say; # OUTPUT: «(1 2)␤»

sub 형태를 쓰는 추가 예시는 unique를 보세요.

method repeated

multi method repeated()
multi method repeated( :&as!, :&with! )
multi method repeated( :&as! )
multi method repeated( :&with! )

unique와 유사하게, :as를 정규화 함수로, :with를 같음 함수로 써서 values(루틴으로) 또는 객체에서 반복 요소를 찾아요.

<1 -1 2 -2 3>.repeated(:as(&abs),:with(&[==])).say; # OUTPUT: «(-1 -2)␤»
(3+3i, 3+2i, 2+1i).repeated(as => *.re).say;        # OUTPUT: «(3+2i)␤»

위 예시처럼 정규화 전 마지막 반복 요소를 돌려줘요. sub 형태에 대한 더 많은 예시는 repeated을 보세요.

method squish

multi method squish( :&as!, :&with = &[===] )
multi method squish( :&with = &[===] )

.repeated와 유사하게, :as 함수로 정규화한 뒤 :with 인자(기본 ===)를 같음 연산자로 사용해 연속된 동일 요소 시퀀스의 첫 요소들의 시퀀스를 돌려줘요.

"aabbccddaa".comb.squish.say;             # OUTPUT: «(a b c d a)␤»
"aABbccdDaa".comb.squish( :as(&lc) ).say; # OUTPUT: «(a B c d a)␤»
(3+2i,3+3i,4+0i).squish( as => *.re, with => &[==]).put; # OUTPUT: «3+2i 4+0i␤»

마지막 예시처럼 시퀀스는 단일 요소를 담을 수 있어요. 추가 sub 예시는 squish을 보세요.

method permutations

method permutations(|c)

.list 메서드로 invocant를 list로 강제하고 List.permutations을 적용해요.

say <a b c>.permutations;
# OUTPUT: «((a b c) (a c b) (b a c) (b c a) (c a b) (c b a))␤»
say set(1,2).permutations;
# OUTPUT: «((2 => True 1 => True) (1 => True 2 => True))␤»

단일 요소 또는 요소가 없는 데이터 구조의 순열은 빈 리스트 또는 단일 요소 리스트를 담은 리스트를 돌려줘요.

say 1.permutations; # OUTPUT: «((1))␤»

method join

method join($separator = '') is nodal

self.list를 호출해 객체를 리스트로 바꾸고 리스트에 .join을 호출해요. 구분자를 받을 수 있고 기본은 빈 문자열이에요.

(1..3).join.say;       # OUTPUT: «123␤»
<a b c>.join("❧").put; # OUTPUT: «a❧b❧c␤»

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 )

첫 형태는 항상 실패해요. 두 번째 형태는 주어진 객체의 정체성으로 분류하는데, 보통 :&as 인자와 함께 쓸 때만 의미가 있어요.

가장 단순한 형태에서는 키로 쓰일 결과를 만드는 $test를 사용해요. 그 키의 값은 테스트 결과로 그 키를 만든 요소들의 배열이에요.

say (1..13).categorize( * %% 3);
say categorize( * %% 3, 1..13)
# OUTPUT: «{False => [1 2 4 5 7 8 10 11 13], True => [3 6 9 12]}␤»

:as 인자는 분류 전에 정규화해요:

say categorize( * %% 3, -5..5, as => &abs )
# OUTPUT: «{False => [5 4 2 1 1 2 4 5], True => [3 0 3]}␤»

$into associative 인자로 새 Hash를 반환하는 대신 결과를 그 안에 넣을 수 있어요.

my %leap-years;
my @years = (2002..2009).map( { Date.new( $_~"-01-01" ) } );
@years.categorize( *.is-leap-year , into => %leap-years );
say %leap-years
# OUTPUT:
# «{ False
# => [2002-01-01 2003-01-01 2005-01-01 2006-01-01 2007-01-01 2009-01-01],
#    True => [2004-01-01 2008-01-01]}␤»

분류에 쓰이는 함수는 그 인자를 넣을 수 있는 모든 정의역(bin)을 나타내는 배열을 반환할 수 있어요.

sub divisible-by( Int $n --> Array(Seq) ) {
    gather {
        for <2 3 5 7> {
            take $_ if $n %% $_;
        }
    }
}

say (3..13).categorize( &divisible-by );
# OUTPUT:
# «{2 => [4 6 8 10 12], 3 => [3 6 9 12], 5 => [5 10], 7 => [7]}␤»

이 경우 범위의 각 숫자는 나눠질 수 있는 만큼 많은 bin으로 분류돼요. 테스트로 Whatever를 쓰는 지원은 Rakudo 컴파일러 2023.02 릴리스에 추가됐어요.

routine classify

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

첫 형태는 항상 실패해요. 두 번째 형태는 주어진 객체의 정체성으로 분류하며 보통 :&as 인자와 함께 쓸 때 의미가 있어요.

나머지 형태는 $test 인자를 포함하는데, 각 입력에 대해 스칼라를 반환하는 함수예요. 그 스칼라들이 해시의 키로 쓰이고, 값은 테스트 함수에서 그 키를 출력하는 요소들의 배열이에요.

my @years = (2003..2008).map( { Date.new( $_~"-01-01" ) } );
@years.classify( *.is-leap-year , into => my %leap-years );
say %leap-years;
# OUTPUT: «{False => [2003-01-01 2005-01-01 2006-01-01 2007-01-01],
#           True => [2004-01-01 2008-01-01]}␤»

.categorize와 유사하게, 요소는 :as 인자로 전달된 Callable로 정규화될 수 있고, 결과가 분류될 Hash를 넘기는 :into named 인자를 쓸 수 있어요. 위 예시에서는 그 자리에서 정의됐어요.

6.d 버전부터 .classifyJunction에서도 동작해요. 테스트로 Whatever를 쓰는 지원은 Rakudo 컴파일러 2023.02 릴리스에 추가됐어요.

routine reduce

multi method reduce(Any:U: & --> Nil)
multi method reduce(Any:D: &with)
multi        reduce (&with, +list)

이 루틴은 이항 서브루틴을 적용해 리스트류 객체의 요소들을 결합해 단일 결과를 만들어요. 인자(또는 sub 형태의 첫 인자)를 연산자로 객체(또는 sub 형태의 두 번째 인자)의 모든 요소에 적용해 단일 결과를 만들어요. 서브루틴은 infix 연산자이거나 두 개의 위치 인자를 받아야 해요. infix 연산자를 쓸 때는 서브루틴 버전의 코드 객체, 즉 연산자 카테고리 뒤에 콜론, 그다음 연산자를 이루는 기호의 리스트 따옴표 구조로 제공해야 해요(예: infix:<+>). Operators 문서 참고.

say (1..4).reduce(&infix:<+>);   # OUTPUT: «10␤»
say reduce &infix:<+>, 1..4;     # OUTPUT: «10␤»
say reduce &min, 1..4;           # OUTPUT: «1␤»

sub hyphenate(Str \a, Str \b) { a ~ '-' ~ b }
say reduce &hyphenate, 'a'..'c'; # OUTPUT: «a-b-c␤»

클래스에 적용하면 항상 Nil을 돌려줘요.

say Range.reduce(&infix:<+>);    # OUTPUT: «Nil␤»
say Str.reduce(&infix:<~>);      # OUTPUT: «Nil␤»

더 자세한 논의는 List.reduce를 보세요.

routine produce

multi method produce(Any:U: & --> Nil)
multi method produce(Any:D: &with)
multi        produce (&with, +list)

reduce와 유사하지만, 단일 결과 대신 누적된 값들의 리스트를 돌려줘요.

<10 5 3>.reduce( &[*] ).say ; # OUTPUT: «150␤»
<10 5 3>.produce( &[*] ).say; # OUTPUT: «(10 50 150)␤»

produced 리스트의 마지막 요소는 .reduce 메서드가 내는 출력이 돼요. 클래스라면 그냥 Nil을 돌려줘요.

method pairs

multi method pairs(Any:U:)
multi method pairs(Any:D:)

invocant가 타입 객체이면 빈 List를 돌려줘요.

say Num.pairs; # OUTPUT: «()␤»

값 객체라면 list 메서드로 List로 변환하고 그에 대한 List.pairs 결과를 돌려줘요.

<1 2 2 3 3 3>.Bag.pairs.say;# OUTPUT: «(1 => 1 3 => 3 2 => 2)␤»

이 경우 bag의 각 요소(가중치 포함)가 pair로 변환돼요.

method antipairs

multi method antipairs(Any:U:)
multi method antipairs(Any:D:)

invocant가 타입 객체이면 빈 List를 돌려줘요.

Range.antipairs.say; # OUTPUT: «()␤»

값 객체라면 pair 리스트로 변환한 뒤 반전시킨 pair 리스트를 돌려줘요. 값이 키가 되고 그 반대도 마찬가지예요.

%(s => 1, t=> 2, u => 3).antipairs.say ;# OUTPUT: «(2 => t 1 => s 3 => u)␤»

method invert

multi method invert(Any:U:)
multi method invert(Any:D:)

타입 객체에 적용하면 빈 리스트를, 객체에 적용하면 리스트로 변환한 뒤 List.invert을 적용해 모든 Pair에서 키와 값을 맞바꿔요. 결과 리스트는 Pair들의 리스트여야 해요.

"aaabbcccc".comb.Bag.invert.say; # OUTPUT: «(4 => c 3 => a 2 => b)␤»

이 경우 BagPair 리스트로 변환할 수 있어요. 객체를 리스트로 변환한 결과가 pair 리스트가 아니면 실패해요.

routine kv

multi method kv(Any:U:)
multi method kv(Any:D:)
multi        kv($x)

invocant가 타입 객체이면 빈 List를 돌려줘요.

Sub.kv.say ;# OUTPUT: «()␤»

값 객체에 대해서는 invocant에 list를 호출하고 그에 대한 List.kv 결과를 키와 값이 순서·연속적으로 배열된 리스트로 돌려줘요.

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

Positional의 경우 인덱스가 로 간주돼요.

method toggle

method toggle(Any:D: *@conditions where .all ~~ Callable:D, Bool :$off  --> Seq:D)

invocant를 반복하며 Seq를 만들고, @conditionsCallable 호출 결과에 따라 받은 값이 결과로 전파될지 말지를 켜고 끄는(toggle) 방식이에요.

say (1..15).toggle(* < 5, * > 10, * < 15); # OUTPUT: «(1 2 3 4 11 12 13 14)␤»
say (1..15).toggle(:off, * > 2, * < 5, * > 10, * < 15); # OUTPUT: «(3 4 11 12 13 14)␤»

켜짐/꺼짐(True/False) 스위치를 상상해보세요. 켜져 있으면 값이 만들어져요. 기본적으로 스위치의 초기 상태는 "켜짐"이고, :$off를 참으로 설정하면 초기 상태가 "꺼짐"이 돼요.

@conditionshead에서 Callable을(가능하면) 하나 가져와 현재 테스터로 삼아요. 원본 시퀀스의 각 값은 이 테스터 Callable로 테스트해요. 가상 스위치의 상태는 테스터의 반환 값으로 정해져요. truthy하면 "켜짐", 아니면 "꺼짐"으로요.

스위치가 토글될 때(꺼짐→켜짐 또는 켜짐→꺼짐), 현재 테스터 Callable@conditions의 다음 Callable로 교체돼요(가능하면). 더 이상 테스터 Callable이 없으면 반복이 끝날 때까지 스위치는 현재 상태를 유지해요.

# our original sequence of elements:
say list ^10; # OUTPUT: «(0 1 2 3 4 5 6 7 8 9)␤»
# toggled result:
say ^10 .toggle: * < 4, * %% 2, &is-prime; # OUTPUT: «(0 1 2 3 6 7)␤»

# First tester Callable is `* < 4` and initial state of switch is "on".
# As we iterate over our original sequence:
# 0 => 0 < 4 === True  switch is on, value gets into result, switch is
#                      toggled, so we keep using the same Callable:
# 1 => 1 < 4 === True  same
# 2 => 2 < 4 === True  same
# 3 => 3 < 4 === True  same
# 4 => 4 < 4 === False switch is now off, "4" does not make it into the
#                      result. In addition, our switch got toggled, so
#                      we're switching to the next tester Callable
# 5 => 5 %% 2 === False  switch is still off, keep trying to find a value
# 6 => 6 %% 2 === True   switch is now on, take "6" into result. The switch
#                        toggled, so we'll use the next tester Callable
# 7 => is-prime(7) === True  switch is still on, take value and keep going
# 8 => is-prime(8) === False switch is now off, "8" does not make it into
#                            the result. The switch got toggled, but we
#                            don't have any more tester Callables, so it
#                            will remain off for the rest of the sequence.

스위치 상태의 토글이 다음 테스터 Callable을 불러오므로, :$offTrue로 설정하면 첫 테스터가 언제 버려질지에 영향을 줘요.

# our original sequence of elements:
say <0 1 2>; # OUTPUT: «(0 1 2)␤»
# toggled result:
say <0 1 2>.toggle: * > 1; # OUTPUT: «()␤»

# First tester Callable is `* > 1` and initial state of switch is "on".
# As we iterate over our original sequence:
# 0 => 0 > 1 === False  switch is off, "0" does not make it into result.
#                      In addition, switch got toggled, so we change the
#                      tester Callable, and since we don't have any more
#                      of them, the switch will remain "off" until the end

:off를 쓰면 동작이 달라져요:

# our original sequence of elements:
say <0 1 2>; # OUTPUT: «(0 1 2)␤»
# toggled result:
say <0 1 2>.toggle: :off, * > 1; # OUTPUT: «(2)␤»

# First tester Callable is `* > 1` and initial state of switch is "off".
# As we iterate over our original sequence:
# 0 => 0 > 1 === False  switch is off, "0" does not make it into result.
#                       The switch did NOT get toggled this time, so we
#                       keep using our current tester Callable
# 1 => 1 > 1 === False  same
# 2 => 2 > 1 === True   switch is on, "2" makes it into the result

routine head

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

객체의 첫 요소 또는, $n을 쓰면 첫 $n개를 돌려줘요.

"aaabbc".comb.head.put; # OUTPUT: «a␤»
say ^10 .head(5);           # OUTPUT: «(0 1 2 3 4)␤»
say ^∞ .head(5);            # OUTPUT: «(0 1 2 3 4)␤»
say ^10 .head;              # OUTPUT: «0␤»
say ^∞ .head;               # OUTPUT: «0␤»

Mix에는 정의된 순서가 없으므로 첫 두 경우의 결과가 달라요. 나머지 경우에는 Seq를 돌려줘요. Callable로 마지막 요소들을 제외한 모두를 돌려받을 수도 있어요.

say (^10).head( * - 3 );# OUTPUT: «(0 1 2 3 4 5 6)␤»

Rakudo 컴파일러 2022.07 릴리스부터 head의 "sub" 버전도 있어요.

multi head(\\specifier, +values)

반드시 head 지정자를 첫 인자로 가져야 해요. 나머지 인자는 Seq로 바뀐 뒤 head 메서드가 호출돼요.

routine tail

multi method tail() is raw
multi method tail($n)

객체의 마지막 또는 마지막 $n개 요소를 돌려줘요. $n은 보통 WhateverCodeCallable일 수 있는데, 객체의 처음 n개를 제외한 모두를 얻는 데 쓰여요.

say (^12).reverse.tail ;     # OUTPUT: «0␤»
say (^12).reverse.tail(3);   # OUTPUT: «(2 1 0)␤»
say (^12).reverse.tail(*-7); # OUTPUT: «(4 3 2 1 0)␤»

Rakudo 컴파일러 2022.07 릴리스부터 tail의 "sub" 버전도 있어요.

multi tail(\\specifier, +values)

반드시 tail 지정자를 첫 인자로 가져야 해요. 나머지 인자는 Seq로 바뀐 뒤 tail 메서드가 호출돼요.

method tree

multi method tree(Any:U:)
multi method tree(Any:D:)
multi method tree(Any:D: Whatever )
multi method tree(Any:D: Int(Cool) $count)
multi method tree(Any:D: @ [&first, *@rest])
multi method tree(Any:D: &first, *@rest)

정의되지 않았거나 Iterable이 아니면 클래스를 돌려주고, 아니면 invocant에 tree 메서드를 적용한 결과를 돌려줘요.

say Any.tree; # OUTPUT: «Any␤»

.treeIterable 요소에 대해 여러 프로토타입이 있어요.

my @floors = ( 'A', ('B','C', ('E','F','G')));
say @floors.tree(1).flat.elems; # OUTPUT: «6␤»
say @floors.tree(2).flat.elems; # OUTPUT: «2␤»
say @floors.tree( *.join("-"),*.join("—"),*.join("|"));# OUTPUT: «A-B—C—E|F|G␤»

숫자로 호출하면 낮은 레벨의 모든 요소에 반복적으로 tree를 적용해요. 첫 인스턴스는 배열의 각 요소에 .tree(0)을 적용하고, 다음 예시도 마찬가지예요.

두 번째 프로토타입은 인자로 전달된 WhateverCode를 각 레벨에 차례로 적용해요. 첫 인자는 레벨 1에, 다음은 레벨 2에 가는 식이에요. 따라서 tree는 복잡하고 다층적인 데이터 구조의 모든 레벨을 처리하는 좋은 방법이 될 수 있어요.

method nl-out

method nl-out(--> Str)

"\n" 값을 가진 Str을 돌려줘요. 자세한 건 IO::Handle.nl-out을 보세요.

Num.nl-out.print;     # OUTPUT: «␤»
Whatever.nl-out.print;# OUTPUT: «␤»
33.nl-out.print;      # OUTPUT: «␤»

method combinations

method combinations(|c)

.list 메서드로 invocant를 list로 강제하고 List.combinations을 적용해요.

say (^3).combinations; # OUTPUT: «(() (0) (1) (2) (0 1) (0 2) (1 2) (0 1 2))␤»

빈 데이터 구조의 조합은 단일 요소(빈 리스트) 리스트를 돌려주고, 단일 요소 구조는 두 리스트(하나는 빈 것, 다른 하나는 단일 요소)를 돌려줘요.

say set().combinations; # OUTPUT: «(())␤»

method grep

method grep(Mu $matcher, :$k, :$kv, :$p, :$v --> Seq)

.list 메서드로 invocant를 list로 강제하고 List.grep을 적용해요. 정의되지 않은 invocant의 경우 $matcher에 따라 반환 값이 ((Any)) 또는 빈 리스트가 될 수 있어요.

my $a;
say $a.grep({ True }); # OUTPUT: «((Any))␤»
say $a.grep({ $_ });   # OUTPUT: «()␤»

method append

multi method append(Any:U \SELF: |values)

인스턴스가 positional이 아니면 새 Array로 인스턴스화하고, 아니면 현재 인스턴스를 복제해요. 그 후 인자로 받은 값들을 Array.append을 호출해 그 배열에 추가해요.

my $a;
say $a.append; # OUTPUT: «[]␤»
my $b;
say $b.append((1,2,3)); # OUTPUT: «[1 2 3]␤»

method values

multi method values(Any:U:)
multi method values(Any:D:)

정의되지 않았거나 클래스 인자에는 빈 리스트를, 그 외에는 리스트로 변환된 객체를 돌려줘요.

say (1..3).values; # OUTPUT: «(1 2 3)␤»
say List.values;   # OUTPUT: «()␤»

method collate

method collate()

collate 메서드는 유니코드 그래픽(grapheme) 특성을 고려해 정렬해요. 즉 코드포인트가 나타나는 순서 대신 사람이 기대하는 대로 정렬해요. 적용되는 객체가 Iterable이라면 collate는 이렇게 동작해요.

say ('a', 'Z').sort; # (Z a)
say ('a', 'Z').collate; # (a Z)
say <ä a o ö>.collate; # (a ä o ö)
my %hash = 'aa' => 'value', 'Za' => 'second';
say %hash.collate; # (aa => value Za => second);

이 메서드는 네 개의 collation 레벨을 설정하는 $*COLLATION 변수의 영향을 받아요. Primary, Secondary, Tertiary는 스크립트마다 뜻이 다르지만, 영어에 쓰이는 라틴 스크립트에서는 대체로 Primary=Alphabetic, Secondary=Diacritics, Tertiary=Case에 대응해요.

아래 예시에서 라틴 스크립트에서 주로 대소문자인 tertiary collation과, 문자열의 코드포인트 값을 검사해 동점을 깨는 quaternary를 비활성화하면 Aa에 대해 Same이 돌아오는 걸 볼 수 있어요:

$*COLLATION.set(:quaternary(False), :tertiary(False));
say 'a' coll 'A'; # OUTPUT: «Same␤»
say ('a','A').collate == ('A','a').collate; # OUTPUT: «True␤»

이 변수는 보이는 것처럼 coll 연산자뿐 아니라 이 메서드에도 영향을 줘요.

method cache

method cache()

인스턴스에 list 메서드를 호출해 객체 자체의 List 표현을 제공해요.

method batch

multi method batch(Int:D $batch)
multi method batch(Int:D :$elems!)

.list 메서드로 invocant를 list로 강제하고 List.batch을 적용해요.

method rotor

multi method rotor(Any:D: Int:D $batch, :$partial)
multi method rotor(Any:D: *@cycle, :$partial)

객체의 요소를 $batch개씩 묶어 그룹화하는 Seq를 만들어요.

say (3..9).rotor(3); # OUTPUT: «((3 4 5) (6 7 8))␤»

:partial named 인자를 쓰면 $batch 크기가 되지 못하는 리스트도 포함돼요.

say (3..10).rotor(3, :partial); # OUTPUT: «((3 4 5) (6 7 8) (9 10))␤»

.rotor는 정수와 Pair의 배열로 호출할 수 있고, 이들이 차례로 적용돼요. 정수는 위처럼 배치 크기를 정하고, Pair는 키를 배치 크기로, 값은 양수면 건너뛸 요소 수, 음수면 겹칠 요소 수로 써요.

say (3..11).rotor(3, 2 => 1, 3 => -2, :partial);
# OUTPUT: «((3 4 5) (6 7) (9 10 11) (10 11))␤»

이 경우 첫 배치(정수가 지배)는 3개 요소, 두 번째는 2개 요소(쌍의 키)이지만 1개(8)를 건너뛰고, 세 번째는 2개 크기(부분 허용)에 2개 겹침을 가져요. 겹침은 하위 리스트 크기보다 클 수 없어요. 그러면 Exception이 나요:

say (3..11).rotor(3, 2 => 1, 3 => -4, :partial);
# OUTPUT: «(exit code 1) Rotorizing gap is out of range. Is: -4, should be in
# -3..^Inf; ␤Ensure a negative gap is not larger than the length of the
# sublist␤ ␤␤»

$batchInt-아닌 값은 Int로 강제돼요:

say (3..9).rotor(3+⅓); # OUTPUT: «((3 4 5) (6 7 8))␤»

리스트에 적용된 예시는 list.rotor도 참고하세요.

method sum

method sum() is nodal

내용이 iterable이라면 값을 하나씩 뽑아 합을 돌려주고, 리스트가 비어 있으면 0을 돌려줘요.

(3,2,1).sum; # OUTPUT: «6␤»
say 3.sum;   # OUTPUT: «3␤»

요소 중 숫자로 변환할 수 없는 게 있으면 실패해요.

multi method slice

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

Rakudo 컴파일러 2021.02 릴리스부터 사용 가능해요. invocant를 Seq로 변환한 뒤 그에 slice 메서드를 호출해요.

say (1..10).slice(0, 3..6, 8);  # OUTPUT: «(1 4 5 6 7 9)␤»

routine snip

multi        snip(\\matcher, +values)
multi method snip(\\values: \\matcher)

6.e 언어 버전부터(초기 구현은 Rakudo 2022.07+) 사용 가능해요. snip 메서드/서브루틴은 주어진 Iterable을 둘 이상의 List로 자르는 방법을 제공해요. 주어진 Iterable의 값이 smartmatch에서 False를 반환하는 즉시 "snip"이 이루어져요. matcher는 matcher 리스트일 수도 있는데, "snip"이 이루어지면 다음 matcher로 검사를 시작해요. matcher가 남아 있지 않으면 Iterable의 나머지가 만들어져요.

.say for snip * < 10, 2, 5, 13, 9, 6;      # OUTPUT: «(2 5)␤(13 9 6)␤»
.say for snip (* < 10, * < 20), 5, 13, 29; # OUTPUT: «(5)␤(13)␤(29)␤»
.say for snip Int, 2, 5, 5, "a", "b";      # OUTPUT: «(2 5 5)␤(a b)␤»
.say for (2, 5, 13, 9, 6).snip(* < 10);    # OUTPUT: «(2 5)␤(13 9 6)␤»
.say for (5, 13,29).snip(* < 10, * < 20);  # OUTPUT: «(5)␤(13)␤(29)␤»
.say for (2, 5, 5, "a", "b").snip: Int;    # OUTPUT: «(2 5 5)␤(a b)␤»

routine snitch

multi  snitch(\\snitchee)
multi  snitch(&snitcher, \\snitchee)
method snitch(\\snitchee: &snitcher = &note)

6.e 언어 버전부터(초기 구현은 Rakudo 2022.12+) 사용 가능해요. snitch 메서드/서브루틴은 어떤 invocant/인자가 주어져도 그대로 돌려주는 디버깅/로깅 도구예요. 기본적으로 invocant/인자를 note하지만, invocant/인자를 단일 인자로 받을 것으로 예상되는 Callable을 지정해 덮어쓸 수 있어요.

(my $a = 42).snitch = 666; say $a;  # OUTPUT: «42␤666␤»
(1..5).snitch;                      # OUTPUT: «1..5␤»
(1..5).Seq.snitch;                  # OUTPUT: «(1 2 3 4 5)␤»
(1..5).Seq.snitch(&dd);             # OUTPUT: «(1, 2, 3, 4, 5).Seq␤»
(1..5).map(*+1).snitch;             # OUTPUT: «(2 3 4 5 6)␤»
say (1..3).Seq.snitch.map(*+2);     # OUTPUT: «(1 2 3)␤(3 4 5)␤»

피드 연산자를 쓴 같은 예시:

(1..3).Seq ==> snitch() ==> map(*+2) ==> say();  # OUTPUT: «(1 2 3)␤(3 4 5)␤»

커스텀 로거:

my @snitched;
my @result = (1..3).Seq.snitch({ @snitched.push($_) }).map(*+2);
say @snitched;  # OUTPUT: «[(1 2 3)]␤»
say @result;    # OUTPUT: «[3 4 5]␤»

출처: Raku 공식 문서 — Any