Array — 요소를 자유롭게 바꿀 수 있는 리스트

Array — 요소를 자유롭게 바꿀 수 있는 리스트

여러 값을 순서대로 담아두고 나중에 원소를 교체하고 싶을 때가 많아요. Raku에서 그 기본 타입이 Array예요. List처럼 순서 있는 컬렉션이지만, 모든 요소를 스칼라 컨테이너로 강제해서 배열 요소에 할당이 가능해요.

class Array is List {}

ArrayPositional을 구현하므로 첨자(subscripts)를 지원해요.

참고: 6.d 버전부터 .raku(2019.11 릴리스 이전에는 .perl)를 다차원 배열에서 호출할 수 있어요.

본문

특정 타입의 Array를 선언하려면 여러 방법이 있어요.

my @foo of Int = 33,44;        # [33 44]
my @bar is Array[Int] = 33,44; # [33 44]

두 번째 예시는 타입을 매개변수화하는 것으로, Rakudo 2019.03부터 사용 가능해요.

메서드

method gist

List.gist와 정확히 같지만, 둘러싸는 구분자로 대괄호를 써요.

method pop

method pop(Array:D:) is nodal

배열의 마지막 항목을 제거하고 돌려줘요. 배열이 비어 있으면 실패해요.

많은 Array 메서드처럼 pop도 대응하는 서브루틴으로 호출할 수 있어요.

my @foo = <a b>; # a b
@foo.pop;        # b
pop @foo;        # a
pop @foo;
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::Cannot::Empty: Cannot pop from an empty Array␤»

method push

multi method push(Array:D: **@values is raw --> Array:D)
multi method push(Array:D: \value --> Array:D)
multi method push(Array:D: Slip \values --> Array:D)

배열 끝에 값(들)을 추가하고 수정된 배열을 돌려줘요. 인자가 Slip이면 그 인자의 iterator가 만들어내는 값들을 추가해요. invocant 배열이나 Sliplazy하면 예외를 던져요.

my @foo = <a b c>;
@foo.push: 'd';
say @foo;                   # OUTPUT: «[a b c d]␤»

push는 인자 리스트를 평탄화하지 않아요. 배열이나 리스트를 하나의 값으로 넘기면 그게 한 요소가 돼요.

my @a = <a b c>;
my @b = <d e f>;
@a.push: @b;
say @a.elems;               # OUTPUT: «4␤»
say @a[3].join;             # OUTPUT: «def␤»

여러 값을 추가하려면 별도 인자로 주거나 Slip에 담아야 해요.

my @a = '1';
say @a.push: 'a', 'b';       # OUTPUT: «[1 a b]␤»
my @c = <E F>;
say @a.push: @c.Slip;        # OUTPUT: «[1 a b E F]␤»

단일 비슬립(non-slipping) Iterable이 만드는 여러 값을 추가하려면 method append를 보세요.

method append

multi method append(Array:D: **@values is raw --> Array:D)
multi method append(Array:D: \arg --> Array:D)

배열 끝에 값을 추가하고 수정된 배열을 돌려줘요. invocant 배열이나 평탄화가 필요한 인자가 lazy하면 예외를 던져요.

method push와 달리 append단일 인자 규칙을 따르는데, 대략 이렇게 생각하면 돼요:

multi method append(Array:D: +values --> Array:D)

단일 인자로 itemized되지 않은 Iterable을 넘기면 append가 그걸 평탄화하려고 해요.

my @a = <a b c>;
my @b = <d e f>;
@a.append: @b;
say @a.elems;               # OUTPUT: «6␤»
say @a;                     # OUTPUT: «[a b c d e f]␤»

method elems

method elems(Array:D: --> Int:D)

invocant의 요소 수를 돌려줘요. invocant가 lazy하면 X::Cannot::Lazy 예외를 던져요. shaped 배열이면 바깥 차원을 돌려주고, 모든 차원 정보가 필요하면 shape을 보세요.

say [<foo bar ber>] .elems; # OUTPUT: «3␤»
say (my @a[42;3;70]).elems; # OUTPUT: «42␤»

try [-∞...∞].elems;
say $!.^name;               # OUTPUT: «X::Cannot::Lazy␤»

method clone

method clone(Array:D: --> Array:D)

원본 Array를 복제해요. 복제본의 요소를 수정해도 원본에 전파되지 않고 그 반대도 마찬가지예요:

my @a = <a b c>; my @b = @a.clone;
@b[1] = 42; @a.push: 72;
say @b; # OUTPUT: «[a 42 c]␤»
say @a; # OUTPUT: «[a b c 72]␤»

