1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
SYNOPSIS
use Syntax::Keyword::Gather;
my @list = gather {
# Try to extract odd numbers and odd number names...
for (@data) {
if (/(one|three|five|seven|nine)$/) { take qq{'$_'} }
elsif (/^\d+$/ && $_ %2) { take $_ }
}
# But use the default set if there aren't any of either...
take @defaults unless gathered;
}
or to use the stuff that Sub::Exporter gives us, try
# this is a silly idea
use syntax gather => {
gather => { -as => 'bake' },
take => { -as => 'cake' },
};
my @vals = bake { cake (1...10) };
DESCRIPTION
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.
HISTORY
This module was forked from Damian Conway's Perl6::Gather for a few
reasons.
to avoid the slightly incendiary name
to avoid the use of the Perl6::Exporter
~ doesn't overload to mean string context
BUGS AND IRRITATIONS
It would be nice to be able to code the default case as:
my @odds = gather {
for (@data) {
take if $_ % 2;
take to_num($_) if /(?:one|three|five|nine)\z/;
}
} or (1,3,5,7,9);
but Perl 5's or imposes a scalar context on its left argument. This is
arguably a bug and definitely an irritation.
|