neptuneray has asked for the wisdom of the Perl Monks concerning the following question:

I have been a programmer for many years, but only recently found Perl. So far, I love what I see, and only wish I had gotten involved 15 years ago when my brain was a lot sharper! I have been reading many of the great Perl books out there, and I'm trying to absorb a lot about this unique language first before I dive in coding. So my question is VERY basic: it has to do with Perl statements. For the most part, they are C-like and should usually each be on a line of it's own, terminated with a semicolon. No problem. But then I see this comma usage, as in "Perls of Wisdom", page 22:
#!/usr/bin/perl $names++, shift if ARGV[0] eq "-l"; $search = shift; $showname = @ARGV > 1; ...
Also, I see syntax like:
$c = do { $a = 3, $b = 6 }; # a block, $c = 6
Could someone please clarify the comma usage? Thanks, Ray

Replies are listed 'Best First'.
Re: perl statements
by rir (Vicar) on Mar 25, 2008 at 03:38 UTC
    Many Perlers will censure the use of the comma as given. I'd usually avoid the construct, but if the context is simple and the core statement, the part up to if, is just a couple of "words", I'll write such.
    $names++, shift if ARGV[0] eq "-l";
    In scalar context, the comma operator separates expressions and returns the evaluation of the rightmost expression. Here, the first expression is evaluated and the result discarded; the side-effect of incrementing $names remains.

    Update: It should be noted that no list is created by a series of comma operators in scalar context. The expressions operated on by the commas become subexpressions.

    The second construct is similar but I find it odder.

    $c = do { $a = 3, $b = 6 };
    The same thing is happening; the assignments are in scalar context. I'd write this with a semi-colon in the middle of the do's block; one standard use of the do BLOCK is to wedge a statement or statements into a place that needs an expression. If $c were @c, my semi-colon would change the meaning.

    Be well,
    rir

Re: perl statements
by kyle (Abbot) on Mar 25, 2008 at 02:58 UTC

    Here's how I look at it...

    First, a literal "counts" as a statement. You can say:

    foo(); 'this is a string'; bar();

    The string-as-statement doesn't do anything, but it's syntactically "correct." You could have also put a number there. I use this sometimes when I want to loop while something is true, but I don't otherwise have a loop body.

    1 while ( s/^\t// );

    Rather than a simple scalar as a statement, you could also have a list, delimited with commas. The list elements can be calls to subs or whatever else you might stick in a list.

    # assignment @foo_and_bar = ( foo(), bar() ); # same thing, not assigned to anything foo(), bar();

    There may also be a way of thinking about this that has to do with operator precedence (the comma is an operator), but I personally don't look at it that way. Having written this now, I wonder how this compares to the actual semantics. This kind of construct is one that I encounter infrequently enough that I haven't needed to know the gory details.

    Update: Upon further consideration, I think all these are expressions. A sub call is an expression, a literal scalar is an expression, and a comma-separated list of expressions is an expression. You could even have two semi-colons on top of each other and make an empty expression.

Re: perl statements
by ack (Deacon) on Mar 25, 2008 at 04:25 UTC

    Both kyle and rir have provided the definitive responses. I am far too junior a monk to enhance either response. For what it is worth, I can, however, provide what the Camel book has to say. According to the Camel (Programming Perl,3rd ed., Wall, Christiansen & Orwant, O'Riley: 2000, pp. 108-109) with respect to the COMMA OPERATORS:

    In scalar context it [the comma operator] evaluates its left argument in void context, throws the value away, then evaluates its right argument in scalar context and returns that value.

    It goes on to say that:

    Do not confuse the scalar context with the list context use. In list context, the comma is just the list argument separator, and inserts both its arguments into the LIST. It does not throw any values away.

    In that same reference on pg. 131, the authors go on to say:

    Modifiers bind more tightly (with higer precedence) than the comma does. The following example erroneously declares one variable, not two, because the list following the modifier is not enclosed in parentheses.
    my $foo, $bar = 1; # wrong # The above has the same effect # as: my $foo; $bar = 1;

    Hope that helps a little.

    ack Albuquerque, NM
Re: perl statements
by nefigah (Monk) on Mar 25, 2008 at 04:32 UTC

    A good rule of thumb to go by, is that if something looks kinda funky and complicated and hard to understand, it probably is :P If you have Perl Best Practices, it will certainly pound that into your head. The semantics of the examples were explained above; just know that you won't need to use them yourself much or really worry about it.

    Things to look out for:

    • C does have the comma usage as well, seen sometimes in things like:
      for (i = 0, j = 10; i < 10; i++, j--) { . . .}
      The commas there, like in Perl, do separate statements.
    • Note (as was well stated in the previous reply) that you can't do this, the likes of which you CAN do in C:
      my $var1, $var2 = 3, 4;
      As in your example, commas aren't making lists! (If you are running with use strict; like you always should, it will catch this for sure.) What you probably wanted was:
      my ($var1, $var2) = (3, 4); which assigns 3 to $var1 and 4 to $var2.
    • The block construct shown in the second example can be useful for creating a scope (that is, giving you more exact control about the breadth of your actions within it). An example would be:
      my $code = do { local $/; <$in> };
      (Stolen from PBP, which states: Note that it's important to put that localization-and-read inside a do {...} or in some other small block. A common mistake is to write this instead:

      $/ = undef; my $text = <$in>;

      That works perfectly well, in itself, but it also undefines the global input record separator, rather than its temporary localized replacement. But the global input record separator controls the read behaviour of every filehandleeven those that are lexically scoped, or in other packages. So, if you don't localize the change in $/ to some small scope, you're dooming every subsequent read everywhere in your program to vile slurpitude.

    In summary, the examples listed are fairly rare, but like everything occasionally have application. Good luck and happy coding :)


    I'm a peripheral visionary... I can see into the future, but just way off to the side.