Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Strange aliasing(?) when modifying variable in range operator

by vr (Chaplain)
on Jun 09, 2018 at 16:50 UTC ( #1216254=perlquestion: print w/replies, xml ) Need Help??
vr has asked for the wisdom of the Perl Monks concerning the following question:

It's not the issue with order of evaluation, but, looks like, some aliasing is going on, when same variable is used on left and right sides, and modified on the right side.

I was trying to be "clever", slicing array and sliding window at the same time, like this:

>perl -wE "@a=1..9; $i=0; say @a[$i..($i+=3)-1] while $i<@a #1"

No ouput.. Blank lines, but not the output I expected (123\n456\n789\n). Then further:

>perl -wE "$i=1; say for $i..$i++ #2" >perl -wE "$i=1; say for $i..++$i #3" 2 >perl -wE "$i=1; say for do{$i}..do{$i++} #4" >perl -wE "$i=1; say for do{say 'hi!';$i}..do{say 'bye!';$i++} #5" hi! bye! 1 >perl -wE "$i=1; say for do{$i?$i:($i+1)}..$i++ #6" >perl -wE "$i=1; say for do{$i?$i:($i++)}..$i++ #7" 1

Maybe I'm wrong and it's not a bug, and there's some logical explanation? Last examples are frightening.

Edit. Corrected the output I expected (thanks, Marshall). Added line numbers to one-liners to refer to them.

Edit 2. But what about the do returning either value (#5, #7) or alias (#4, #6)?

do:

do BLOCK

...Returns the value of the last command in the sequence of commands indicated by BLOCK.

Replies are listed 'Best First'.
Re: Strange aliasing(?) when modifying variable in range operator
by LanX (Archbishop) on Jun 09, 2018 at 17:28 UTC
    > there's some logical explanation?

    Well ... implementation details and an explicit warning not to mess with side-effects of multiple increments in the same statement.*

    When calling a sub simple arguments are passed by reference which can be modified (you could call this alias) but the result of increments are passed as values .

    The increments happen from left to right on the same instance, but will influence the aliases inside the sub.

    DB<17> sub tst { say "@_" } DB<18> $i=0;tst $i,$i,++$i # last increment operates on fir +st two aliases 1 1 1 DB<19> $i=0;tst $i,$i,$i++ # again, increment returns value + at evaluation time 1 1 0 DB<20> $i=0;tst $i++,$i,$i++ # increments return value, but i +nfluence alias in the middle 0 2 1

    Does it explain your results?

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

    *) from perlop#Auto-increment-and-Auto-decrement

    Note that just as in C, Perl doesn't define when the variable is incremented or decremented. You just know it will be done sometime before or after the value is returned. This also means that modifying a variable twice in the same statement will lead to undefined behavior. Avoid statements like:
    $i = $i ++; print ++ $i + $i ++;
    Perl will not guarantee what the result of the above statements is.

    update

    sorry I just notice that you didn't have multiple increments in the same statement, but the explanation with call by reference and value still holds:

    >perl -wE "$i=1; say for $i..$i++" # 2 .. 1 (first is alias +, postinc returns 1 ) >perl -wE "$i=1; say for $i..++$i" # 2 .. 2 (first is alias +, preinc returns 2 ) 2 ...

    update

    ) but already mixing $i and $i++ in the same statement is discouraged!

      Thank you for answer, Lanx, but range operator is not a "call" (to function), is it? And operators aren't (shouldn't be(?)) executed before arguments are evaluated?

      Hm-m, I'm wrong. Back to school...

      >perl -wE "$i=1; say $i + ($i+=1)" 4
        You're welcome! :)

        NB: You have no guaranty at all when INSIDE the statement the increment happens, it's only guarantied AFTER the statement.

        Most probably is Perl doing internal optimizations here.

        Your do-block examples puzzle me, please note the difference between debugger and one-liner

        DB<1> $i=1; say for do{$i?$i:($i+1)}..$i++ #6 1 DB<2> q D:\Users\lanx>perl -wE "$i=1; say for do{$i?$i:($i+1)}..$i++ #6" D:\Users\lanx>

        again, unpredictable internal optimization.

        If interested, you could use B::Concise to track what is happening here...

        So Rule Of Thumb:

        NEVER mix $i and $i++ (or --$i) inside the same statement.

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

Re: Strange aliasing(?) when modifying variable in range operator
by Marshall (Abbot) on Jun 09, 2018 at 17:15 UTC
    Before writing a one-liner, try writing the code as a .pl file with
    use strict; use warnings;
    Your code actually does give an output:
    C:\....\Projects>perl -wE "@a=1..9; $i=0; say @a[$i..($i+=3)-1] while +$i<@a" C:\....\Projects>perl>
    Those blank lines are "output".
Re: Strange aliasing(?) when modifying variable in range operator
by ikegami (Pope) on Jun 11, 2018 at 07:24 UTC

    You are reading and modifying the same variable in the same expression where order of operand evaluation is not provided. As such, the result is undefined.

    That said, for all existing versions of Perl, you'll get the same results, so we can explain what happens even if you can't count on it.

    The key to understanding what happens is that $i puts $i on the stack. Not a copy of it, but $i itself. So $i..($i+=3)-1 is equivalent to $i+3 .. ($i+=3)-1, which will always be an empty range.

    The following recreates the issue. If you add debug statements, you'll see range receiving arguments 7 and 6.

    use feature qw( say refaliasing ); no warnings qw( experimental::refaliasing ); my @stack; sub padsv { \$stack[@stack] = \$_[0]; } sub const { \$stack[@stack] = \$_[0]; } sub add_equal { \my $rhs = \pop(@stack); \my $lhs = \pop(@stack); \my $rv = \( $lhs += $rhs ); \$stack[@stack] = \$rv } sub add { \my $rhs = \pop(@stack); \my $lhs = \pop(@stack); \my $rv = \( $lhs + $rhs ); \$stack[@stack] = \$rv } sub minus { \my $rhs = \pop(@stack); \my $lhs = \pop(@stack); \my $rv = \( $lhs - $rhs ); \$stack[@stack] = \$rv } sub range { \my $rhs = \pop(@stack); \my $lhs = \pop(@stack); #say "$lhs..$rhs"; \$stack[@stack] = \$_ for $lhs .. $rhs; } my $i = 4; # $i @stack # -- ------- padsv($i); # 4 $i padsv($i); # 4 $i,$i const(3); # 4 $i,$i,3 add_equal(); # 7 $i,7 const(1); # 7 $i,7,1 minus(); # 7 $i,6 range(); # 7 - say join ', ', @stack; # ""

    In comparison, $i+0 .. ($i+=3)-1:

    my $i = 4; # $i @stack # -- ------- padsv($i); # 4 $i const(0); # 4 $i,0 add(); # 4 4 padsv($i); # 4 4,$i const(3); # 4 4,$i,3 plus_equal(); # 7 4,7 const(1); # 7 4,7,1 minus(); # 7 4,6 range(); # 7 4,5,6 say join ', ', @stack; # "4, 5, 6"
Re: Strange aliasing(?) when modifying variable in range operator
by LanX (Archbishop) on Jun 09, 2018 at 18:39 UTC

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (7)
As of 2018-12-17 09:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How many stories does it take before you've heard them all?







    Results (72 votes). Check out past polls.

    Notices?
    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!