다만 reifier(실체화기)는 두 Array가 공유해요. 그래서 각 요소가 실체화 시 무작위 생성되더라도 두 배열은 같은 요소를 갖고, 실체화가 복제본이든 원본이든 각 요소는 정확히 한 번만 실체화돼요. 여러 스레드에서 Array를 동시에 실체화하는 게 안전하지 않듯, 한 스레드에서 복제본을 실체화하면서 다른 스레드에서 원본을 실체화하는 것도 안전하지 않아요.

my @a = 1, {rand} … ∞; my @b = @a.clone;
say @b[^3]; # OUTPUT: «(1 0.0216426755282736 0.567660896142156)␤»
say @a[^3]; # OUTPUT: «(1 0.0216426755282736 0.567660896142156)␤»

method flat

multi method flat(Array:U:)
multi method flat(Array:D:)

타입 객체에 적용하면 타입 객체 자신을 돌려주고, 객체에 적용하면 Array의 기본 iterator로 만든 Seq를 돌려줘요.

my @a = <a 2 c>;
say @a.flat.^name; # OUTPUT: «Seq␤»

method shift

method shift(Array:D:) is nodal

배열의 첫 번째 항목을 제거하고 돌려줘요. 배열이 비어 있으면 실패해요.

my @foo = <a b>;
say @foo.shift;             # OUTPUT: «a␤»
say @foo.shift;             # OUTPUT: «b␤»
say @foo.shift;
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::Cannot::Empty: Cannot shift from an empty Array␤»

routine unshift

multi        unshift(Array:D, **@values --> Array:D)
multi method unshift(Array:D: **@values --> Array:D)

배열 시작에 @values를 추가하고 수정된 배열을 돌려줘요. @values가 lazy 리스트면 실패해요.

my @foo = <a b c>;
@foo.unshift: 1, 3 ... 11;
say @foo;                   # OUTPUT: «[(1 3 5 7 9 11) a b c]␤»

배열에 추가되는 요소 수에 대한 method push 문서의 주의가 그대로 적용돼요. 한 리스트/배열에서 여러 요소를 추가하는 버전은 routine prepend이에요.

routine prepend

sub prepend(\array, |values)
multi method prepend(Array:D: \values)
multi method prepend(Array:D: **@values is raw)

values의 요소를 배열 앞에 추가해 제자리에서 수정해요.

my @foo = <a b c>;
@foo.prepend: 1, 3 ... 11;
say @foo;                   # OUTPUT: «[1 3 5 7 9 11 a b c]␤»

unshift와의 차이는, 단일 배열/리스트 인자를 prepend하면 그 배열/리스트를 평탄화하지만, unshift는 그 리스트/배열을 그냥 한 요소로 앞에 붙인다는 점이에요.

routine splice

multi        splice(@list,   $start = 0, $elems?, *@replacement --> Array)
multi method splice(Array:D: $start = 0, $elems?, *@replacement --> Array)

Array에서 인덱스 $start부터 $elems개 요소를 삭제하고, 그 요소들을 돌려주면서 @replacement로 교체해요. $elems를 생략하거나 $start부터 남은 요소 수보다 크면 $start부터 끝까지의 모든 요소가 삭제돼요. $start$elems를 둘 다 생략하면 모든 요소가 삭제되고 돌아와요.

$start$elems 각각은 Whatever 또는 Int 호환 값을 반환하는 Callable로 지정할 수 있어요. 그 반환 값이 해당 인자로 쓰여요.

Whatever $start@list(또는 invocant)의 요소 수를, Callable $start@list(또는 self)의 요소 수 하나를 인자로 받아 호출돼요.

Whatever $elems$start부터 @list(또는 self) 끝까지 삭제해요($elems 없음과 동일). Callable $elems@list(또는 self)의 요소 수에서 $start 값을 뺀 값을 인자로 받아 호출돼요.

my @foo = <a b c d e f g>;
say @foo.splice(2, 3, <M N O P>);        # OUTPUT: «[c d e]␤»
say @foo;                                # OUTPUT: «[a b M N O P f g]␤»

6.d 버전부터 그냥 현재 크기보다 더 많은 요소를 끼워 넣어 배열을 확장할 수도 있어요.

my @foo = <a b c d e f g>;
say @foo.splice(6, 4, <M N O P>);       # OUTPUT: «[g]␤»
say @foo;                               # OUTPUT: «[a b c d e f M N O P]␤»

