Syntax understanding problem

Discussion in 'Perl Misc' started by Justin C, Dec 20, 2013.

  1. Justin C

    Justin C Guest

    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 C, Dec 20, 2013
    1. Advertisements

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

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

    use Data::Dumper; print Dumper \@array
    George Mpouras, Dec 20, 2013
    1. Advertisements

  3. That's not surpising. You are (mis?)-using data structures as control
    flow elements. That is at the very least a rather questionable habit.
    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.
    Well, this version obviously never jumps to the next incarnation of
    whatever enclosing loop you got.
    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.
    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/;

    Jürgen Exner, Dec 20, 2013
  4. This is just a cryptic perlish way of saying this:

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

    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).
    $ perldoc perlop

    Tony Mountifield, Dec 20, 2013
  5. 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.

    Jürgen Exner, Dec 20, 2013
  6. 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 ...)
    Rainer Weikusat, Dec 20, 2013
  7. And exactly for this reason it is just as insane.

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

    Jürgen Exner, Dec 20, 2013
  8. The code in question is an expression. The purpose of an expression is
    to manipulate date, not to control execution flow.
    I never said that side-effects in statements that are used for control
    flow were a good idea in C, either.
    Well, that's a given, isn't it?

    Jürgen Exner, Dec 20, 2013
  9. Justin C

    Tim McDaniel Guest

    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;
    Tim McDaniel, Dec 20, 2013
  10. Justin C

    Tim McDaniel Guest

    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.
    Tim McDaniel, Dec 21, 2013
  11. Justin C

    Justin C Guest

    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;

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

    Thank you all for the discussion.

    Justin C, Dec 22, 2013
  12. Justin C

    Tim McDaniel Guest

    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.

    An assignment statement returns a value:

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

    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.
    Tim McDaniel, Dec 22, 2013
  13. 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)
    Rainer Weikusat, Dec 22, 2013
  14. 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)));
    Charles DeRykus, Dec 23, 2013
  15. Justin C

    Tim McDaniel Guest

    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?
    Tim McDaniel, Dec 30, 2013
  16. Justin C

    Tim McDaniel Guest

    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");



    .... 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
    Tim McDaniel, Dec 30, 2013
  17. (Tim McDaniel) writes:


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

    ($a = 23) = 45

    is obviously a list assignment.
    Rainer Weikusat, Dec 30, 2013
  18. 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).
    Charles DeRykus, Dec 31, 2013

  19. 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"
    Charles DeRykus, Jan 1, 2014
  20. 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 :)
    Charles DeRykus, Jan 1, 2014
    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.