perl5
perl5 copied to clipboard
Missing support for undef in for_list assignments
This post https://www.effectiveperlprogramming.com/2024/06/iterate-over-multiple-elements-at-the-same-time/ exposes some of the shortcomings of the for_list feature:
No placeholders (yet)
So far, this new syntax doesnât have a way to skip values. In a normal list assignment, you discard a value coming from the right hand list with a literal
undef:my( $s, undef, $t ) = @animalsTry that in the
forlist and you get a syntax error:foreach my( $s, undef, $u ) ( @animals ) {Â # ERROR! say "$s ^^^ $u"; }
The rest of the post comments on other missing capabilities of the feature:
- operating on a hash could be optimized to not build the whole list of values, but iterate on the hash directly instead
- no support for refaliasing
The conclusion implies we could invest a little more work on the for_list feature:
The experimental for_list feature lets you take multiple elements of the list in each iteration. This doesn't yet handle many of the list assignment features that would make this as useful as people will want it to be.
- operating on a hash could be optimized to not build the whole list of values, but iterate on the hash directly instead
Another thing that could definitely be optimised internally is applying a similar optimisation to the FOR_ARRAY case, when iterating two at a time over a call to builtin::indexed on same array.
I.e. both of these should be done efficiently without a temporary list:
foreach my $elem ( @array ) { ... }
foreach my ( $idx, $elem ) ( builtin::indexed @array ) { ... }
aliasing
You only get that elsewhere in the language by not unpacking @_ (subs) or $_ (loops). There's no destructuring syntax that can give you aliases. Elsewhere the "= is copy" rule thwarts unpacking an alias by copying the value when you unpack it/them into scalar(s).
sub frobulate {
my ($copy, @copies) = @_;
# if you wanna do alias stuff you have
# to assign to $_[xx] but you can
# shallow modify $copy/@copies freely
...
}
When looping over hash(-ref)es you gotta do something about key randomisation anyway, and then you gotta zip them back into a list for for_list to unpack, so you'd have to do
for my ($k, $v) ( map { $_, $hash->{$_} } sort keys %$hash) {
...
}
at that point you're much more likely to get this patch past a code review:
for my $k (sort keys %$hash) {
my $v = $hash->{$k};
...
}
@leonerd
I.e. both of these should be done efficiently without a temporary list:
foreach my $elem ( @array ) { ... } foreach my ( $idx, $elem ) ( builtin::indexed @array ) { ... }
I thought the first one was already optimised?
@guest20
There's no destructuring syntax that can give you aliases.
Sure there is (at least experimentally â but which is all @book was referring to anyway):
use 5.022;
use feature 'refaliasing';
my @array = ( "a".."c" );
\my ( $x ) = \( @array );
$x = "foo";
say for @array;
@book
operating on a hash could be optimized to not build the whole list of values, but iterate on the hash directly instead
My gut reaction is that this might not actually be a good idea. People use for ( keys %hash ) as a best practice because the fact that the each iterator is global makes it susceptible to action at a distance.
(E.g.: if you last of out of a while ( my ( $k, $v ) = each %hash ) loop, without exhausting the iterator, and then the hash is passed to somewhere else that tries to iterate the hash the same way, then the other loop might inadvertently skip the keys youâd already iterated. Or, if your while/each loop passes the hash to something that calls keys on it, you have an infinite loop that restarts over and over. Etc.)
We already have while ( my ( $k, $v ) = each %hash ). Is it useful to have a different spelling of the exact same thing?
Plus, âaliasingâ the key wonât actually alias the key. This is true for keys (which returns copies) vs values (which returns aliases). Itâs even true for each - which produces a copy of the key and an alias of the value! This is slightly more awkward to demonstrate, but you can:
use 5.012;
my %h = map +( $_ => $_ ), "a".."c";
while ( my ( $key_ref, $value_ref ) = \each %h ) {
( $$key_ref, $$value_ref ) = qw( foo bar );
}
say for %h;
[ A side note here is that you canât usefully do while ( \my ( $key, $value ) = \each %h ) because each returns an empty list when the iterator runs out, which \my ( ... ) winds up treating as an error: Assigned value is not a reference. Looks like refaliasing doesnât vivify undefs into references, which off the cuff Iâm not sure is a design flaw or correct choice or just an implementation bug or what. ]
@leonerd
I.e. both of these should be done efficiently without a temporary list:
foreach my $elem ( @array ) { ... } foreach my ( $idx, $elem ) ( builtin::indexed @array ) { ... }I thought the first one was already optimised?
Yes, it is. Sorry, bad wording on my part. I meant, by comparison to the first (which is already optimised), the second should be optimised the same way.
@ap having very recently learnt about feature refaliasing I can confidently say I hate it.
use 5.022; say $^V;
use feature 'refaliasing';
my $whatever = "whatever";
\(my $thing) = \$whatever;
$thing = "Oh no!";
say "$thing $whatever"
Aliasing via reference is experimental at main.pl line 5.
v5.34.0
Oh no! Oh no!
It breaks the = does copy rule, hard. This is super cursed.