Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

What's happening in this expression?

by zapdos (Sexton)
on Oct 11, 2020 at 09:11 UTC ( #11122690=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks, if I do what's in the code below, that's I don't enclose the left-hand side variables in parentheses like  my ($a, $x, $y, $z) = foo() but instead do:

 my $a, $x, $y, $z = foo()

Can you monks please explain step-by-step what computations will happen here?

Replies are listed 'Best First'.
Re: What's happening in this expression?
by haukex (Archbishop) on Oct 11, 2020 at 09:50 UTC
    Can you monks please explain step-by-step what computations will happen here?

    Under strict, none, because it will not compile unless $x, $y, and $z have been previously declared. Otherwise, B::Deparse helps:

    $ perl -MO=Deparse,-p -e 'my $a, $x, $y, $z = foo()' (my($a), $x, $y, ($z = foo()));

    In other words, it's declaring a lexical $a, then $x and $y are simply sitting there, and then $z is assigned the return value of the function call foo() - although if that's not defined, that call will fail. See also Comma Operator. Turning on warnings gives some relatively helpful hints:

    $ perl -w -e 'my $a, $x, $y, $z = foo()' Parentheses missing around "my" list at -e line 1. Useless use of a variable in void context at -e line 1. Useless use of a variable in void context at -e line 1. Name "main::x" used only once: possible typo at -e line 1. Name "main::z" used only once: possible typo at -e line 1. Name "main::y" used only once: possible typo at -e line 1. Undefined subroutine &main::foo called at -e line 1.
      Thank you so much. What are the two warnings "Useless use of a variable in void context at -e line 1." referring to?
        What are the two warnings "Useless use of a variable in void context at -e line 1." referring to?

        They're referring to $x and $y, because the statement as written above is in void context, so these variables are in void context too. I was intentionally a little vauge with my "simply sitting there" because it's possible for them to not be in void context, for example if this statement was the last thing in a sub (the context that the sub is called in is passed through):

        use warnings; use strict; my ($x,$y,$z); sub foo { qw/a b c d/ } sub bar { my $a, $x, $y, $z = foo() } my @x = bar(); my $r = bar();

        This only produces the warning "Parentheses missing around "my" list". @x will contain the values (undef, undef, undef, "d"), because $a, $x, and $y are undef (nothing is assigned to them), while $z is assigned the return value of foo(), which means it is assigned "d" because my example sub foo is returning a list, and a list in scalar context evaluates to its last value. $r will contain "d" for the same reason.

        >then $x and $y are simply sitting there
Re: What's happening in this expression? (Updated)
by LanX (Sage) on Oct 11, 2020 at 12:42 UTC
    > but instead do:

    >  my $a, $x, $y, $z = foo()

    The precedence of = is higher than , !

    Resulting evaluation:

    (my $a), ($x), ($y),  ($z = foo())

    That's effectively the same like 4 separate statements.

    my $a ; $x ; $y ; $z = foo() ;

    Consequences

    1. => scalar assignment to $z and scalar context for foo() call

    2. => my declaration only for $a

    Update

    3. => $x and $y are undeclared and in void context

    4. => strict will fail

    In short: DON'T!!!

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

      What about
      my $a=$x=$y=$z = foo()
      Here my attribute does not propogate like in case of comma. It applies only to $a, right ?

      But now the fun starts

      DB<3> use v5.10
      
      DB<4> my $a=$x=$y=$z = 1
      
      DB<5> say "|$a|$x|$y|$z|"
      ||1|1|1|  
      DB<6> unless( defined($a) ){ say "a is undefined"}
      a is undefined
      
      why $a remains uninitialized?

        The debugger is a bad place to play with scoping like this. In effect when you evaluate single lines like this they're more like doing an eval within the scope of the program (more or less; I'm sure someone more familiar with the perl5db could give more specifics). It's kind of like (handwaving) textually shimming in say DebugDump( eval { YOURTEXTHERE } ) into wherever you're looking at and seeing the result.

        This means that your my declaration is happening inside of a transient scope (that single eval statement) and then it's going away. Since the my was affecting only $a when you check for defined-ness it fails because the package $a wasn't defined (however your modifications to $x et al changes the package versions of those and the values do persist after the statement).

        $ cat test.pl use 5.032; my $foo = 10; say qq{foo: $foo} $ perl -d test.pl Loading DB routines from perl5db.pl version 1.57 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. main::(test.pl:2): my $foo = 10; DB<1> x $foo 0 undef DB<2> n main::(test.pl:3): say qq{foo: $foo} DB<2> x $foo 0 10 DB<3> my $foo = 20 DB<4> x $foo 0 10 DB<5> my $foo = 20; say qq{foo: $foo} foo: 20 DB<6> x $foo 0 10

        Simple rule of thumb I tend to follow is just don't use my (or state or our) from the debugger command line to try and affect anything outside of that immediate command line.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        What about my $a=$x=$y=$z = foo()
        $ perl -MO=Deparse,-p -e 'my $a=$x=$y=$z = foo()' (my $a = ($x = ($y = ($z = foo()))));

        The comma is left associative and the assignment is right associative. See perlop.

        Here my attribute does not propogate like in case of comma.

        No, it doesn't "propagate" with the comma either, as I showed.

        That's not what I get nor what I would expect (but this is not in debug mode):

        Win8 Strawberry 5.8.9.5 (32) Sun 10/11/2020 21:47:54 C:\@Work\Perl\monks >perl -Mwarnings my $a=$x=$y=$z = 1; print "|$a|$x|$y|$z|"; ^Z |1|1|1|1| Win8 Strawberry 5.30.3.1 (64) Sun 10/11/2020 21:45:55 C:\@Work\Perl\monks >perl -Mwarnings use v5.10; my $a=$x=$y=$z = 1; say "|$a|$x|$y|$z|"; ^Z |1|1|1|1|

        Update:

        What about

        my $a=$x=$y=$z = foo()
        With respect to this specific statement:
        Win8 Strawberry 5.8.9.5 (32) Sun 10/11/2020 21:48:09 C:\@Work\Perl\monks >perl -Mstrict -Mwarnings use Data::Dump qw(dd); sub foo { return 9, 8, 7; } my ($x, $y, $z); my $a = $x = $y = $z = foo; dd $a, $x, $y, $z; ^Z (7, 7, 7, 7)
        Same result under version 5.30.3.1.


        Give a man a fish:  <%-{-{-{-<

Re: What's happening in this expression?
by jwkrahn (Monsignor) on Oct 12, 2020 at 06:09 UTC

    In Perl, as in the C programming language, statements are separated with a semicolon. There are also times where statements can be separated with a comma, mostly used in a for ( i = 1, j = 10; i < 10; ++i, j += 4 ) { ... } expression.

    In your case you have four separate statements, separated by commas instead of semicolons: i.e. my $a; $x; $y; $z = foo().

Re: What's happening in this expression?
by zapdos (Sexton) on Oct 11, 2020 at 13:47 UTC
    Thank you very much haukex, perlfan and LanX.
Re: What's happening in this expression?
by perlfan (Vicar) on Oct 11, 2020 at 09:55 UTC
    perl -e 'sub foo {return (1,2,3,4)}; my $a, $x, $y, $z = foo(); print +qq{$a, $x, $y, $z\n}' , , , 4
    perl -e 'sub foo {return (1,2,3,4)}; my ($a, $x, $y, $z) = foo(); prin +t qq{$a, $x, $y, $z\n}' 1, 2, 3, 4
      return (1,2,3,4)

      Protip: Don't use numbers like this to debug lists and arrays, because it makes it impossible to tell the difference between a list returning its last value and an array returning its size.

        Yes, that's why I always use characters ... but there is another trap lurking ...

        Extra tip: never use the range operator to return a list.

        One of the most annoying design flaws of Perl is the propagation of context to a subs returning statement. That's action at a distance...

        And you really don't want a flip-flop when you expect a list...

        DB<50> sub tst { "a".."d" } DB<51> x tst() # list context => range 0 'a' 1 'b' 2 'c' 3 'd' DB<52> p scalar tst() # scalar context => flip-flop 1E0 DB<53> x tst() # list context => WTF??? 0 0 DB<54> x tst() # once flip-flop, always flip-flop 0 0 DB<55>

        Workaround: reverse

        DB<44> sub tst { reverse "a".."d" } DB<45> x tst() 0 'd' 1 'c' 2 'b' 3 'a' DB<46> p scalar tst() dcba DB<47> x tst() 0 'd' 1 'c' 2 'b' 3 'a' DB<48>

        And if don't like the order, reverse twice

        DB<55> sub tst { reverse reverse "a".."d" }

        update

        another - uglier - alternative:

        DB<59> sub tst { @{["a".."d"]} } DB<60> x tst() 0 'a' 1 'b' 2 'c' 3 'd' DB<61> p scalar tst() 4 DB<62> x tst() 0 'a' 1 'b' 2 'c' 3 'd' DB<63>

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (2)
As of 2023-03-22 03:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Which type of climate do you prefer to live in?






    Results (60 votes). Check out past polls.

    Notices?