duckmap
duckmap (중첩 구조 순회 매핑)
리스트 안에 리스트가 들어 있는 중첩 구조를 다룰 때, 안쪽 요소까지 원하는 대로 바꾸고 싶은 경우가 있어요. duckmap은 요소에 함수를 적용할 수 있으면 적용하고, 안 되면 재귀적으로 안으로 들어가서 처리하는 루틴이에요.
본문
Any에서
method duckmap(&block --> Iterable) is rw is nodal
sub duckmap(&op, \\obj --> Iterable)
duckmap은 인자(또는 서브 형태라면 첫 번째 인자)를 적용할 수 있는 성질을 가진 각 요소에 그 인자를 적용해요. 적용에 실패하면 할 수 있을 때 재귀적으로 내려가고, 아니면 변환 없이 그 항목을 그대로 돌려줘요. 객체가 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과 일치하는 것에만 연산을 적용해요.
겉으로 보기에(이름도 그렇고) duckmap은 deepmap과 비슷해 보일 수 있는데, deepmap은 항목의 타입과 상관없이 재귀적으로 적용된다는 차이가 있어요.