in reply to Re^2: Stopping and restarting a loop
in thread stoping and restarting a loop

In the original code I used $_[0] and $_[1], I changed that and gave the vars names simply to make it clearer to the OP.

Why (shift,shift)? Why not? It makes it clear that I am only going to consider using 2 parameters. Admittely that can be derived from the left-hand side of the expression, but what is wrong with shift? The docs don't indicate that it is depreciated or anything.


Well It's better than the Abottoire, but Yorkshire!

Replies are listed 'Best First'.
Re^4: Shifty Politics
by tadman (Prior) on Sep 10, 2002 at 06:47 UTC
    It's not deprecated, in fact, it's very useful. But just because you can use a screwdriver to stir paint doesn't mean you should (JBYCDMYS, the complement to TMTOWTDI). The thing that cheeses me off about shift is that it actually modifies @_. Not that I care in most cases, but it seems like wasted effort, and programs are supposed to be efficient.

    I find a few cases where shift is handy, though, such as in these highly simplified examples:
    sub new { my $class = shift; my $self = bless ({ @_ }, $class); # Hash-style params return $self; } sub fooge_default { my $self = shift; $self->fooge(@_); # Blind parameter passing }
    In most cases, though, you can just do direct assignment, mapping your params to @_ in a single declaration. If you have some sort of sick monstrosity that takes so many paramters you have to split up the declaration on to two lines, maybe you should re-think your interface. Once you're past six arguments, things get hard to parse.

    As an example where shift is dangerous, consider this:
    sub fooge { my ($foo, $bar) = (shift, shift); die "fooge() needs two arguments\n" unless (defined($bar)); # ... }
    What's wrong with this? What if undef was a valid parameter? You've destroyed the evidence, so to speak. I'd try to use this instead:
    sub fooge { my ($foo, $bar) = @_; die "fooge() needs two arguments\n" unless (@_ == 2); # ... }
    This version distinguishes between undef and not provided.

      Thanks. This and Shift versus Sanity are very interesting. I'm still learning all the why's and wherefor's of perl syntax and semantics. I probably will be for some time to come. I will just about have mastered it, and Perl6 will hit the streets! Hopefully, enough of it will carry over to not invalidate the exercise.

      A couple of questions arising:

      Wouldn't this

      sub fooge_default { my $self = shift; $self->fooge(@_); # Blind parameter passing }

      be better done as this?

      sub fooge_default { my $self = shift; goto &$self->fooge; # Not entirely sure of the syntax }

      vis

      The goto-&NAME form is highly magical, and substitutes a call to the named subroutine for the currently running subroutine. This is used by AUTOLOAD() subroutines that wish to load another subroutine and then pretend that the other subroutine had been called in the first place (except that any modifications to @_ in the current subroutine are propagated to the other subroutine.) After the goto, not even caller() will be able to tell that this routine was called first.

      Also, I realise that finding good examples is always a problem, but your example of where shift is dangerous isn't the fault of shift but of the logic of the test.

      Using the correct test in the right place make it irrelavent which method is used to access the args I think?

      sub fooge { die "fooge() needs two arguments\n" unless (@_ ==2); my ($foo, $bar) = (shift, shift); # or @_ are equally safe. # ... }

      Well It's better than the Abottoire, but Yorkshire!
        I'd rather have two dozen shifts than a single goto. There's something almost assembler-like about that version of the goto statement. I've used it before, but only inside an AUTOLOAD. That's also only because in my opinion, AUTOLOAD is lacking some important functionality, like being able to return a subroutine reference that is automatically injected into the namespace to plug a hole. That, however, is another story.

        Further, moving the @_ check to the front of the subroutine might not be viable. Or you could have something like this:
        sub fooge { my ($foo, $bar, @baz) = @_; # ... if ($foo->isa('Acme::Frobnicator')) { die "Missing 'baz' parameter to fooge()\n" unless (@baz); $foo->frob(@baz); # ... } # ... }
        You can do that with shift, sure, but it's not as concise.

        Now regardless, I still can't see why using shift is better in any respect. Where do you draw the line? Four parameters? Five? And why draw the line at shift?
        sub fooge { my ($foo, $bar) = (pop, pop); }
        You'll note that shift is more verbose, and also permanently modifies @_. I'd rather have it there intact for debugging purposes. For example:
        sub blarg { my ($foo, $bar, $bork) = @_; # ... if (computer_is_on_fire()) { die "Apocalyptic end of blarg(@_)\n"; } }