this should work

Discussion in 'Perl Misc' started by George Mpouras, Jul 10, 2013.

  1. foreach my $dir (qw/commands_pre commands_post/) {
    $dir = "/tmp/$dir";
    print "$dir\n"
    }

    # Modification of a read-only value attempted at ./test line 139
     
    George Mpouras, Jul 10, 2013
    #1
    1. Advertisements

  2. George Mpouras

    Jim Gibson Guest

    Because of the aliasing feature of foreach loops, you are trying to
    modify the elements of qw/commands_pre commands_post/, which are not
    lvalues. Try this instead:

    #!/usr/bin/perl
    use strict;
    use warnings;

    foreach my $dir (qw/commands_pre commands_post/) {
    my $tmpdir = "/tmp/$dir";
    print "$tmpdir\n"
    }

    See 'perldoc perlsyn' and the section entitled 'Foreach Loops':

    "LABEL foreach VAR (LIST) BLOCK"

    "If any element of LIST is an lvalue, you can modify it by modifying VAR
    inside the loop. Conversely, if any element of LIST is NOT an lvalue,
    any attempt to modify that element will fail. In other words, the
    'foreach' loop index variable is an implicit alias for each item in the
    list that you're looping over."
     
    Jim Gibson, Jul 10, 2013
    #2
    1. Advertisements

  3. it is a perl bug or bad behavior , should change.
     
    George Mpouras, Jul 11, 2013
    #3
  4. George Mpouras

    Tim McDaniel Guest

    I don't like it -- it's an "action at a distance spookiness" effect
    that I've found little use for. I just work around it.

    You have just had the documentation quoted and explained. It's a
    long-standing aspect of Perl. I call something a "bug" when it's a
    result that does not match the documentation, so you have no right to
    call it a "perl bug".

    "Misfeature" or "bad design": aliasing something to each item in turn,
    where changing the variable changes the underlying thing, is
    documented in several areas and decades old. If you don't like it, it
    has an immediate and obvious workaround: assign it to a temporary.

    TL;DR: Quit whining. Learn when aliasing happens and don't change
    aliased variables (unless you have a need for it).
     
    Tim McDaniel, Jul 11, 2013
    #4
  5. [...]
    The perl compiler doesn't do invariant code motion because whether or
    not some code is 'invariant' cannot generally be decided at compile
    time. Because of this, the loop body above behave exactly as written
    down: For each iteration, it creates a new my variable and assigns a
    value to it. Unless there's a specific reason why this behaviour would
    be desirable, such constructs should be avoided[*].

    [*] This is also true for the more general case of 'Yes we can!'
    variable declarations: Just because a new my variable can be created
    as part of some construct doesn't mean there's a good reason why it
    should be created, not only because this takes time but more
    importantly, because it makes the code more complicated, especially as
    real world code isn't going to have nice and small two lines blocks
    but rather big and ugly 500 lines blocks with 5 hundred lines blocks
    inside and each of these further subdivided into dozens of line
    blocks (this is probably still and optimistic estimate). This makes
    for extremely 'enjoyable' variable hunting ...
     
    Rainer Weikusat, Jul 11, 2013
    #5
  6. _WHAT_ is a bug or bad behaviour in the Perl interpreter?
    Please quote sufficient context such that your postings are meaningful.

    jue
     
    Jürgen Exner, Jul 11, 2013
    #6
  7. Στις 11/7/2013 13:34, ο/η Jürgen Exner έγÏαψε:

    Perl is for humans so its behavior should be the expected if there is
    not a serious reason not to be
     
    George Mpouras, Jul 11, 2013
    #7
  8. George Mpouras <>
    writes:

    [ foreach is aliasing ]
    I can't see any serious reason for Perl not to work as documented and
    designed. The feature is useful and well-documented and absolutely not a
    bug.

    //Makholm
     
    Peter Makholm, Jul 11, 2013
    #8
  9. the behavior is different for an @array and a hardcoded list
     
    George Mpouras, Jul 11, 2013
    #9
  10. In this case, this would be transforming the

    for (...) {
    my $tmp = 'ar!';
    }

    to

    my $tmp;
    for (...) {
    $tmp = 'ar!';
    }

    because the 'my $tmp' is invariant code: It's effective result never
    changes throughout the loop.
    Maybe some versions of Perl do that (which would be an
    improvement). But the one I tested certainly doesn't. Assuming the
    following code

    -------------
    use Benchmark;

    sub in_loop
    {
    for (0 .. 100) {
    my $a = $_ + 1;
    }
    }

    sub out_of_loop
    {
    my $a;
    for (0 .. 100) {
    $a = $_ + 1
    }
    }

    timethese(-2,
    {
    in_loop => \&in_loop,
    out_of_loop => \&out_of_loop
    });
    -------------

    the loop in in_loop is translated to (perl -MO=Concise,in_loop, perl
    5.10.1)

    e <0> iter s ->f
    - <@> lineseq sK ->-
    7 <;> nextstate(main 596 a.pl:6) v ->8
    c <2> sassign vKS/2 ->d
    a <2> add[t5] sK/2 ->b
    - <1> ex-rv2sv sK/1 ->9
    8 <#> gvsv[*_] s ->9
    9 <$> const[IV 1] s ->a
    b <0> padsv[$a:596,597] sRM*/LVINTRO ->c
    d <0> unstack s ->e

    is what creates the variable.

    For out_of_loop, this looks like this:

    e <0> iter s ->f
    - <@> lineseq sK ->-
    9 <;> nextstate(main 601 a.pl:14) v ->a
    c <2> add[$a:600,603] sK/TARGMY,2 ->d
    - <1> ex-rv2sv sK/1 ->b
    a <#> gvsv[*_] s ->b
    b <$> const[IV 1] s ->c
    d <0> unstack s ->e

    and the padsv ... LVINTRO happens in the subroutine preamble.

    Running the program here yields the expected result that out_of_loop
    executes at about 1.43 times the speed of in_loop.
    I speak for code written by people other than me I happen to know. I'm
    completely willing to believe that you either don't know any code
    written by other people or only code written by other people who are
    above-average skilled programmers and that you're one yourself,
    however, that doesn't change this observation.
     
    Rainer Weikusat, Jul 11, 2013
    #10
  11. Yes, for the same reason that the behaviour for $x[1] = 5 is different
    than 2 = 5: You can assign a value to an array element, but you can't
    assign a value to a constant.

    After
    my @x = (1, 2, 3);
    for (@x) {
    $_ = 5;
    }
    @x has the value (5, 5, 5), so it's basically the same as:
    $x[0] = 5;
    $x[1] = 5;
    $x[2] = 5;

    Now consider the equivalent with constants:
    for (1, 2, 3) {
    $_ = 5;
    }
    That would be the same as:
    1 = 5;
    2 = 5;
    3 = 5;
    Oops!

    hp
     
    Peter J. Holzer, Jul 11, 2013
    #11
  12. ok but this is a tautology

    for (1, 2, 3) {
    $_ = 5;
    }

    should NOT be the same as:

    1 = 5;
    2 = 5;
    3 = 5


    never mind , some things never change
     
    George Mpouras, Jul 11, 2013
    #12
  13. [so-called 'aliasing in for loops']
    I think a discrepancy between your 'mental model' of 'Perl execution'
    and the way it actually works exists here: In perl, values are always
    represented as SVs ('scalars') and a SV is a pretty complex object,
    copying of which may be 'expensive', eg, the following perl code:

    ---
    use Devel::peek;

    my $s = 'string';
    my $ss = $s;

    Dump($s);
    Dump($ss);
    ---

    causes a the string assigned to s to be copied to a newly, dynamically
    allocated area of memory, as can be seen in the output generated when
    running this code:

    ---
    SV = PV(0x603b78) at 0x621188
    REFCNT = 1
    FLAGS = (PADMY,POK,pPOK)
    PV = 0x61b9f0 "string"\0
    CUR = 6
    LEN = 8
    SV = PV(0x603c38) at 0x6211b8
    REFCNT = 1
    FLAGS = (PADMY,POK,pPOK)
    PV = 0x617a10 "string"\0
    CUR = 6
    LEN = 8
    ---

    Because an SV is not really a 'value' in the sense of, say, a C
    integer or pointer, it is always passed 'by reference' into
    syntactical constructs which work with 'lists of SVs' such as map { },
    for ( ) { } or subroutine calls and it is up to the user to copy the
    'argument SV' in case an independently modifiable copy of the original
    thing is actually needed for some reason (perl supports, supported or
    was supposed to support COW-sharing of 'SV string bodies' in some
    circumstance or at some point in the in the past, but except 'somebody
    worked on that in the past', I don't really know any details about
    that). That's similar to the way Java handles this where complex
    objects are always passed 'by reference', just that Java also has
    'primitive types' which are passed by value and Perl doesn't.
     
    Rainer Weikusat, Jul 11, 2013
    #13
  14. Indeed: Perl still represents all values as SVs internally and this
    means the literal numbers exist in your source code but in the
    compiled code, they're just used to initialize read-only SVs which are
    passed by reference. That's something one just needs to be aware of
    when using Perl.
     
    Rainer Weikusat, Jul 11, 2013
    #14
  15. Copy-on-Write strings was supposed to be enabled by default for Perl
    5.18, but it broke a significant number of XS modules. It can be enabled
    by running Configer with '-Accflags=-DPERL_NEW_COPY_ON_WRITE' when
    building perl.

    I believe it is enabled in perl 5.19.

    //Makholm
     
    Peter Makholm, Jul 11, 2013
    #15
  16. It doesn't because the first thing which happens in the loop body is
    an assignment overwriting the old value.

    [...]
    Let me but it this way:

    -------
    #include <stdio.h>
    #include <stdlib.h>

    int main(void)
    {
    char *p;

    p = malloc(8);
    printf("%p\n", p);
    free(p);

    p = malloc(8);
    printf("%p\n", p);
    free(p);
    return 0;
    }
    -------

    This should usually print the same value twice despite, as can be
    checked with ltrace (on Linux), it actually calls malloc and free
    twice, IOW, that two entities which exists at two different times
    happen to have the same address doesn't necessarily mean anything
    except that they happen to have the same address.




    No, it's not. It's a completely valid example of the difference
    between '[needlessly] putting my into the loop' and 'my outside of the
    loop':

    [...]
    I have no doubt that you will be able to find a way to transform the
    code such that the difference I was writing about becomes unmeasurable
    because it is tiny compared to the time 'accidental operations' also
    performed in the loop need. However, 'a sub call' is not yet good
    enough. But I see little reason to climb this ladder: Nobody ever
    changed his opinion just because it was demonstrably wrong, only the
    pseudo-arguments supposed to make it appear as the rational choice
    nevertheless become more complicated.
     
    Rainer Weikusat, Jul 11, 2013
    #16
  17. Are you saying that Perl should have caught your error but failed to
    What about if an error occure while perl is trying correctly to catch the
    error and this fail ;
    Joking, Perl correctly catches the error (of course it is not correct, that
    this error correctly occured)
     
    George Mpouras, Jul 11, 2013
    #17
  18. [...]
    Just in case there's any doubt about that: Ben is - of course - right
    in claiming that Perl doesn't really create a new variable if the
    previously created one is still available for reuse, it just puts that
    back into a 'virgin' state. Which is still more work then when the
    variable has been created outside of the loop. Also, it is possible to
    write code such that it exploits other side-effects of the 'new
    variable perl loop' to make the difference more accentuated eg

    -------------
    use Benchmark;

    sub three()
    {
    return "three";
    }

    sub in_loop
    {
    for (0 .. 100) {
    my $a;
    $a = three() if $_ & 1;
    }
    }

    sub out_of_loop
    {
    my $a;
    for (0 .. 100) {
    $a = three() if $_ & 1;
    }
    }

    timethese(-2,
    {
    in_loop => \&in_loop,
    out_of_loop => \&out_of_loop
    });
    -------------

    and it is - of course - also possible to 'enhance' perl to work around
    stupidly written code harder in order to catch this as well. But
    making the compiler work harder in order to hide someone's mistakes
    better is still not exactly what I want to have given that the amount
    of work the compiler performs is also a cost wich affects me.
     
    Rainer Weikusat, Jul 11, 2013
    #18
  19. George Mpouras

    Keith Keller Guest

    This strikes me as premature optimization. Unless I know my program is
    spending too much time doing my $tmp = 'ar!'; inside the for loop, I
    would much prefer to keep $tmp properly scoped and initialized inside
    the loop.

    --keith
     
    Keith Keller, Jul 11, 2013
    #19
  20. How very true!!!
    CPU-time is cheap, programmers time is expensive.

    jue
     
    Jürgen Exner, Jul 11, 2013
    #20
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.