Syntax understanding problem

J

Justin C

I've just found something in code I wrote, but I don't
understand it and don't know where I found it. I wrote it a long
time ago. I have this line:

($discard = 0, next) if /^Relayed messages/;

I was tweaking the program for a issue I had (that turns out to
have been elsewhere), and changed the above to:

$discard = 0 if /^Relayed messages/;

At a later stage I had to revert to the original behaviour and
put this:

($discard = 0 && next) if /^Relayed messages/;

Which didn't do what I wanted!

Where can I read about this behaviour of the comma? And what is
that && doing in the last version, am I really saying ($discard
= 0) and ($discard = next)?


Justin.
 
G

George Mpouras

you are using a list but instead of data, its items is executed code.
More examples

my
@array;
@array = ( print 12 , 1==1 ? 'foo' : 'boo' ) if 2==2;
@array = qw/a b/ if 2==0;

use Data::Dumper; print Dumper \@array
 
J

Jürgen Exner

Justin C said:
I've just found something in code I wrote, but I don't
understand it

That's not surpising. You are (mis?)-using data structures as control
flow elements. That is at the very least a rather questionable habit.
and don't know where I found it. I wrote it a long
time ago. I have this line:

($discard = 0, next) if /^Relayed messages/;

If the condition is true, then you are creating a temporary list that is
never used. This list has 2 elements, first the value 0, and second the
return value of next. As a side effect while calculating the first value
it also sets $discard to 0. And of course 'next' never returns, so it
does not really have a valid return value.
I was tweaking the program for a issue I had (that turns out to
have been elsewhere), and changed the above to:

$discard = 0 if /^Relayed messages/;

Well, this version obviously never jumps to the next incarnation of
whatever enclosing loop you got.
At a later stage I had to revert to the original behaviour and
put this:

($discard = 0 && next) if /^Relayed messages/;

Which didn't do what I wanted!

Not surprising that's something completely different. Here you got a
list with only one element. And this element is calculated as the result
of the assignment of the expression (0 && next) to $discard because &&
has a higher precedence than the assignment.
0 is logical 'false', therefore the && short-circuits and never
evaluates the right side 'next' but instead returns 'false' immediately
and that's what gets assigned to $discard. And then the code continues
with the next statement.
Where can I read about this behaviour of the comma? And what is
that && doing in the last version, am I really saying ($discard
= 0) and ($discard = next)?

No, why would you think so? You are saying
($discard = (0 && next)) if /^Relayed messages/;
just as if you were writing
($foo = 4 + 9) if /^Relayed messages/;

jue
 
T

Tony Mountifield

I've just found something in code I wrote, but I don't
understand it and don't know where I found it. I wrote it a long
time ago. I have this line:

($discard = 0, next) if /^Relayed messages/;

This is just a cryptic perlish way of saying this:

if (/^Relayed messages/) {
$discard = 0;
next;
}