다음 등가 관계가 성립해요(@a.elems ≥ $i라고 가정):

@a.push($x, $y)      @a.splice: *  , *, $x, $y
@a.pop               @a.splice: *-1,
@a.shift             @a.splice: 0  , 1,
@a.unshift($x, $y)   @a.splice: 0  , 0, $x, $y
@a[$i] = $y          @a.splice: $i , 1, $y,

위에서 말했듯 $start$elemsWhateverCallable을 줄 수 있어요. 예를 들어 배열이 둘째 마지막 요소를 가질 만큼 충분히 크다면 그걸 제거하려고 둘 중 하나를 쓸 수 있어요:

my @foo = <a b c d e f g>;
say @foo.splice: *-2, *-1;           # OUTPUT: «[f]␤»
say @foo;                            # OUTPUT: «[a b c d e g]␤»

my &start     = -> $n { $n - 2 };
my &elems-num = -> $m { $m - 1 };
say @foo.splice: &start, &elems-num; # OUTPUT: «[e]␤»
say @foo;                            # OUTPUT: «[a b c d g]␤»

method shape

method shape() { (*,) }

배열의 모양(shape)을 리스트로 돌려줘요.

my @foo[2;3] = ( < 1 2 3 >, < 4 5 6 > ); # Array with fixed dimensions
say @foo.shape;                          # OUTPUT: «(2 3)␤»
my @bar = ( < 1 2 3 >, < 4 5 6 > );      # Normal array (of arrays)
say @bar.shape;                          # OUTPUT: «(*)␤»

method default

method default

invocant의 기본값을 돌려줘요. 즉 Array에서 아직 초기화되지 않은 요소에 접근하거나 Nil로 명시적으로 설정된 요소에 접근할 때 반환되는 값이에요. is default 트레이트로 기본값이 선언되지 않은 ArrayAny의 타입 객체를 돌려줘요.

my @a1 = 1, "two", 2.718;
say @a1.default;                               # OUTPUT: «(Any)␤»
say @a1[4];                                    # OUTPUT: «(Any)␤»

my @a2 is default(17) = 1, "two", 3;
say @a2.default;                               # OUTPUT: «17␤»
say @a2[4];                                    # OUTPUT: «17␤»
@a2[1] = Nil;                                  # (resets element to its default)
say @a2[1];                                    # OUTPUT: «17␤»

method of

method of()

invocant 값의 타입 제약을 돌려줘요. 기본적으로, 즉 선언 시 타입 제약이 없으면 (Mu)를 돌려줘요.

my @a1 = 1, 'two', 3.14159;              # (no type constraint specified)
say @a1.of;                              # OUTPUT: «(Mu)␤»

my Int @a2 = 1, 2, 3;                    # (values must be of type Int)
say @a2.of;                              # OUTPUT: «(Int)␤»
@a2.push: 'd';
CATCH { default { put .^name, ': ', .Str } };
# OUTPUT: «X::TypeCheck::Assignment: Type check failed in assignment to @a2; expected Int but got Str ("d")␤»

method dynamic

method dynamic(Array:D: --> Bool:D)

invocant가 is dynamic 트레이트로 선언되었으면 True를 돌려줘요. 즉 그 자리에는 선언되지 않았어도 내부 렉시컬 범위에서 접근할 수 있는 동적 변수인지 나타내요.

my @a;
say @a.dynamic;                          # OUTPUT: «False␤»

my @b is dynamic;
say @b.dynamic;                          # OUTPUT: «True␤»

* twigil로 변수를 선언하면 is dynamic이 암시돼요.

my @*b;
say @*b.dynamic;                         # OUTPUT: «True␤»

dynamic 트레이트는 변수의 속성이고 내용의 속성이 아니에요. Scalar 동적 변수가 배열을 담고 있으면 이 컨테이너의 규칙이 적용되고, 항상 False를 돌려줘요.

method List

multi method List(Array:D:)

배열을 List로 변환해요.

my @array= [1];
@array[3]=3;
say @array.List;       # OUTPUT: «(1 Nil Nil 3)␤»

구멍(hole)은 Nil로 나타나요.

method Slip

multi method Slip(Array:D: --> Slip:D)

배열을 Slip으로 변환하는데, 구멍은 Array가 정의된 타입 값으로 채워요.

my Int @array= [0];
@array[3]=3;
say @array.Slip; # OUTPUT: «(0 (Int) (Int) 3)␤»

출처: Raku 공식 문서 — Array