Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Can't understand function returning undefs

by rg0now (Chaplain)
on Sep 24, 2010 at 20:10 UTC ( [id://861875]=perlquestion: print w/replies, xml ) Need Help??

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

Dear all,

Could someone please enlighten me why the below code

use strict; use warnings; use Data::Dumper; sub a{ return(undef, undef); } my($x, $y); if(($x, $y) = a() ){ print "OK1\n"; } if((undef, undef) = a() ){ print "OK2\n"; } if(a()){ print "OK3\n"; } if(undef, undef){ print "OK4\n"; }
prints
OK1 OK2
I absolutely expected it to print nothing at all. I mean I expect ($x, $y) = a() and above all (undef, undef) = a() to evaluate to false in boolean context. What do I miss here?

Replies are listed 'Best First'.
Re: Can't understand function returning undefs
by ikegami (Patriarch) on Sep 24, 2010 at 21:50 UTC

    Just like "FOO + BAR" doesn't return "FOO" or "BAR", "FOO = BAR" doesn't necessarily return "FOO" or "BAR". A list assignment in scalar context returns the number of values returned by its RHS.


    In detail,

    "($x,$y) = a()" is a list assignment, and it is being evaluated in scalar context (from the if()). A list assignment in scalar context returns the number of values returned by its RHS. In this case, that's 2.

    "(undef,undef) = a()" is a list assignment, and it is being evaluated in scalar context (from the if()). A list assignment in scalar context returns the number of values returned by its RHS. In this case, that's 2.

    "a()" is a function call, and it is being evaluated in scalar context (from the if()). A function called in scalar context evaluates its return expression in scalar context, so "undef, undef" is evaluated in scalar context. The list/comma operator in scalar context evaluates each operand in turn and returns the last value. In this case, that's undef.

    "undef, undef" is being evaluated in scalar context (from the if()). The list/comma operator in scalar context evaluates each operand in turn and returns the last value. In this case, that's undef.

      Retracted after bitter tirade by ikegami. Thanks for setting me straight in such a civil manner.

        This is closely related to the concept of the default input operator

        What are you talking about???

        • $_ is not an operator, at least no more so than $x.
        • Some functions default to using $_ as an argument, but that is unrelated to this thread.
        • $_ has nothing to with input.
        • How can an input operator have anything to do with this?

        And the rest of the post is no better. So many errors!

        sub a{ return 1, 2, 3 } a();

        This evaluates what a() returns, in scalar context. $_ gets the last value returned by a(). In this case, $_ is 3, or True if evaluated in Boolean context. If you tested $x = a(), you'd get the same True result, with $x equal to 3.

        • That code doesn't provide enough information to determine in which context a() is evaluated. If anything, void context is implied.
        • Nothing is assigned to $_.
        • If a() is evaluated in scalar context, then it makes no sense to talk about the last value it returns as it only returns one.

        sub a{ return 1, 2, 0 }  a();

        $_ gets the last value returned by a(), as before. In this case, $_ is 0 in scalar context, or False in Boolean context!

        • Nothing is assigned to $_.
        • The same value is returned by the comma operator in both "boolean context" and scalar context.

        @a = (undef, undef); @a;

        This array is evaluated in scalar context because we haven't done anything to force list context. An array, evaluated in scalar context, is always the number of elements it contains.

        • That code doesn't provide enough information to determine in which context a() is evaluated. If anything, void context is implied.
        • There is no default context.

        sub a{ return 1, 2, 0 } ($x,$y) = a();

        We're still evaluating what a() returns, but here we've forced list context. That means that $_ is assigned the number of elements in the list returned, not the elements. It doesn't matter what a() returns -- if it returns one or more elements, $_ will be set to non-zero, or True in Boolean context.

        • List context isn't being forced, if there is such a thing.
        • Nothing is assigned to $_, so most of that is just nonsense.
        • The same value is returned by the list assignment operator in both "boolean context" and scalar context.

        sub a{ return 1, 2, 0 }  @arr = a();

        Exactly the same as above.

        • List context isn't being forced, if there is such a thing.
        • Nothing is assigned to $_, so most of that is just nonsense.
        • The same value is returned by the list assignment operator in both "boolean context" and scalar context.

        (undef, undef);

        An array is a container for a list. This is NOT an array, and we haven't done anything to force list context, so we have a list of scalars evaluated in scalar context. $_ is set to the last element, which is undef -- or False in Boolean context.

        • There is no default context.
        • That code doesn't provide enough information to determine in which context a() is evaluated. If anything, void context is implied.
        • Nothing is assigned to $_.
        • The same value is returned by the comma operator in both "boolean context" and scalar context.

        sub a { return undef, undef } ($x, $y) = a(); if(not $x && not $y) { print "False\n" }

        I think this is closer to what you're looking for. Whenever you don't specifically lay out the condition you are testing, you are probably testing $_.

        No, it's not what he wants.

        • It doesn't loop.
        • The condition of the "if" statement relies on incorrect assumptions about precedence.
        • A truth test is two levels removed from optiomal. (A defined test would be better, but testing whether the iterator returned anything at all would be best.)

        I hope this helps.

        Pure harm. Good thing the formatting makes it unreadable.

Re: Can't understand function returning undefs
by jettero (Monsignor) on Sep 24, 2010 at 20:21 UTC
    if( ()=(0,0) ) { print "sometimes you're counting the things in the list and getting +2.\n" } if( (0,0) ) { print "sometimes you're applying the comma operator and getting und +ef (last)\n"; }

    if is going to boolean test, so it's rather like this:

    use Data::Dumper; print Dumper({ test1=>scalar( 7,13 ), test2=>scalar( ()=(7,13) ) }), " +\n";
      Thanks. Not that I like this behavior but I think I'll have to live with it.

      But if we are at it. My problem is that I have a function that returns two values when data is available to it and when no data remained it returns undef.

      sub recv_data{ if(read_some_data){ return($stuff1, $stuff2); } else { return; } }
      What I would like to do is to call this function in a while until all data is consumed. I tried to do this first by
      while(($x, $y) = recv_data){ ... }
      which failed due to the above reasons (i.e., the while block always executed since ($x, $y) = recv_data always evaluates to true even if the function returns undef).

      I still have the option to write

      while(1){ my($x, $y) = recv_data; last unless defined $x; ... }
      but I fail to see this as elegant.

      How to organize this code so that I obtain the required behavior in an elegant and readable way?

        sub recv_data{ if(read_some_data){ return($stuff1, $stuff2); } else { return; } }

        The function in your example returns an empty list, not undef, in list context when there is no more data available.

        If your real function actually returns undef in list context (which is 'supplied' to the function by the  ($x, $y) sub-expression in your example code), then the list will always have at least a single item (i.e., undef) and will always evaluate as true; only the empty list or array is false. See the code below for the difference between
            return;
        and
            return undef;

        >perl -le "use warnings FATAL => 'all'; use strict; ; my @pairs1 = qw(1 2 3 4); ; sub S1 { return if not @pairs1; return pop @pairs1, pop @pairs1; } ; my ($x, $y); while (($x, $y) = S1()) { print qq{$x $y}; } ; my @pairs2 = qw(5 6 7 8); ; sub S2 { return undef if not @pairs2; return pop @pairs2, pop @pairs2; } ; while (($x, $y) = S2()) { print qq{$x $y}; } " 4 3 2 1 8 7 6 5 Use of uninitialized value $x in concatenation (.) or string at ...
        while( defined( ( ($x,$y)= recv_data() )[0] ) )

        But I'd rather do that like:

        my( $x, $y )= recv_data(); while( defined $x ) { ... ( $x, $y )= recv_data(); }

        - tye        

        But if we are at it. My problem is that I have a function that returns two values when data is available to it and when no data remained it returns undef.

        It should return an empty list when no data remains. Compare

        my @a = qw( a b c d ); my $i = sub { @a ? splice(@a, 0, 2) : () }; while (my ($x,$y) = $i->()) { print("$x:$y\n"); }
        with
        my @a = qw( a b c d ); my $i = sub { @a ? splice(@a, 0, 2) : (undef,undef) }; while (my ($x,$y) = $i->()) { print("$x:$y\n"); }

        Update: Added missing semicolons.

Re: Can't understand function returning undefs
by JavaFan (Canon) on Sep 24, 2010 at 21:40 UTC
    You're expecting a true/false value from your function a(). However, the expression in your if statement isn't a(), it's a list assignment. And, in scalar context, list assignment returns the number of elements on the RHS of the assignment. So, if a() returns 2 elements, the assignment returns 2 (changing the variables on the LHS is a mere side-effect of the assignment). It's only the quantity of the elements returned by a() that matters, not their value.
      Here is what drives me crazy:
      if(undef, undef){ print "OK1\n"; } if( (undef, undef) ){ print "OK2\n"; } my @a = (undef, undef); if(@a){ print "OK3\n"; }
      This prints OK3. According to the above, it should also print OK2 because (undef, undef) is a list of two elements that evaluates to 2 in scalar context. Still, it doesn't.
        There are no lists in scalar context! It's SCALAR context. What you have in
        if (undef, undef) { # An extra set of parens doesn't matter. print "OK1\n"; }
        is the comma operator in scalar context. Which evaluates its LHS, throws it away, then returns its RHS. Which is undef, and hence false.
        According to the above
        If with "above" you mean the post you're replying to, you notice I talk about list ASSIGNMENT. Not lists. Nor arrays.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-25 15:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found