perl5
perl5 copied to clipboard
[EXPERIMENT] Iterating over every N elements in a list
I would like to do:
my $list = [ key => 'value', k2 => 2, k3 => 3 ];
for my( $key, $value ) ( @$list ) {
...
}
I do not want to use hacks listed here
Then I found similar idea at this question
for i,k in ???:
...
Notice, how many people are interested in similar question: 2500 votes up, 1 000 000+ views!
But there is also no solution from the box =(
But perl
is better it will allow just:
for my( $first, $second, $n ) ( @list ) {
...
}
or any number of elements we want, will it? ;-)
Previously discussed at https://www.nntp.perl.org/group/perl.perl5.porters/2017/03/msg243848.html. I would like this syntax as well.
it makes more sense with hashes (and hashrefs) where there is each
operator (as used in while
loop conditional)
With core-shipped modules, the neatest that can currently be done is probably List::Util::pairs
:
use List::Util 'pairs';
foreach ( pairs %$hash ) {
my ($k, $v) = @$_;
...
}
An imagined hypothetical syntax involving foreach my(VARLIST) (LIST) { BLOCK }
would probably look like:
foreach my ($k, $v) ( %$hash ) {
...
}
It's a small improvement but nothing too ground-breaking. I don't imagine any back-compat issues because currently the proposed syntax is an error, albeit a weirdly-worded one:
$ perl -ce 'foreach my ($k, $v) (one=>1, two=>2) { }'
Missing $ on loop variable at -e line 1.
Two lists in a row look confusing when syntax is only parentheses, no real separator.
It could be e.g. foreach(;){}
or foreach(;;){}
syntax (semicolon as separator), if foreach
and for
would be different, but they are same, they are alliases.
E.g. it could be foreach( @binding_variable ; @array ; $position_increment ){}
, where @binding_variable
was e.g. my( $key, $value )
and $position_increment
was 2, or e.g. @binding_variable
was my( $left, $middle, $right )
, and $position_increment
was e.g. 1, which mean that "iterator" advance by 1, e.g. one can find how many hills ($_[ $i - 1 ] < $_[ $i ] > $_[ $i + 1 ]
) the array contains. It would be similar to "@integer_array" =~ m/ (?= (\d+)[ ](\d+)[ ](\d+) ) (?{ compare_them }) (*FAIL) /x
.
But instead of foreach(;;){}
(which is C style loop) it can be a confusing foreach()()(){}
, where third ()
if omitted evaluates to default.
Could sound crazy, but how about:
forall( @list ) {
my( $i1, $i2, $3 ) = (shift, shift, shift);
}
Looks like usual subroutine call and subroutine implementation at one place. @list
is bound to @_
and we even can remove items dynamically.
forall
will call BLOCK while there are elements and we can shift
as many elements as we want.
It seems it could be implemented as module without any problem. Drawback here could be performance issue while coping @list
If anything, that should put them in a lexically declared array. We don't need to abuse more global variables.
forall my @items (@list) { ...
But I don't really see the point of this. You can already just shift items off @list
.
Having shift
change its behaviour inside a forall
block would be kinda weird. But currently I don't see the difference from the tried-and-tested:
while(@list) {
my ($x, $y, $x) = (shift @list, shift @list, shift @list);
...
}
which admittedly is a little repetative. OK how about
while(@list) {
my ($x, $y, $z) = splice @list, 0, 3;
...
}
Or lets invent an shiftn
which just eats the right number of items already:
while(@list) {
my ($x, $y, $z) = shiftn @list;
...
}
Already many ideas that are very close to your suggestion.
Edited: renamed shiftn
, from previous nshift
idea
@Grinnz : at your case @items
will grab all items from @list
@leonerd: while( @list ){ ... }
its great. Could you add this to documentation for List::Util
?
right number
do you mean that shiftn
will shift as many element as there on left side of =
? If so it will be nice.
@leonerd:
while( @list ){ ... }
its great. Could you add this to documentation forList::Util
?
Why? It shouldn't be List::Util
's job to teach users basic Perl.
If anything, this would be suitable for a new entry in https://perldoc.perl.org/perlfaq4#Data:-Arrays
@leonerd : yes, List::Util
should not teach, but this module is a bundle of functions to work with list. So at least it must mention while( @list ) { ... }
to deal with N elements and provide a link to appropriate documentation.
IMHO: Despite on I am an old perl programmer I never see this great construction before. I did not know this BASIC tool before
while(@list) {
my ($x, $y, $z) = splice @list, 0, 3;
...
}
We're using this suggestion to trial an RFC process.
Or lets invent an nshift which just eats the right number of items already:
my ($x, $y, $z) = nshift @list;
Cool idea, I guess it has a lot of edge cases tho:
my @.***)= nshift @list;
It would be neat in general to be able to do this. Maybe a special prototype that pushes the number of arguments that the result will be assigned to into the stack automagically?
cheers, Yves
-- perl -Mre=debug -e "/just|another|perl|hacker/"
https://perldoc.perl.org/5.36.0/perldelta#iterating-over-multiple-values-at-a-time-(experimental)
Can we close this issue, now that @leonerd committed https://github.com/Perl/perl5/commit/4b6ad5110c29c13149771584eabe8b42feb5c63c making the feature no longer experimental?