this should work

G

George Mpouras

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
 
J

Jim Gibson

George Mpouras said:
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

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."
 
T

Tim McDaniel

it is a perl bug or bad behavior , should change.

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).
 
R

Rainer Weikusat

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

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 ...
 
J

Jürgen Exner

George Mpouras said:
it is a perl bug or bad behavior , should change.

_WHAT_ is a bug or bad behaviour in the Perl interpreter?
Please quote sufficient context such that your postings are meaningful.

jue
 
G

George Mpouras

Στις 11/7/2013 13:34, ο/η Jürgen Exner έγÏαψε:
_WHAT_ is a bug or bad behaviour in the Perl interpreter?
Please quote sufficient context such that your postings are meaningful.

jue


Perl is for humans so its behavior should be the expected if there is
not a serious reason not to be
 
P

Peter Makholm

George Mpouras <[email protected]>
writes:

[ foreach is aliasing ]
Perl is for humans so its behavior should be the expected if there is
not a serious reason not to be

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
 
R

Rainer Weikusat

Ben Morrow said:
Quoth Rainer Weikusat said:
[...]
foreach my $dir (qw/commands_pre commands_post/) {
my $tmpdir = "/tmp/$dir";
print "$tmpdir\n"
}

The perl compiler doesn't do invariant code motion because whether or
not some code is 'invariant' cannot generally be decided at compile
time.

I don't know what you mean by that, but...

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.
...this is nonsense. Perl quite deliberately reuses the same variable
every time, to avoid the allocation overhead, unless you pass a (strong)
reference to it outside the loop. Try it and see:

use Scalar::Util qw/refaddr/;

for (1, 2, 3) {
my $tmp = "foo/$_";
say refaddr \$tmp;
}

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.
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).

Speak for yourself. *My* real-world code doesn't look like that.

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.
 
P

Peter J. Holzer

the behavior is different for an @array and a hardcoded list

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
 
G

George Mpouras

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
 
R

Rainer Weikusat

George Mpouras <[email protected]> wrote:

[so-called 'aliasing in for loops']
it is a perl bug or bad behavior , should change.

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.
[...]

"Misfeature" or "bad design":

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.
 
R

Rainer Weikusat

George Mpouras said:
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

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.
 
P

Peter Makholm

Rainer Weikusat said:
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).

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
 
R

Rainer Weikusat

Ben Morrow said:
Ben Morrow said:
Quoth Rainer Weikusat <[email protected]>:

[...]

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

The perl compiler doesn't do invariant code motion because whether or
not some code is 'invariant' cannot generally be decided at compile
time.

I don't know what you mean by that, but...

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.

But it does: logically, you get a different variable each time. That
transformation changes the result, since the value of $tmp will persist
from one iteration to the next (as well as being visible below the
loop).

It doesn't because the first thing which happens in the loop body is
an assignment overwriting the old value.

[...]
Did you actually run that bit of code? With 'say' replaced with 'print',
it produces the same refaddr three times on every version perl I have
available (back to 5.6.0).

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.


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.


Ah yes, that's a *different* bit of cheating (on perl's part).


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':

[...]
in the specific case of a builtin operator whose result is
assigned directly to an already-declared lexical, perl will optimise by
having the operator store its result directly in the lexical, rather
than using a temporary.
[...]

Run your benchmark again with a sub call or something instead of the
addition, and I doubt you'll see any difference between the two.

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.
 
G

George Mpouras

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)
 
R

Rainer Weikusat

[...]
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

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.
 
K

Keith Keller

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.

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
 
J

Jürgen Exner

Keith Keller said:
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.

How very true!!!
CPU-time is cheap, programmers time is expensive.

jue
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,013
Latest member
KatriceSwa

Latest Threads

Top