Array — 요소를 자유롭게 바꿀 수 있는 리스트
Array — 요소를 자유롭게 바꿀 수 있는 리스트
여러 값을 순서대로 담아두고 나중에 원소를 교체하고 싶을 때가 많아요. Raku에서 그 기본 타입이 Array예요. List처럼 순서 있는 컬렉션이지만, 모든 요소를 스칼라 컨테이너로 강제해서 배열 요소에 할당이 가능해요.
class Array is List {}
Array는 Positional을 구현하므로 첨자(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 배열이나 Slip이 lazy하면 예외를 던져요.
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와 $elems에 Whatever나 Callable을 줄 수 있어요. 예를 들어 배열이 둘째 마지막 요소를 가질 만큼 충분히 크다면 그걸 제거하려고 둘 중 하나를 쓸 수 있어요:
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 트레이트로 기본값이 선언되지 않은 Array는 Any의 타입 객체를 돌려줘요.
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)»