1Syntax::Keyword::GatherU(s3e)r Contributed Perl DocumentaStyinotnax::Keyword::Gather(3)
2
3
4
6 Syntax::Keyword::Gather - Implements the Perl 6 'gather/take' control
7 structure in Perl 5
8
10 version 1.003002
11
13 use Syntax::Keyword::Gather;
14
15 my @list = gather {
16 # Try to extract odd numbers and odd number names...
17 for (@data) {
18 if (/(one|three|five|seven|nine)$/) { take qq{'$_'} }
19 elsif (/^\d+$/ && $_ %2) { take $_ }
20 }
21 # But use the default set if there aren't any of either...
22 take @defaults unless gathered;
23 }
24
25 or to use the stuff that Sub::Exporter gives us, try
26
27 # this is a silly idea
28 use syntax gather => {
29 gather => { -as => 'bake' },
30 take => { -as => 'cake' },
31 };
32
33 my @vals = bake { cake (1...10) };
34
36 Perl 6 provides a new control structure -- "gather" -- that allows
37 lists to be constructed procedurally, without the need for a temporary
38 variable. Within the block/closure controlled by a "gather" any call to
39 "take" pushes that call's argument list to an implicitly created array.
40 "take" returns the number of elements it took. This module implements
41 that control structure.
42
43 At the end of the block's execution, the "gather" returns the list of
44 values stored in the array (in a list context) or a reference to the
45 array (in a scalar context).
46
47 For example, instead of writing:
48
49 print do {
50 my @wanted;
51 while (my $line = <>) {
52 push @wanted, $line if $line =~ /\D/;
53 push @wanted, -$line if some_other_condition($line);
54 }
55 push @wanted, 'EOF';
56 join q{, }, @wanted;
57 };
58
59 instead we can write:
60
61 print join q{, }, gather {
62 while (my $line = <>) {
63 take $line if $line =~ /\D/;
64 take -$line if some_other_condition($line);
65 }
66 take 'EOF';
67 }
68
69 and instead of:
70
71 my $text = do {
72 my $string;
73 while (<>) {
74 next if /^#|^\s*$/;
75 last if /^__[DATA|END]__\n$/;
76 $string .= $_;
77 }
78 $string;
79 };
80
81 we could write:
82
83 my $text = join q{}, gather {
84 while (<>) {
85 next if /^#|^\s*$/;
86 last if /^__[DATA|END]__\n$/;
87 take $_;
88 }
89 };
90
91 There is also a third function -- "gathered" -- which returns a
92 reference to the implicit array being gathered. This is useful for
93 handling defaults:
94
95 my @odds = gather {
96 for @data {
97 take $_ if $_ % 2;
98 take to_num($_) if /[one|three|five|nine]$/;
99 }
100 take (1,3,5,7,9) unless gathered;
101 }
102
103 Note that -- as the example above implies -- the "gathered" function
104 returns a special Perl 5 array reference that acts like a Perl 6 array
105 reference in boolean, numeric, and string contexts.
106
107 It's also handy for creating the implicit array by some process more
108 complex than by simple sequential pushing. For example, if we needed to
109 prepend a count of non-numeric items:
110
111 my @odds = gather {
112 for @data {
113 take $_ if $_ %2;
114 take to_num($_) if /[one|three|five|seven|nine]$/;
115 }
116 unshift gathered, +grep(/[a-z]/i, @data);
117 }
118
119 Conceptually "gather"/"take" is the generalized form from which both
120 "map" and "grep" derive. That is, we could implement those two
121 functions as:
122
123 sub map (&@) {
124 my $coderef = shift;
125 my @list = @{shift @_};
126
127 return gather {
128 take $coderef->($_) for (@list)
129 };
130 }
131
132 sub grep (&@) {
133 my $coderef = shift;
134 my @list = @{shift @_};
135
136 return gather {
137 do { take $_ if $coderef->($_) } for @list
138 };
139 }
140
141 A "gather" is also a very handy way of short-circuiting the
142 construction of a list. For example, suppose we wanted to generate a
143 single sorted list of lines from two sorted files, but only up to the
144 first line they have in common. We could gather the lines like this:
145
146 my @merged_diff = gather {
147 my $a = <$fh_a>;
148 my $b = <$fh_b>;
149 while (1) {
150 if ( defined $a && defined $b ) {
151 if ($a eq $b) { last } # Duplicate means end of list
152 elsif ($a lt $b) { take $a; $a = <$fh_a>; }
153 else { take $b; $b = <$fh_b>; }
154 }
155 elsif (defined $a) { take $a; $a = <$fh_a>; }
156 elsif (defined $b) { take $b; $b = <$fh_b>; }
157 else { last }
158 }
159 }
160
161 If you like it really short, you can also "gather"/"take" $_ magically:
162
163 my @numbers_with_two = gather {
164 for (1..20) {
165 take if /2/
166 } }; # @numbers_with_two contains 2, 12, 20
167
168 Be aware that $_ in Perl5 is a global variable rather than the current
169 topic like in Perl6.
170
172 This module was forked from Damian Conway's Perl6::Gather for a few
173 reasons.
174
175 to avoid the slightly incendiary name
176 to avoid the use of the Perl6::Exporter
177 ~ doesn't overload to mean string context
178
180 It would be nice to be able to code the default case as:
181
182 my @odds = gather {
183 for (@data) {
184 take if $_ % 2;
185 take to_num($_) if /(?:one|three|five|nine)\z/;
186 }
187 } or (1,3,5,7,9);
188
189 but Perl 5's "or" imposes a scalar context on its left argument. This
190 is arguably a bug and definitely an irritation.
191
193 • Arthur Axel "fREW" Schmidt <frioux+cpan@gmail.com>
194
195 • Damian Conway
196
198 This software is copyright (c) 2018 by Arthur Axel "fREW" Schmidt.
199
200 This is free software; you can redistribute it and/or modify it under
201 the same terms as the Perl 5 programming language system itself.
202
203
204
205perl v5.32.1 2021-01-27 Syntax::Keyword::Gather(3)