leocharre has asked for the wisdom of the Perl Monks concerning the following question:

I'm looping for arguments in a subroutine. How can I loop and shift using the default variable $_ ?

We can do this..

sub scribble { while ( $a = shift ){ $a; # has arg } }

What I am trying to flirt with here is..

sub scribble { while ( shift ){ $_ ; # not here! } }
That I won't have to instance a symbol?

( And I mean, seriously... where the #$!@& is shift output going?? Is its scope only in () ??? )

Replies are listed 'Best First'.
Re: using shift and $_ in loop
by Corion (Patriarch) on Oct 21, 2009 at 15:30 UTC

    I'm not sure that what you want to do is sane, but that might be because I don't know your purpose. The following works but leaves @_ unmodified until after your loop:

    sub scribble { for (@_) { ... }; @_ = (); };

    Alternatively, with the assignment:

    sub scribble { while ($_ = shift) { ... }; };

    My second approach and your while(...) approach suffer from the same problem that they will end the loop if a false value is contained in @_

      Yes they do suffer that- it tests for truth.

      Maybe I was too vague in my posting, if so I apologize.

      I expect my sub to take an unforeseen number of arguments. My sub will take the arguments, discard some, and the ones that are not discarded, will be altered- and a list of the altered arguments are returned back.

      An example..

      use Lingua::Names 'is_name'; my @a = qw/andy lo1 beca cris/; my @A = alter(@a); sub alter { my @newlist; for ( @_ ){ my $name = is_name($_) or next; push @newlist, uc($name); } @newlist; }

      Now, how can I make this so I can use shift, maybe even a for loop with undef and last??? What might be apropriate here to make it a little more minimal? This is a little closer..

      sub alter { my @newlist; while ( @_ ){ $_ = shift @_; my $name = is_name($_) or next; push @newlist, uc($name); } @newlist; }
      I'm wondering if I can just shift and push from @_ and then return the modified @_ .. using $_ ??? Maybe this is just too much coffee speaking and I need to chill out?
        sub alter { my @newlist; for ( @_ ){ my $name = is_name($_) or next; push @newlist, uc($name); } @newlist; }

        I'd write that as

        sub alter { grep $_, map { uc is_name($_) } @_; }

        instead.

        In Perl 6 I'd maybe write something more similar to what you wrote:

        sub alter(*@a) { gather for @a { my $name = is_name($_); take uc $name if $name; } }

        Or even

        sub alter(*@a) { gather for @a.map({ is_name($_)}) { take .uc if $_; } }
        Perl 6 - links to (nearly) everything that is Perl 6.

        Now, how can I make this so I can use shift,

        What's wrong with the for version?

        map and grep lend themselves well here:

        sub alter { return map uc, grep is_name($_), @_; }
        If I'm reading this correctly, you want @A as the elements of @a that pass the is_name(x) test?
        @A = grep { is_name($_) } @a;
        perldoc -f grep is your friend.

        Update: I just noticed the uc() you used. In that case:

        @A = map { uc($_) } grep { is_name($_) } @a;
        perldoc -f map is also your friend.
Re: using shift and $_ in loop
by ikegami (Patriarch) on Oct 21, 2009 at 15:45 UTC
    You never assigned to $_. The equivalent of
    sub scribble { while ( $a = shift ){ $a; # has arg } }
    that assigns to $_ is
    sub scribble { while ( $_ = shift ){ $_; # has arg } }

    In both cases, you are using global variables. $_ is particularly dangerous to clobber. Fix:

    sub scribble { local $_; while ( $_ = shift ){ $_; # has arg } }
      Thank you, this is really useful. So, you suggest localizing $_ as a precaution?
        Yeah, don't clobber your caller's variables.
Re: using shift and $_ in loop
by moritz (Cardinal) on Oct 21, 2009 at 15:27 UTC
    If deconstruction of the @_ array is not required, just use
    for (@_) { print; # using $_ here }

    See shift, @_.

    Perl 6 - links to (nearly) everything that is Perl 6.
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: using shift and $_ in loop
by jakobi (Pilgrim) on Oct 21, 2009 at 16:08 UTC

    Leocharre, you're right on in being cautious of the truth of the loop condition. Which is necessary due to the lack of while(<>)-style magic in a mere while($_=shift){}.

    Consider how the usual looping variants work or mostly fail, or just ask Perl with a simple, cute and short one-liner:

    perl -e 'use Data::Dumper; @a=(1,0,undef,2); print Dumper(@a); print " +1=$_=\n" foreach (@a); @ARGV=@a; print "2=$_=\n" while($_=shift @a); +@a=@ARGV; print "3=$_=\n" while($_=shift @a,defined $_); @a=@ARGV; pr +int "4=$_=\n" while($_=shift);'

    PS: ad local: local($_) helps to not hurt the caller (but isn't e.g. necessary in a foreach(@a) scope), while my($_) (since 5.10) helps the current scope to not be hurt by called routines (and signal handlers, etc).

    \Peter, who is wondering whether the line-eater did like the taste of his attempted .sig
Re: using shift and $_ in loop
by johngg (Canon) on Oct 21, 2009 at 16:59 UTC

    No need to test the shift in a loop.

    $ perl -le ' > sub isname { return 1 if $_[ 0 ] =~ m{(?i)^(?:bill|fred)$} } > @a = qw{ apple peach fred }; > sub alter > { > my @new = > map { uc } > grep { isname( $_ ) } > map { my $w = shift } > 0 .. $#_; > } > @A = alter( @a ); > print for @A;' FRED $

    I hope this is of interest.

    Cheers,

    JohnGG

Re: using shift and $_ in loop
by pileofrogs (Priest) on Oct 21, 2009 at 19:47 UTC
    ( And I mean, seriously... where the #$!@& is shift output going?? Is its scope only in () ??? )

    Actually, it's scope is the whole while loop. For instance..

    for my $foo (@wibble) { print $foo; }

    Oh, and $_ is global, as mentioned above (said Pileofrog as though he already knew that...) so the scope won't matter.

    UpdateD'oh! Removed stupid '=' sign. Thanks moritz.

      ... so the scope won't matter.
      Famous last words :-)