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?