Raku의 on-switch — 소스 핸들이 바뀔 때 콜백 실행하기
Raku의 on-switch — 소스 핸들이 바뀔 때 콜백 실행하기
IO::CatHandle는 여러 파일을 마치 하나의 스트림처럼 이어서 읽어 줄 거예요. 그런데 어떤 파일에서 어떤 파일로 넘어가는 "전환(switch)" 시점에 뭔가를 하고 싶을 때가 있죠. 그때 쓰는 것이 on-switch 콜백이에요.
has &.on-switch is rw
.new 호출 때 설정할 수 있고 나중에 할당으로 바꿀 수도 있는 속성 중 하나예요. 기본값은 지정되어 있지 않습니다. .count가 0, 1, 2, Inf 중 하나인 Callable을 받아요.
이 콜백은 .next-handle가 호출될 때마다 실행되는데, .new 호출 중에 한 번, 그리고 소스 핸들이 큐의 다음 핸들로 바뀔 때마다 실행됩니다. .next-handle 메서드를 직접 호출해서도 실행할 수 있어요.
인자 개수가 정해져 있는 게 특징이에요.
.count가0이면 인자를 받지 않아요.1이면 현재 활성 핸들을 하나 받아요.2나Inf면 현재 활성 핸들과 직전 활성 핸들을 순서대로 위치 인자로 받아요.
첫 번째 &.on-switch 실행에서는 "직전 활성 핸들" 인자가 Nil이에요. 소스 핸들 큐가 모두 소진되면 "현재 활성 핸들" 인자가 Nil이 되고, 그 이후 실행들은 두 인자 모두 Nil로 들어오죠.
(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
my $line;
my $cat = IO::CatHandle.new: :on-switch{ $line = 1 }, $f1, $f2;
say "{$cat.path}:{$line++} $_" for $cat.lines;
# OUTPUT:
# foo:1 A
# foo:2 B
# foo:3 C
# bar:1 D
# bar:2 E
:on-switch{ $line = 1 }은 부호 없는 블록이니 .count가 0이라 인자를 받지 않고, 핸들이 바뀔 때마다 $line을 1로 초기화해요. 그래서 foo 파일을 읽는 동안 1, 2, 3으로 증가하다가 bar로 넘어가는 순간 다시 1부터 시작하는 걸 볼 수 있어요.
두 인자를 받는 형태는, 새 핸들에 무언가 준비를 하고 이전 핸들을 정리하는 작업에 어울려요.
my @old-stuff;
sub on-switch ($new, $old) {
$new and $new.seek: 1, SeekFromBeginning;
$old and @old-stuff.push: $old.open.slurp: :close;
}
(my $f1 = 'foo'.IO).spurt: "A\nB\nC";
(my $f2 = 'bar'.IO).spurt: "D\nE";
my $cat = IO::CatHandle.new: :&on-switch, $f1, $f2;
$cat.lines.raku.say; # OUTPUT: «("", "B", "C", "", "E").Seq»
@old-stuff.raku.say; # OUTPUT: «["A\nB\nC", "D\nE"]»
새 핸들($new)이 있으면 읽기 위치를 1(SeekFromBeginning)로 옮기고, 이전 핸들($old)이 있으면 내용을 통째로 읽어 @old-stuff에 쌓아요. 첫 줄에서 파일 시작을 1바이트 건너뛰므로 각 파일의 첫 글자는 비어 있는 문자열로 나오고, 이전 핸들의 전체 내용이 @old-stuff에 순서대로 쌓이는 걸 확인할 수 있죠.