Unexpectedly short Perl snippets

The next Perl snippet should print the first 5 elements of the array referenced by the hash value, or less if the array is shorter.

while ( my ($key,$value) = each %groups ) { print "$key: \n"; my @list = grep defined, @{$value}; my @slice = grep defined, @list[0..4]; foreach my $item ( @slice ) { print " $item \n"; } print " (", scalar @slice, " of ", scalar @list, ")\n"; } 

I do not think that the first grep defined necessary, but it cannot do much harm, and it must ensure that there are no undefined array elements before the slice. The second grep defined should remove the elements of the undefined array as a result of slice when @list shorter than 5.

%groups was populated with repeated calls:

  $groups{$key} = () unless defined $groups{$key}; push @{$groups{$key}}, $value; 

In most cases, it works fine:

 key1: value1 value2 value3 value4 value5 (5 of 100) 

But sometimes - and I did not work out under what circumstances - I see:

 key2: value1 (1 of 5) key3: value1 value2 (2 of 5) 

I expect the length of the printed list and x from (x of y) will be min(5,y)

What can cause this behavior?

+6
source share
1 answer

Using grep with an array slice for @list , autogenerates the elements and expands the array.

 @foo = (1,2,3); @bar = @foo[0..9999]; print scalar @foo; # => 3 @foo = (1,2,3); @bar = grep 1, @foo[0..9999]; print scalar @foo; # => 10000 

This also happens in other contexts where Perl wants to iterate over a slice of an array.

 @foo = (1,2,3); foreach (@foo[0..9999]) { } print scalar @foo; # => 10000 @foo = (1,2,3); @bar = map { } @foo[0..9999]; print scalar @foo; # => 10000 

So what are the workarounds?

  • Use a more complex expression for grep range or operand

     @bar = grep 1, @foo[0..(@foo>=9999?9999:$#foo)]; @bar = grep 1, @foo>=9999 ? @foo[0..9999] : @foo; 
  • Use temporary array variable

     @bar = grep 1, @ tmp=@foo [0..9999] 
  • (suggested by @FMc) use map to set the intermediate array

     @bar = grep 1, map { $list[$_] } 0..9999; 
  • work with array indices, not directly with an array

     @bar_indices = grep defined($foo[$_]), 0..9999; @bar = @foo[@bar_indices]; @bar = @foo[ grep defined($foo[$_]), 0..9999 ]; 
+8
source

Source: https://habr.com/ru/post/944537/


All Articles