sub transduce ($stream, |transducers ) {
my ($stream-iterator, $input);
# Step through specified transducers:
gather transducer: for transducers -> $transducer {
# Start pulling from $stream.
# Code written here inside the `for transducers` iteration for right scoping of the `last` in &pull-one.
# Next line is called once when `transduce` sub execution begins, BEFORE `for transducers` loop begins.
FIRST { $stream-iterator = $stream .iterator; $input = pull-one }
# `last` out of the `for transducers` iteration above if stream's IterationEnd reached:
sub pull-one { my $element := $stream-iterator .pull-one; last if $element =:= IterationEnd; $element }
my role Keep {}
# Extract or construct transducer (consumer => producer pair) given each transducer's rule type.
# This transducer `given`/`when` must be in sync with the consumer & producer ones further down.
my ($consumer, $producer) = do given $transducer { # eg transducer rule: does this transduction:
when Array[Code] { .[0], Keep } # `[* %% 2]` Keeps while number is even
when [] { Keep, Keep } # `[]` Keeps entire rest of stream
when Pair { .key[0], .value[0] } # `[] => Nil` Skips entire rest of stream
# `10 => Nil` Skips 10 elements of stream
# `[*.Int] => [++*]` Adds 1 to input while Int
default { .[0], Keep } # `* %% 2` Keeps until number not even
# `10` Keeps 10 elements of stream
}
# Step through stream:
loop {
# All consumers except Keep (`[]`) have a test. If test fails, time for next transducer:
given $consumer {
when Code { next transducer unless .($input) }
when Int { next transducer unless $consumer-- }
}
# A Keep producer always outputs (takes) the input (aka keep).
# A `Nil` producer always outputs nothing (aka skip).
# A code producer that returns an undefined value when run outputs nothing (aka conditional skip).
# A code producer that returns a defined value outputs that value.
# Any other producer value outputs the producer as the value.
given $producer {
when Keep { take $input } # If no explicit producer (or `[]` CONSUMER) then Keep
when Any:U { } # `=> Nil` producer means take nothing (aka skip it)
when Code { .($input) andthen .take if *.defined } # $producer($input) is taken unless result is Empty
default { .take } # If none of the above, take $producer as is as value
}
$input = pull-one
}
}
}
say transduce ^20, 3 => 'strings', [* < 6] => Nil, [* < 10] => [++*], [* %% 5] => 'divisible by 5', [] => -1;
castle
cattle
acetal
acetol
ctttld
aaaaab
aaaabb
aabbbb
abbbbb