Capture — 시그니처로 넘길 수 있는 인자 묶음
Capture — 시그니처로 넘길 수 있는 인자 묶음
함수를 호출할 때 넘기는 인자들, 즉 "호출부에서 건네는 값 묶음"을 하나의 객체로 다루고 싶을 때가 있어요. 그 역할을 하는 게 바로 Capture예요. 인자를 받는 쪽(시그니처)과 짝을 이루는 개념입니다.
본문
class Capture { }
Capture는 코드 객체에 인자를 넘기기 위한 컨테이너예요. Signature(시그니처)의 반대편 개념이라고 할 수 있어요. 즉 capture는 호출자가 정한 인자들이고, 시그니처는 피호출자가 정한 매개변수들이죠. 예를 들어 print $a, $b라고 호출하면 $a, $b 부분이 capture입니다.
캡처는 위치 인자를 위한 리스트 같은 부분과, 이름 인자를 위한 해시 같은 부분을 담고 있어서 Positional과 Associative처럼 동작하지만, 실제로 그 두 롤을 mixin 하지는 않아요. 다른 자료구조처럼 독립된 캡처를 만들어 저장하고 나중에 쓸 수 있습니다.
리터럴 Capture는 어떤 항(term) 앞에 백슬래시 \를 붙여 만들 수 있어요. 보통 그 항은 List인데, 여기서 key => value 형태나 :key<value> 형태의 Pair 리터럴은 이름 부분으로 들어가고, 나머지 항들은 모두 위치 부분으로 들어갑니다('key' => value 형태의 Pair도 위치 부분으로 들어가요).
my $a = \(42); # 위치 인자 하나를 가진 Capture
my $b = \(1, 2, verbose => True); # 위치 인자 둘과 이름 인자 하나를 가진 Capture
my $c = \(1, 2, :verbose(True)); # 위와 같아요
my $c = \(1, 2, 'verbose' => True); # 위치 인자 셋을 가진 Capture
다시 강조하자면, 캡처의 이름 인자는 다음 두 가지 방식 중 하나로만 만들어야 해요.
- 매개변수를 가리키는 따옴표 없는 키 다음에
=>, 그 다음에 값을 쓰는 방식. 예:by => {1/$_}. - 매개변수 이름을 딴 콜론-페어 리터럴을 쓰는 방식. 예:
:into(my %leap-years).
예를 들어:
sub greet(:$name, :$age) {
"$name, $age"
}
my $d = \(name => 'Mugen', age => 19); # OK
my $e = \(:name('Jin'), :age(20)); # OK
my $f = \('name' => 'Fuu', 'age' => 15); # 안 됨, 키에 따옴표가 붙어 있어요
name과 age라는 두 이름 인자를 받는 greet 서브루틴에서, $d와 $e 캡처는 잘 동작하지만 $f 캡처는 Too many positionals passed... 오류를 던져요. 'age' => 20은 (앞서 말한 두 방식 중 하나로 만든) 이름 인자가 아니라, greet이 전혀 기대하지 않는 위치 인자이기 때문이에요. 캡처 맥락에서는 따옴표가 붙은 키가 이름 인자를 만들지 않습니다. 'key' => value는 그냥 또 하나의 위치 매개변수일 뿐이라, 이름 인자로 캡처를 만들 때는 주의해야 해요.
캡처를 만들고 나면, 서브루틴 호출에서 세로 막대 |를 앞에 붙여 사용할 수 있어요. 그러면 캡처 안의 값들을 마치 직접 인자로 넘긴 것처럼 동작해요 — 이름 인자는 이름 인자로, 위치 인자는 위치 인자로 전달됩니다. 캡처는 다른 서브루틴에서든 몇 번이든 재사용할 수 있어요.
say greet |$d; # OUTPUT: «Mugen, 19»
say greet |$e; # OUTPUT: «Jin, 20»
my $x = \(4, 2, 3, -2);
say reverse |$x; # OUTPUT: «(-2 3 2 4)»
say sort 5, |$x; # OUTPUT: «(-2 2 3 4 5)»
say unique |$x, as => {.abs}; # OUTPUT: «(4 2 3)»
say unique |$x, :as({.abs}); # OUTPUT: «(4 2 3)»
my $y = \(1, 7, 3, by => {1/$_});
say min |$y; # OUTPUT: «7», min 1, 7, 3, by => {1/$_}과 같아요
say max |$y; # OUTPUT: «1», max 1, 7, 3, by => {1/$_}과 같아요
Signature 안에서는 시길리스(시길 없는) 매개변수 앞에 세로 막대 |를 붙여 Capture를 만들 수 있어요. 그러면 인자 목록의 나머지를 그 캡처 매개변수로 모아 줍니다.
sub f($a, |c) {
say $a;
say c;
say c.^name;
say c.list; # Methods 섹션 참고
say c.hash; # Methods 섹션 참고
}
f 1, 2, 3, a => 4, :b(5);
# OUTPUT:
# 1
# \(2, 3, :a(4), :b(5))
# Capture
# (2 3)
# Map.new((a => 4, b => 5))
Capture는 List처럼 컨테이너를 담을 수도 있다는 점을 기억하세요. 리터럴 값만 담는 게 아니에요.
my $b = 1;
my $c = \(4, 2, $b, 3);
say min |$c; # OUTPUT: «1»
$b = -5;
say min |$c; # OUTPUT: «-5»
메서드
method list
method list(Capture:D:)
Capture의 위치 부분을 반환해요.
my Capture $c = \(2, 3, 5, apples => (red => 2));
say $c.list; # OUTPUT: «(2 3 5)»
method hash
method hash(Capture:D:)
Capture의 이름/해시 부분을 반환해요.
my Capture $c = \(2, 3, 5, apples => (red => 2));
say $c.hash; # OUTPUT: «Map.new((:apples(:red(2))))»
method elems
method elems(Capture:D: --> Int:D)
Capture 안의 위치 요소 개수를 반환해요.
my Capture $c = \(2, 3, 5, apples => (red => 2));
say $c.elems; # OUTPUT: «3»
method keys
multi method keys(Capture:D: --> Seq:D)
모든 위치 키 다음에 모든 이름 키를 담은 Seq를 반환해요. 위치 인자의 키는 0부터 시작하는 각 인자의 서수 위치입니다.
my $capture = \(2, 3, 5, apples => (red => 2));
say $capture.keys; # OUTPUT: «(0 1 2 apples)»
method values
multi method values(Capture:D: --> Seq:D)
모든 위치 값 다음에 모든 이름 인자 값을 담은 Seq를 반환해요.
my $capture = \(2, 3, 5, apples => (red => 2));
say $capture.values; # OUTPUT: «(2 3 5 red => 2)»
method kv
multi method kv(Capture:D: --> Seq:D)
keys와 values가 번갈아 나오는 Seq를 반환해요. 있으면 위치 키와 값이 먼저 나오고, 그다음 이름 키와 값이 나옵니다.
my $capture = \(2, 3, apples => (red => 2));
say $capture.kv; # OUTPUT: «(0 2 1 3 apples red => 2)»
method pairs
multi method pairs(Capture:D: --> Seq:D)
모든 인자를, 위치 인자 먼저 이름 인자 나중에, Pair의 Seq로 반환해요. 위치 인자는 0부터 시작하는 서수 값을 키로 갖고, 이름 인자는 자기 이름을 키로 가집니다.
my Capture $c = \(2, 3, apples => (red => 2));
say $c.pairs; # OUTPUT: «(0 => 2 1 => 3 apples => red => 2)»
method antipairs
multi method antipairs(Capture:D: --> Seq:D)
모든 인자를, 위치 인자 먼저 이름 인자 나중에, 키와 값을 서로 바꾼 Pair의 Seq로 반환해요. 즉 값이 키가 되고 키가 값이 됩니다. pairs 메서드와 반대 동작이에요.
my $capture = \(2, 3, apples => (red => 2));
say $capture.antipairs; # OUTPUT: «(2 => 0 3 => 1 (red => 2) => apples)»
method Bool
method Bool(Capture:D: --> Bool:D)
Capture가 이름 인자나 위치 인자를 하나라도 담고 있으면 True를 반환해요.
say \(1,2,3, apples => 2).Bool; # OUTPUT: «True»
say \().Bool; # OUTPUT: «False»
method Capture
method Capture(Capture:D: --> Capture:D)
자기 자신, 즉 invocant를 반환해요.
say \(1,2,3, apples => 2).Capture; # OUTPUT: «\(1, 2, 3, :apples(2))»
method Numeric
method Numeric(Capture:D: --> Int:D)
Capture 안의 위치 요소 개수를 반환해요.
say \(1,2,3, apples => 2).Numeric; # OUTPUT: «3»