Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Avoiding vivification with array slice

by jbert (Priest)
on Sep 09, 2008 at 12:37 UTC ( #710067=perlquestion: print w/ replies, xml ) Need Help??
jbert has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

This surprised me in code recently:

#!/usr/bin/perl use strict; use warnings; use Data::Dumper qw(Dumper); my @a = qw(1 2 3); print "Initially: " . Dumper(\@a); my @b = @a[0..5]; print "After assign from: " . Dumper(\@a); @b = grep { defined $_ } @a[0..5]; print "After grep over: " . Dumper(\@a); # Didn't expect the undef's

Well, I actually ran into the issue with a foreach loop rather than a grep. I was thinking that the defined test would exclude any bits of the slice I didn't want - with no side effects. But clearly reading the value to test it's defined-ness is enough to vivify.

Does anyone have a nice, concise syntax for looping over "the first N elements of an array, without vivification"?

The best I can think of is:

my @b = @a[0..N-1]; foreach my $val (@b) { .... }

which has the wart of requiring the additional variable @b.

Comment on Avoiding vivification with array slice
Select or Download Code
Re: Avoiding vivification with array slice
by moritz (Cardinal) on Sep 09, 2008 at 12:45 UTC
    Does anyone have a nice, concise syntax for looping over "the first N elements of an array, without vivification"?
    use List::Util qw(min); for my $value (@a[0 ... min($#a, $N)]) { }
Re: Avoiding vivification with array slice
by betterworld (Deacon) on Sep 09, 2008 at 12:57 UTC

    You could modify the line in question like this:

    @b = grep { defined $_ } @{[ @a[0..5] ]};

    Or even:

    @b = grep { defined $_ } @{[@a]}[0..5];

    But whatever approach you choose, make sure that you comment it so it's clear why you do it.

      Ah, this is cool. An anonymous copy, dereferenced on the spot.

      And, as you say, definitely comment-worthy.

      Thanks

Re: Avoiding vivification with array slice
by Anonymous Monk on Sep 09, 2008 at 13:49 UTC
    Next time please put also the output of your test programs :)
    your $program, 'here'; __END__ # perl /tmp/slices.pl Initially: $VAR1 = [ '1', '2', '3' ]; After assign from: $VAR1 = [ '1', '2', '3' ]; After grep over: $VAR1 = [ '1', '2', '3', undef, undef, undef ];

      Well, I thought the comment in the code was sufficient - I didn't want to over-cook the node.

      I'll try and be more clear next time, thanks.

Re: Avoiding vivification with array slice
by jhourcle (Prior) on Sep 09, 2008 at 14:04 UTC
    I was thinking that the defined test would exclude any bits of the slice I didn't want - with no side effects. But clearly reading the value to test it's defined-ness is enough to vivify.

    You're testing too late. Besides betterworld's trick, or moritz's optimization, you could specifically test for defined-ness per element, rather than taking the slice first:

    @b = map { defined($a[$_]) ? ($a[$_]) : () } (0..5);

      Cool. Accessing $a[$i] isn't enough to vivify (otherwise your defined test would do it).

      So we don't actually need the defined test in your map and can get away with:

      # This *doesn't* vivify $a[3,4,5] @b = map { $a[$_] } (0..5);

      Thanks.

        The point of the defined wasn't to prevent autovivification, but to implement the grep you had in your original code. The following two snippets are equivalent.
        @b = map { defined($a[$_]) ? ($a[$_]) : () } 0..5;
        @b = grep { defined } map { $a[$_] } 0..5;

        What you suggested (@b = map { $a[$_] } 0..5;) is *not* equivalent.

Re: Avoiding vivification with array slice
by Narveson (Chaplain) on Sep 09, 2008 at 14:05 UTC

    You want to stop after you have

    • looked at N elements, or
    • reached the end of the array.

    You have chosen to put the "N elements" idea in the loop control and add a test for the end of the array. How about reversing this? Looping over an array is so easy in Perl. Adding a counter to quit after N elements is not much harder.

    use constant N => 3; my $counter; my @a = 'A' .. 'Z'; $counter = 1; ELEMENT: foreach my $val (@a) { last ELEMENT if $counter++ > N; print "Looking at $val\n"; }

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://710067]
Approved by Corion
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (5)
As of 2014-07-13 11:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    When choosing user names for websites, I prefer to use:








    Results (249 votes), past polls