Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

Syntax Perl Version support $c = () = $a =~ /\./g

by h2 (Beadle)
on Jul 17, 2018 at 18:02 UTC ( #1218677=perlquestion: print w/replies, xml ) Need Help??

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

Hi Perl Monks, I have what is hopefully a simple question, but one I have been unable to search for due to the odd syntax of the below.

The program I'm working on has a core requirement to work on older systems, and has a cutoff of Perl 5.008 (selected because Redhat mid 2000s release was quite late to support 5.010) or newer, though that could change in the future to either a touch earlier version, or a touch later version (but certainly never newer as max oldest than 5.010). As an aside, so far Perl 5.x has far exceeded my wildest expectations in this regard, the program is in fact working on everything! No matter how weird or arcane.

my $a='3.4.4'; my $c = () = $a =~ /\./g; print "$c\n"

However, my question is this: when did Perl begin supporting this type of structure?

$c = () = $a =~ /\./g

I've come across this several times, and I'm allured by it, it calls to me, but I have to be careful using structures that could trigger warnings or failures in old systems.

I'd actually like to significantly expand my use of this type of structure, so besides this specific example, can anyone point me to the right search terms that describe it, that's multiple assignments of some value generated on the right side of the items? And also I believe various items that could be in the second position, (), though I'm not positive about that. And has this support been consistent since version Are there various types of this assignment that have been introduced earlier or later?

I have the big Larry Walls Perl book, but I don't know what this is called to begin learning more about it, and what variants will be safe to use.

Replies are listed 'Best First'.
Re: Syntax Perl Version support $c = () = $a =~ /\./g (updated)
by AnomalousMonk (Archbishop) on Jul 17, 2018 at 18:34 UTC
    my $c = () = $a =~ /\./g;

    This statement evaluates a regex in list context (imposed by the empty parens):
        () = $a =~ /\./g
    and then evaluates the contents of the intermediate list captured by the parens in scalar context (imposed by the scalar assignment):
        my $c = ()
    which evaluates to the number of elements in the list.

    Some variants of this syntax (in which the regex matches are captured instead of being thrown away):

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "print 'perl version: ', $]; ;; my $s = '3.5.9'; ;; my @ra; my $c = @ra = $s =~ m{ \d }xmsg; dd $c, \@ra; ;; $s = ''; ;; $c = my @rb = $s =~ m{ \d }xmsg; dd $c, \@rb; " perl version: 5.008009 (3, [3, 5, 9]) (4, [8, 6, 5, 1])

    ... when did Perl begin supporting this type of structure?

    AFAIK, Perl 5.x has always supported this, and I believe 4.x did as well, but I'm too lazy to do the research. (Update: See this for pertinent info on Perl 5 support for the  =()= "operator," and this for Perl 4 support (none).) This is a well-known and safe Perl idiom. In general, look for discussions of "context" and "context dependence."

    Update: See also Context tutorial in the Tutorials section of the Monastery.

    Give a man a fish:  <%-{-{-{-<

      AFAIK, Perl 5.x has always supported this

      I'm having some trouble running a bisect at the moment, but as a tentative result, it appears that Perl versions roughly before 5.004 didn't like the test program -e '$a=""; $b=()=$a=~/\./g; $b==3 or die $b' because Can't modify stub in list assignment at -e line 1, near "/\./g;". AFAICT it was not possible to assign to an empty list back then (a workaround seems to be $b = @{[]} = $a =~ /\./g;, which works on 5.000).

        haukex, thanks for digging in to find the lower limit, that was a key thing I needed to know. I'll note this in the docs, but I doubt I will ever need to go older than Perl 5.008 but I have learned painfully to never say never in this area.
      and I believe 4.x did as well

      No, it didn't and doesn't yet without patches ;-)

      perl 4 patchlevel 36:

      qwurx [shmem] ~> perl4 -e '$s="3.4.5";$r=()=$s=~/\./g;print$r,"\n"' Illegal item (LEXPR) as lvalue in file /tmp/perl-eEdymqE at line 1, ne +xt 2 tokens "/\./g;" Execution of /tmp/perl-eEdymqE aborted due to compilation errors.

      However, chaining assignments and evaluating ARRAY in scalar context did:

      qwurx [shmem] ~> perl4 -e '$s="3.4.5";$r=@r=$s=~/\./g;print$r,"\n"' 2

      Applying semantics of ARRAY to LEXPR (list expression) happened in perl5.

      perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'
      Thank you very much, I'm glad to see I can start digging into these more arcane operator collections. Thanks for the links as well, now I at least know what to be looking for. Your responses are exactly what I needed to know.
Re: Syntax Perl Version support $c = () = $a =~ /\./g
by jdporter (Chancellor) on Jul 17, 2018 at 18:41 UTC

    Read: perlsecret - Perl secret operators and constants

    (Hint: It's the one right after the "space station" operator)

      It's also called the "goatse" operator — just don't ask why!

      Give a man a fish:  <%-{-{-{-<

        I think this is supposed to look like an ASCII emoticon for a type of beard called a goatee. But yeah, that is what this is called.
Re: Syntax Perl Version support $c = () = $a =~ /\./g
by jwkrahn (Monsignor) on Jul 17, 2018 at 21:28 UTC
    $c = () = $a =~ /\./g

    That counts the number of the characters '.' in the $a string.

    A more efficient way to do that is:

    $c = $a =~ tr/.//

    But note that while the first verion will work with strings of any length the second verion will only work with characters.

      jwkrahn, I had to test this, and lo, as you said, tr/.// is about 80% or so faster. I mean, of course, I'd have to run it thousands of times, but there it is, much faster. Thanks for that tip. I'm not clear on what you meant by this:

      But note that while the first version will work with strings of any length the second version will only work with characters.

      The data is a string of varying lengths, and usually is a decimal number.

      While testing this again with tr/ in mind, I decided to see how much faster it would be if I got rid of all the regex in the tests, and replaced them with tr/ with the numeric values and count, and ended up with a 4x (!) speed improvement over the pure regex sequence of tests.

      I also discovered that there is no obvious difference between

      (my $c = $_[0] =~ tr/.// ) <= 1 ## and ($_[0] =~ tr/.// ) <= 1

      which I assume means I stumbled onto another secret operator (the wrapping parentheses), which suggests to me that I should study these more to become more aware of this area of Perl.

        ... another secret operator (the wrapping parentheses) ...

        You have stumbled upon the secret operator known as operator precedence. In this case, there's no need for the disambiguation of grouping parentheses because the  =~ binding operator is of higher precedence than  <= or any other comparator.

        c:\@Work\Perl\monks>perl -wMstrict -le "sub not_too_many_dots { return $_[0] =~ tr/.// <= 1; } ;; for my $s ('', qw(. .. ... ....)) { printf qq{'$s' %stoo many dots \n}, not_too_many_dots($s) ? 'NOT ' : '' ; } " '' NOT too many dots '.' NOT too many dots '..' too many dots '...' too many dots '....' too many dots
        See perlop (update: in particular Operator Precedence and Associativity). Of course, parenthetic grouping disambiguation never hurts, and many recommend it as a general BP to support readability/maintainability.

        Update 1: I suspect the speedup you're seeing is due to operating directly upon an element of the aliased  @_ subroutine argument array rather than burning the computrons needed to create lexical variables. See perlsub. (This would be in addition to using  tr/// rather than  m// for counting individual characters.)

        Update 2: If you want to know what Perl thinks about the precedence and associativity of the operators you're using, use the O and B::Deparse modules. The  -p flag produces full, explcit parenthetic grouping. (The useless assignments just produce some more grouping examples.)

        c:\@Work\Perl\monks>perl -wMstrict -MO=Deparse,-p -le "sub not_too_many_dots { return $_[0] =~ tr/.// <= 1; } ;; for my $s ('', qw(. .. ... ....)) { my $g = my $f = not_too_many_dots($s); printf qq{'$s' %stoo many dots \n}, $f ? 'NOT ' : ''; print $g; } " BEGIN { $^W = 1; } BEGIN { $/ = "\n"; $\ = "\n"; } sub not_too_many_dots { use strict 'refs'; return((($_[0] =~ tr/.//) <= 1)); } use strict 'refs'; foreach my($s) (('', ('.', '..', '...', '....'))) { (my $g = (my $f = not_too_many_dots($s))); printf("'${s}' %stoo many dots \n", ($f ? 'NOT ' : '')); print($g); } -e syntax OK

        Give a man a fish:  <%-{-{-{-<

        G'day h2,

        "... and ended up with a 4x (!) speed improvement ..."

        You might like to take a look at "perlperf - Perl Performance and Optimization Techniques" which discusses this, amongst other things, and includes benchmarks.

        While y/// and s/// are not always interchangeable, when they can provide the same functionality, I've generally found y/// to be measurably faster than s///.

        — Ken

Re: Syntax Perl Version support $c = () = $a =~ /\./g
by Marshall (Canon) on Jul 18, 2018 at 01:12 UTC
    That is a rather odd statement, but completely valid since at least Perl 5.6 "mid-90's".

    my @anyname = some match regex global expression; my $c = @anyname; #scalar value of number of elements in @anyname # The Goatse means the same thing but you don't have to have to # Updated spelling of Goatse, one "e", not two - Ooops thanks YourMoth +er # have the array @anyname
    In general my code usually processes each match from a "match global".
    I think I've used this construct before, but this is definitely not common.
    More common is whether or not a match exists - not how many.
Re: Syntax Perl Version support $c = () = $a =~ /\./g
by ikegami (Patriarch) on Jul 18, 2018 at 09:32 UTC
Re: Syntax Perl Version support $c = () = $a =~ /\./g
by Anonymous Monk on Jul 17, 2018 at 19:42 UTC
    And wouldn't it be the perfect thing to put in a descriptively-named one line subroutine that hides the Perl-voodoo in only one place, and tells everyone else exactly what it's supposed to do? Mmmmm....? Perl has lots of ways to write something such that you don't know at a glance what it's supposed to do, and it doesn't actually do what you intended for it to do or thought that it did, either. Sure would be nice to be able to fix any problems in one place.
      Perl has lots of ways to write something such that you don't know at a glance what it's supposed to do

      Well… Perl programmers do. It's weird… it's like this site is almost dedicated to those people. Maybe a name change would help combat the lack of clarity perptually befuddling you. s/Perl Monks/Perl Programmers Who *Like* Perl and Also Enjoy Learning, Exploring Problems, and Helping Their Community; Unemployable PMs Are Welcome to Participate Providing They Remain Respectful and Don't Become Founts of Acrimony or Wasted Time for Years on End/g;

      Anonymous monk, and that is precisely where that is being placed, inside of a one liner utility subroutine, that is documented as to what it tests, in this case, it returns 1, true, if the match is correct, and undefined, false, if not. As part of a larger test, but as a one liner. I have used arcane syntax like this internally in utility tools, and try to keep it out of the main logic that would be likely to get patches or pull requests. Documented, with comments, etc.

      Now that this is confirmed safe, I'll be extending its use for all numeric tests (which is what this particular case is doing), and look into more secret type constructions for various utilities, that are not meant to be end user readable or serviceable, but are meant to be very very fast.

        Note that the content of the "anonymous" reply you replied to indicates it was authored by our Worst Nodes champion - who's recently introduced an annoying new tactic of replying "anonymously" ... and then sometimes agreeing with himself by replying to the "anonymous" post as sundialsvc4! I don't know why he's started doing this only recently.

        FWIW, I've often used the goatse operator in production code at work without wrapping it in a sub - though I always provide a one-line comment pointing to the goatse operator documentation. Having discussed the use of this operator during code reviews with a number of serious C++ programmers (but occasional Perl programmers) at work, I can report they've all been happy with this approach because, as skilled programmers, once they became aware it's a standard Perl idiom, they found the perlsecret documentation clear and easy to understand.

      "and tells everyone else exactly what it's supposed to do?" It does. You big dummy.

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1218677]
Front-paged by Corion
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (4)
As of 2022-12-03 09:24 GMT
Find Nodes?
    Voting Booth?

    No recent polls found