Perl 6 provides a new control structure -- "gather" -- that allows lists to be constructed procedurally,
without the need for a temporary variable. Within the block/closure controlled by a "gather" any call to
"take" pushes that call's argument list to an implicitly created array. "take" returns the number of
elements it took. This module implements that control structure.
At the end of the block's execution, the "gather" returns the list of values stored in the array (in a
list context) or a reference to the array (in a scalar context).
For example, instead of writing:
print do {
my @wanted;
while (my $line = <>) {
push @wanted, $line if $line =~ /\D/;
push @wanted, -$line if some_other_condition($line);
}
push @wanted, 'EOF';
join q{, }, @wanted;
};
instead we can write:
print join q{, }, gather {
while (my $line = <>) {
take $line if $line =~ /\D/;
take -$line if some_other_condition($line);
}
take 'EOF';
}
and instead of:
my $text = do {
my $string;
while (<>) {
next if /^#|^\s*$/;
last if /^__[DATA|END]__\n$/;
$string .= $_;
}
$string;
};
we could write:
my $text = join q{}, gather {
while (<>) {
next if /^#|^\s*$/;
last if /^__[DATA|END]__\n$/;
take $_;
}
};
There is also a third function -- "gathered" -- which returns a reference to the implicit array being
gathered. This is useful for handling defaults:
my @odds = gather {
for @data {
take $_ if $_ % 2;
take to_num($_) if /[one|three|five|nine]$/;
}
take (1,3,5,7,9) unless gathered;
}
Note that -- as the example above implies -- the "gathered" function returns a special Perl 5 array
reference that acts like a Perl 6 array reference in boolean, numeric, and string contexts.
It's also handy for creating the implicit array by some process more complex than by simple sequential
pushing. For example, if we needed to prepend a count of non-numeric items:
my @odds = gather {
for @data {
take $_ if $_ %2;
take to_num($_) if /[one|three|five|seven|nine]$/;
}
unshift gathered, +grep(/[a-z]/i, @data);
}
Conceptually "gather"/"take" is the generalized form from which both "map" and "grep" derive. That is, we
could implement those two functions as:
sub map (&@) {
my $coderef = shift;
my @list = @{shift @_};
return gather {
take $coderef->($_) for (@list)
};
}
sub grep (&@) {
my $coderef = shift;
my @list = @{shift @_};
return gather {
do { take $_ if $coderef->($_) } for @list
};
}
A "gather" is also a very handy way of short-circuiting the construction of a list. For example, suppose
we wanted to generate a single sorted list of lines from two sorted files, but only up to the first line
they have in common. We could gather the lines like this:
my @merged_diff = gather {
my $a = <$fh_a>;
my $b = <$fh_b>;
while (1) {
if ( defined $a && defined $b ) {
if ($a eq $b) { last } # Duplicate means end of list
elsif ($a lt $b) { take $a; $a = <$fh_a>; }
else { take $b; $b = <$fh_b>; }
}
elsif (defined $a) { take $a; $a = <$fh_a>; }
elsif (defined $b) { take $b; $b = <$fh_b>; }
else { last }
}
}
If you like it really short, you can also "gather"/"take" $_ magically:
my @numbers_with_two = gather {
for (1..20) {
take if /2/
} }; # @numbers_with_two contains 2, 12, 20
Be aware that $_ in Perl5 is a global variable rather than the current topic like in Perl6.