Beefy Boxes and Bandwidth Generously Provided by pair Networks
Come for the quick hacks, stay for the epiphanies.
 
PerlMonks  

shift in list context buggy?

by LanX (Saint)
on Nov 09, 2013 at 13:42 UTC ( [id://1061822]=perlquestion: print w/replies, xml ) Need Help??

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

hi

in this discussion : Re^6: Using an array element as a loop iterator I was quite disappointed to find out that shift in list context can lead to an endless loop:

This will never stop:

while ( ($x) = shift @a ) { ... }

After some meditation I consider this at least a design flaw, there is only an empty list to assign to the LHS when @a is exhausted, so the condition should be false.

($x is of course undef then, but while has to check the list assignment not the scalar!)

To make things worse my tests revealed that the "equivalent" splice doesn't have the same behavior:

DB<170> @a=1..2 => (1, 2) DB<171> ( ($x) = shift @a ) ? $x : "false" => 1 DB<172> ( ($x) = shift @a ) ? $x : "false" => 2 DB<173> ( ($x) = shift @a ) ? $x : "false" => undef # true!

but

DB<174> @a=1..2 => (1, 2) DB<175> ( ($x) = splice @a,0,1 ) ? "$x" : "false" => 1 DB<176> ( ($x) = splice @a,0,1 ) ? "$x" : "false" => 2 DB<177> ( ($x) = splice @a,0,1 ) ? "$x" : "false" => "false"

according to the docs:

The following equivalences hold (assuming "$[ == 0 and $#a >= $i" ) ... shift(@a) splice(@a,0,1)

OK one might argue that $i means 0 here but I still can't understand if there is a good reason justifying this breach of analogy.

Can someone shed light on this?

I suppose it's too late to fix that in newer Perl versions w/o breaking compability ... (I used 5.10)

Cheers Rolf

( addicted to the Perl Programming Language)

Replies are listed 'Best First'.
Re: shift in list context buggy? (scalar)
by tye (Sage) on Nov 09, 2013 at 14:32 UTC
    my( $a, $b ) = ( shift @as, shift @bs );

    There are lots of constructs where returning an empty list can be surprising if one expects a scalar to be returned.

    splice returns a list and so should certainly return an empty list for edge/failure cases. Going back in time, I could see defining shift as returning either 1 scalar or returning the empty list, making that expectation clear, and that decision being a fine one. But I also can see sanity in defining shift as returning a scalar (the current reality).

    Consider the relative difficulty of fixing your problem case vs. my counter-factual problem case:

    while( ($x) = shift @a ) { # Broken while( $x = shift @a ) { # Fixed while( @a ) { $x = shift @a; # Better my( $a, $b ) = ( shift @as, shift @bs ); my( $a, $b ) = ( scalar shift @as, scalar shift @bs );

    Also, consider how likely one is to notice the breakage between the two scenarios.

    This leads me to currently slightly prefer the definition of shift as "returns a scalar", that is, the status quo.

    - tye        

      Thx!²

      But

      > Consider the relative difficulty of fixing your problem case vs. my counter-factual problem case:

      while(  $x  = shift @a ) {    # Fixed

      this is not a fix because while stops, if any element of @a is false.

      DB<187> @a=(1,undef,2,3,0,4,5) DB<188> print $x while $x =shift @a 1 DB<189> print $x while $x =shift @a 23 DB<190> print $x while $x =shift @a 45 DB<191> print $x while $x =shift @a

      Thats the old semipredicate problem, which can only be solved with list-assignments.¹

      But I agree with you that it's most probably too late to fix that design decision...

      Cheers Rolf

      ( addicted to the Perl Programming Language)

      ¹) for completeness: no defined doesn't help here, if undef is a legal value.

      updates
      • corrected "semipredicate problem"
      • improved code example
      • your "better" update is indeed better

      ²) best answer so far

        But I agree with you that it's most probably too late to fix that design decision...

        Amen to that, brother. Trying to fix | change this ancient design decision now would provoke sustained and piercing shrieks of anguish that would make the howls induced by the recent hash ordering 'fix' sound by comparison as mellow as a Hash Bash gathering!

Re: shift in list context buggy?
by BrowserUk (Patriarch) on Nov 09, 2013 at 16:29 UTC

    The problem here is that you are trying to use the return value of shift to determine if the array is empty. And since you appear to want undef to be "a legal value", there is no value that shift could return to indicate that the array is now empty.

    The problem is avoided - or rather you avoid creating a problem -- by making the loop condition test the actual condition -- whether the array is empty -- and not conflating it with an operation that is unrelated to that condition:

    while( @array ) { my $x = shift @array; # use $x. }

    This isn't a "Perl design decision" to be regretted; it's a programmer error to be avoided.


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      > This isn't a "Perl design decision" to be regretted; it's a programmer error to be avoided.

      nope it's inconsistent, and any breach of orthogonality is a pain in the *ss and should at least be better documented.

      > And since you appear to want undef to be "a legal value", there is no value that shift could return to indicate that the array is now empty.

      So according to you, the demonstrated code using splice is a programmers error!

      Of course shift should return an empty array which is false.

      And of course ($x)=() would result in $x=undef without being true. This would be consistent with Perl's usual behavior.

      DB<196> $x=42 => 42 DB<197> ( ($x) = () ) ? "$x" : "false" => "false" DB<198> $x => undef

      Please read the code examples already given before replying.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

      update

      expanded code

        So according to you, the demonstrated code using splice is a programmers error!

        WTF did you dream that incomprehensible conclusion up from?

        It is neither stated nor implied, nor logically derivable from anything I posted.

        You just made it up in an attempt to misdirect.

        Of course shift should return an empty array which is false.

        Why "of course"?

        splice can return multiple values, so when it has nothing to return, it makes sense to return an empty list.

        Conversely, shift can only ever return a single value; so why would it ever return a list. Even an empty one.

        Of course, it could return an empty list, and that would allow your construct to 'work'.

        But it doesn't and never has! And -- other than to make your peculiar construct work -- there is no reason why it should.

        Thus, it is your construct that is at fault.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: shift in list context buggy?
by hdb (Monsignor) on Nov 09, 2013 at 13:51 UTC

    The assignment operator will return a non-empty list: (undef) and therefore while will never stop.

    use strict; use warnings; use Data::Dumper; my @a = ( 1, 2 ); my $x; my @b; while( @b = ( $x ) = shift @a ) { print Dumper \@b; }
      Yes, of course¹, but the question was why !

      shift could easily return an empty list and $x is still undef.

      DB<178> $x=() => undef

      It's list context, no reason to act differently to splice.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

      ¹) tl;dr ?

        "shift could easily return an empty list and $x is still undef."

        It could easily, yes. But it doesn't. And it's documented as retuning undef. To do something different may well subtly break a lot of code on CPAN.

        That said, shift is one of those keywords that Perl allows you to override...

        use subs 'shift'; sub shift (+) { my $arr = $_[0]; @$arr ? CORE::shift(@$arr) : (); }
        use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

        You have been talking about empty lists, so I thought I point out that there is no empty list involved.

Re: shift in list context buggy?
by Beechbone (Friar) on Nov 12, 2013 at 13:33 UTC

    To me it's perfectly clear why those two behave differently: shift() returns a scalar, splice() returns a list.

    Putting a function that is "scalar context only" in a list context will give you a list with one element. No special magic to (surprisingly) return an empty list here.

    Many builtins have list context semantics, but for shift() I'd have no idea what would make sense. Although a "shift ARRAY, COUNT" semantics might be handy sometimes. But even then I'd expect shift to always return exactly COUNT elements, even if ARRAY was empty.


    Search, Ask, Know
      > To me it's perfectly clear why those two behave differently: shift() returns a scalar, splice() returns a list.

      They are documented to be equal...

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        They are documented to be equal...

        ... when used in a void context!


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: shift in list context buggy?
by karlgoethebier (Abbot) on Nov 10, 2013 at 13:06 UTC
    "This will never stop..."

    So just don't do it ;-)

    Sorry bro, i can't follow you. With due respect, isn't that nitpicking?

    My best regards, Karl

    «The Crux of the Biscuit is the Apostrophe»

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (6)
As of 2024-03-19 08:48 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found