It was just a way of dispensing with {} and making it a one-liner,
which some people consider cool. (I'm not commenting on that opinion).
I was tweaking the program for a issue I had (that turns out to
have been elsewhere), and changed the above to:

$discard = 0 if /^Relayed messages/;

At a later stage I had to revert to the original behaviour and
put this:

($discard = 0 && next) if /^Relayed messages/;

Which didn't do what I wanted!

Where can I read about this behaviour of the comma? And what is
that && doing in the last version, am I really saying ($discard
= 0) and ($discard = next)?

$ perldoc perlop

Cheers
Tony
 
J

Jürgen Exner

Ben Morrow said:
instead this is 'the scalar comma operator', which is considered
reasonable to use to sequence side-effects.

This I dare say is highly debatable. Just because it is possible to use
data structures to control execution flow doesn't mean it is a good idea
to do so. The OPs confusion is actually a good example why it is a
pretty darn stupid idea.

jue
 
R

Rainer Weikusat

This is just a cryptic perlish way of saying this:

if (/^Relayed messages/) {
$discard = 0;
next;
}

It was just a way of dispensing with {} and making it a one-liner,
which some people consider cool. (I'm not commenting on that opinion).

It should be noted that this is your opinion about people who use
certain syntactic constructs (they're irrational and do bad things
because of that), camouflaged as asessement of the "deep, inner
motivations of someone who 'does something you'd never do yourself'" and
- completely unsurprisingly - you came to the conclusion that "they're
probably mad" (since 'drunk' won't suffice here).

$discard = 0, next if /^Relayed messages/;

is another way of writing

/^Related messages/ and $discard = 0, next;

That's an expression using the short-ciruiting behaviour of the boolean
operators for flow control, a possibility which has existed at least
since C, except that Perl provides an alternate set of these operators
whose precedence makes them more suitable for this use and an alternate
syntax someone presumably considered to be clearer than using the
operators. AFAIK, statement modifiers are indeed unique to Perl but
different programming languages actually differ and decrying them as
'cryptic' because they do make little sense ("You're all full of shit
and everything someone who has learnt Math doesn't understand ought to be
banned!" is an old Xah-Lee chestnut, pax hibiscus ...)
 
J

Jürgen Exner

Rainer Weikusat said:
is another way of writing

/^Related messages/ and $discard = 0, next;

That's an expression using the short-ciruiting behaviour of the boolean
operators for flow control, [...]

And exactly for this reason it is just as insane.

Just do not mix code (control flow) and data (expressions).

jue
 
J

Jürgen Exner

Ben Morrow said:
The code in question was not a 'data structure', it was the comma
operator in scalar (or, actually, void) context. The sole purpose of the

The code in question is an expression. The purpose of an expression is
to manipulate date, not to control execution flow.
behaviour of this operator is to allow this sort of sequencing of
side-effects within a statement, just like the equivalent operator in C.

I never said that side-effects in statements that are used for control
flow were a good idea in C, either.
Justin's confusion is more an example of why it is a bad idea to copy
code you don't entirely understand (sorry Justin) [...]

Well, that's a given, isn't it?

jue
 
T

Tim McDaniel

The purpose of an expression is
to manipulate data, not to control execution flow.

assert($args->{FLEX}, "Must say how to flex!");
$count = $args->{COUNT} || 1;
$source = $args->{ISUSER} ? $local : $global;
open my $log, '>>', '/home/fred/debug' or die;
 
T

Tim McDaniel

Apart from that, the scalar comma operator is explicitly intended for
control flow, just like 'and' and 'if' (and unlike '&&' and '?:').

I see "and", "&&", and "?:" to be entirely equal: one thing is
evaluated, and depending on the result, some other thing(s) may be
evaluated, whatever the precedence (I consider requiting parentheses
or no to be an inessential feature).

As to whether you call it "control flow", it depend on your
definition. I can think of several possible definitions:
- the locus of execution moves outside the statement: next, last,
return, ...
- the locus of execution may skip or repeat the same statement, which
is those plus the postfix statement modifiers
- the flow of execution causes code to be skipped, but may remain in
the same expression, being those plus: and or && || ?:
I suppose one definition might be more useful than another depending
on the discussion, so I'd define the term or avoid it.
 
J

Justin C

Justin's confusion is more an example of why it is a bad idea to copy
code you don't entirely understand (sorry Justin) than of anything wrong
with the construction itself.

No problem, Ben. I must have been under the impression that I
understood it at the time I used it, but my understanding must
have slipped since. I wouldn't just copy something I saw on the
web. It was probably one of those "oh, that's clever" moments
where I think "I must remember that, it'll save me..." and then
promptly forget it.

I'll probably replace it with Rainer's variation:

/^Relayed message/ and $discard = 0, next;

because this is clearer to me.

The confusing part, for me, in perldoc perlop WRT the comma
operator, is ``In scalar context it evaluates its left argument,
throws that value away, then evaluates its right argument and
returns that value''. I can understand the ``throws ... away''
if I had:

/^match/ and call_a_sub(foo), next;

the return of call_a_sub would be discarded, but the assignment
in my code isn't discarded.

You know, by the time I get back to work, after Christmas, I
may just do:

if (/^Relayed message/) {
$discard = 0;
next;
}

Depends if it's still clear to me in the new year.

Thank you all for the discussion.


Justin.
 
T

Tim McDaniel

/^Relayed message/ and $discard = 0, next; ....
The confusing part, for me, in perldoc perlop WRT the comma
operator, is ``In scalar context it evaluates its left argument,
throws that value away, then evaluates its right argument and
returns that value''. I can understand the ``throws ... away''
if I had:

/^match/ and call_a_sub(foo), next;

the return of call_a_sub would be discarded, but the assignment
in my code isn't discarded.

It doesn't say "ignores the left argument completely"; in contrast, it
says that it "evaluates" it. It says that it "throws that value
away", "that value" being the return value of the evaluation. In
/^match/ and call_a_sub(foo), next;

it still calls call_a_sub, and any global side effects from the sub
remain (opening or closing files, setting global variables,
whatever). The only thing discarded is the return value.

An assignment statement returns a value:

$ perl -e 'print ($x = 23, "\n")'
23

So in
/^Relayed message/ and $discard = 0, next;

the assignment gets done and $discard is set to 0. It's just that the
0 return value coming out of the assignment is discarded.
 
R

Rainer Weikusat

It doesn't say "ignores the left argument completely"; in contrast, it
says that it "evaluates" it. It says that it "throws that value
away", "that value" being the return value of the evaluation. In


it still calls call_a_sub, and any global side effects from the sub
remain (opening or closing files, setting global variables,
whatever). The only thing discarded is the return value.

It might be easier to understand this when knowing the implementation:
Internally, a list is represented as an OP_LIST (corresponding with
pp_list) whose arguments are the elements of the list (Except when the
list is an argument list for another list operator. In this case, the
OP_LIST/ pp_list is elided). Implemented in Perl, the 'list operator'
would look like this:

sub list
{
return wantarray() ? @_ : $_[$#_];
}

which means the

/^Relayed message/ and $discard = 0, next;

is really something like this:

/^Relayed messages/ and list($discard = 0, next); # [*]

[*] Loop control is indeed allowed in 'argument lists', eg

print("$_\n", ($_ & 1 and next) || '') for 1 .. 10;

is a seriously bizarre way to print all even numbers from (1,10)
 
C

Charles DeRykus

...

it still calls call_a_sub, and any global side effects from the sub
remain (opening or closing files, setting global variables,
whatever). The only thing discarded is the return value.

An assignment statement returns a value:

$ perl -e 'print ($x = 23, "\n")'
23

So in


the assignment gets done and $discard is set to 0. It's just that the
0 return value coming out of the assignment is discarded.

Deparse (with or without brackets) may make it clearer:

$ perl -MO=Deparse -e '/^Relayed message/ and $discard = 0, next;'
-e syntax OK
$discard = 0, next if /^Relayed message/;


$ perl -MO=Deparse,-p -e'/^match/ and call_a_sub(foo), next'
-e syntax OK
(/^match/ and (call_a_sub('foo'), (next)));
 
T

Tim McDaniel

Quoth (e-mail address removed):

Specifically, that = operator returns $discard, as a modifiable
lvalue.

So far as I understand the rules,

- a list assignment in a LIST environment returns the left-hand side
as a modifiable lvalue

- a list assignment in a SCALAR environment returns the number of
elements in the RIGHT-hand side. Hence the goatse operator, =()=.

- an assignment to a scalar in a LIST environment returns the
left-hand side as a modifiable lvalue

- an assignment to a scalar in a SCALAR environment returns 1, the
number of "elements" on the right-hand-side?

print (scalar (($a = 23) = 45), "\n");

Do I have that right?
 
T

Tim McDaniel

So far as I understand the rules,

- a list assignment in a LIST environment returns the left-hand side
as a modifiable lvalue

- a list assignment in a SCALAR environment returns the number of
elements in the RIGHT-hand side. Hence the goatse operator, =()=.

- an assignment to a scalar in a LIST environment returns the
left-hand side as a modifiable lvalue

- an assignment to a scalar in a SCALAR environment returns 1, the
number of "elements" on the right-hand-side?

print (scalar (($a = 23) = 45), "\n");

Do I have that right?

Of course, as written, that would obviously simplify into two cases.
But I don't understand what's going on.

print (scalar (($a = 23)), "\n");
print (scalar (($a = 23) = 45), "\n");

prints

23
1

.... unless the parentheses around the second assignment, which I added
merely for precedence, make it considered a list assignment. But that
would be so insanely stupid -- there would be no way to get a scalar
assignment as in case 1, except maybe with an intervening sub call or
somewhting.
 
R

Rainer Weikusat

(e-mail address removed) (Tim McDaniel) writes:

[...]

print (scalar (($a = 23) = 45), "\n");

prints

1

... unless the parentheses around the second assignment, which I added
merely for precedence, make it considered a list assignment.

Ehh ... well ... ($a = 23) is obviously a list with a single elementy so

($a = 23) = 45

is obviously a list assignment.
 
C

Charles DeRykus

Of course, as written, that would obviously simplify into two cases.
But I don't understand what's going on.

print (scalar (($a = 23)), "\n");
print (scalar (($a = 23) = 45), "\n");

prints

23
1

... unless the parentheses around the second assignment, which I added
merely for precedence, make it considered a list assignment. But that
would be so insanely stupid -- there would be no way to get a scalar
assignment as in case 1, except maybe with an intervening sub call or
somewhting.

The only quick ways to finesse the list context that occur to me:


perl -E 'say scalar( (($a=23)=45)[-1] )'
perl -E 'say scalar( do{($a=23)=45;$a} )'


(can't think of any reason to do so however).
 
C

Charles DeRykus

Quoth Charles DeRykus said:
print (scalar (($a = 23) = 45), "\n"); [...]
... unless the parentheses around the second assignment, which I added
merely for precedence, make it considered a list assignment. But that
would be so insanely stupid -- there would be no way to get a scalar
assignment as in case 1, except maybe with an intervening sub call or
somewhting.

The only quick ways to finesse the list context that occur to me:

perl -E 'say scalar( (($a=23)=45)[-1] )'
perl -E 'say scalar( do{($a=23)=45;$a} )'

I'm not sure what these are meant to do... in both cases the '= 45' is a
list assignment. If you want to perform a scalar context assignment to
the result of a scalar context assignment, I believe the simplest way is
like this:

${\($a = 23)} = 45;

(Neither scalar() nor a do{} block can be assigned to, so although they
would be conceptually simpler they don't work.)


Did I skew off-tangent...? this was just a workaround to
finesse the list context within case #2 and get the same results as
case #1, ie, the complaint/challenge: "no way to get a scalar assignment
as in case 1, except maybe with an intervening sub call or something"
 
C

Charles DeRykus

Quoth Charles DeRykus said:
Quoth Charles DeRykus <[email protected]>:
On 12/30/2013 9:20 AM, Tim McDaniel wrote:

print (scalar (($a = 23) = 45), "\n");
[...]
... unless the parentheses around the second assignment, which I added
merely for precedence, make it considered a list assignment. But that
would be so insanely stupid -- there would be no way to get a scalar
assignment as in case 1, except maybe with an intervening sub call or
somewhting.

The only quick ways to finesse the list context that occur to me:

perl -E 'say scalar( (($a=23)=45)[-1] )'
perl -E 'say scalar( do{($a=23)=45;$a} )'

I'm not sure what these are meant to do... in both cases the '= 45' is a
list assignment. If you want to perform a scalar context assignment to
the result of a scalar context assignment, I believe the simplest way is
like this:

${\($a = 23)} = 45;

Did I skew off-tangent...? this was just a workaround to
finesse the list context within case #2 and get the same results as
case #1, ie, the complaint/challenge: "no way to get a scalar assignment
as in case 1, except maybe with an intervening sub call or something"
print (scalar (($a = 23)), "\n"); # case 1
print (scalar (($a = 23) = 45), "\n"); # case 2
prints
23
1

Someone is certainly confused here, but I'm not sure who it is...

...
I'm still not sure what you mean by 'finessing the list context'. Your
first example evaluates the '() = 45' list assignment in list context
(returning a list of one element containing $a as an lvalue), then
slices out the last element of that list, evaluating the slice in scalar
context (returning $a as an rvalue). The second achieves the same thing
by a different route, evaluating the list assignment in void context and
then returning $a as an rvalue directly from the do block. Neither case
returns a value that can be assigned to directly.

Hm, I assumed there was already consensus that case #2 created a list
context and case #1 didn't. The "finesses" were simple workarounds to
force identical print output as I mentioned earlier. I suspect a more
penetrating analysis was sought :)
 

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

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top