NAME
Perl6::Gather - Implements the Perl 6 'gather/take' control structure in Perl 5
SYNOPSIS
@list
= gather {
# Try to extract odd numbers and odd number names...
for
(
@data
) {
if
(/(one|three|five|nine)$/) { take
qq{'$_'}
; }
elsif
(/^\d+$/ &&
$_
%2) { take; }
}
# But use the default set if there aren't any of either...
take
@defaults
unless
gathered;
}
BACKGROUND
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.
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:
# Perl 6 code...
do
{
my
@wanted
;
for
<> ->
$line
{
push
@wanted
,
$line
if
$line
~~ /\D/;
push
@wanted
, -
$line
if
some_other_condition(
$line
);
}
push
@wanted
,
'EOF'
;
@wanted
;
};
in Perl 6 we can write:
# Perl 6 code...
gather {
for
<> ->
$line
{
take
$line
if
$line
~~ /\D/;
take -
$line
if
some_other_condition(
$line
);
}
take
'EOF'
;
}
and instead of:
$text
=
do
{
my
$string
;
for
<> {
next
if
/^
#|^\s*$/;
last
if
/^__[DATA|END]__\n$/;
$string
.=
$_
;
}
$string
;
};
we could write:
$text
= ~gather {
for
<> {
next
if
/^
#|^\s*$/;
last
if
/^__[DATA|END]__\n$/;
take;
}
}
As the above example implies, if take
is called without any arguments, it takes the current topic.
There is also a third function -- gathered
-- which returns a reference to the implicit array being gathered. This is useful for handling defaults:
@odds
= gather {
for
@data
{
take
if
$_
% 2;
take to_num(
$_
)
if
/[one|three|five|nine]$/;
}
take 1,3,5,7,9
unless
gathered;
}
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:
@odds
= gather {
for
@data
{
take
if
$_
%2;
take to_num(
$_
)
if
/[one|three|five|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
(
$transform
is Code, *
@list
) {
return
gather {
for
@list
{ take
$transform
(
$_
) } };
}
sub
grep
(
$selected
is Code|Rule, *
@list
) {
return
gather {
for
@list
{ take
when
$selected
} }
}
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
>;
loop {
if
defined
all
$a
,
$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
}
}
}
DESCRIPTION
The Perl6::Gather module provides the same functionality in Perl 5. So we could code some of the previous examples like so:
# Perl 5 code...
use
Perl6::Gather;
gather {
for
my
$line
(<>) {
take
$line
if
$line
=~ /\D/;
take -
$line
if
some_other_condition(
$line
);
}
take
'EOF'
;
};
and:
# Perl 5 code...
$
" = "
";
$text
= ~gather {
for
(<>) {
next
if
/^
#|^\s*$/;
last
if
/^__(?:DATA|END)__$/;
take;
}
};
and:
# Perl 5 code...
use
Perl6::Gather;
@odds
= gather {
for
(
@data
) {
take
if
$_
% 2;
take to_num(
$_
)
if
/(?:one|three|five|nine)\z/;
}
take 1,3,5,7,9
unless
gathered;
};
Note that -- as the second 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. Note too that that array reference has the ~
operator overloaded to provide string coercion (as in Perl 6).
WARNING
The syntax and semantics of Perl 6 gather
's is still being finalized and consequently is at any time subject to change. The the syntax and semantics of this module will track those changes when and if they occur.
AUTHOR
Damian Conway (damian@conway.org)
DEPENDENCIES
Perl6::Export
BUGS AND IRRITATIONS
It would be nice to be able to code the default case as:
@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.
Comments, suggestions, and patches welcome.
COPYRIGHT
Copyright (c) 2003, Damian Conway. All Rights Reserved.
This module is free software. It may be used, redistributed
and/or modified under the same terms as Perl itself.