2

How can I write this with the smartmatch operator (~~)?

use 5.010;

my $string = '12 23 34 45 5464 46';

while ( $string =~ /(\d\d)\s/g ) {
    say $1;
}
ThisSuitIsBlackNot
  • 23,492
  • 9
  • 63
  • 110
sid_com
  • 24,137
  • 26
  • 96
  • 187

4 Answers4

4

Interesting. perlsyn states:

Any ~~ Regex pattern match $a =~ /$b/

so, at first glance, it seems reasonable to expect

use strict; use warnings;
use 5.010;

my $string = '12 23 34 45 5464 46';

while ( $string ~~ /(\d\d)\s/g ) {
    say $1;
}

to print 12, 23, etc but it gets stuck in a loop, matching 12 repeatedly. Using:

$ perl -MO=Deparse y.pl

yields

while ($string ~~ qr/(\d\d)\s/g) {
    say $1;
}

looking at perlop, we notice

qr/STRING/msixpo 

Note that 'g' is not listed as a modifier (logically, to me).

Interestingly, if you write:

my $re = qr/(\d\d)\s/g;

perl barfs:

Bareword found where operator expected at C:\Temp\y.pl line 5, 
near "qr/(\d\d)\s/g"
syntax error at C:\Temp\y.pl line 5, near "qr/(\d\d)\s/g"

and presumably it should also say something if an invalid expression is used in the code above

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • @Sinan So I can't this write with the smart match operator? – sid_com Oct 28 '09 at 16:21
  • 1
    @sid_com I do not think you can but I do not know if that behavior is by design. – Sinan Ünür Oct 28 '09 at 16:25
  • I'm guessing there is a bug somewhere in what you ran through Deparse. When I use the code he put in I get this: `while ($string =~ /(\d\d)\s/g)`. And as a matching regex (from perlsyn) it should have the `g` operator. I'd check your perl install. =-[ – Jack M. Oct 29 '09 at 20:42
  • @Jack M. What I ran through Deparse is what is shown in my post. And the output I get is the same. **And**, I get the same output on Linux and Windows XP with perl 5.10.1. – Sinan Ünür Oct 29 '09 at 20:56
  • That was my fault. I was using his original code, not the modified smart match code. You are correct, once you plop in the smart match. – Jack M. Oct 29 '09 at 21:43
1

If we go and look at what these two variants get transformed into, we can see the reason for this.


  • First lets look at the original version.

    perl -MO=Deparse -e'while("abc" =~ /(.)/g){print "hi\n"}'
    
    while ('abc' =~ /(.)/g) {
        print "hi\n";
    }
    

    As you can see there wasn't any changing of the opcodes.

  • Now if you go and change it to use the smart-match operator, you can see it does actually change.

    perl -MO=Deparse -e'while("abc" ~~ /(.)/g){print "hi\n"}'
    
    while ('abc' ~~ qr/(.)/g) {
        print "hi\n";
    }
    

    It changes it to qr, which doesn't recognize the /g option.

    This should probably give you an error, but it doesn't get transformed until after it gets parsed.


    The warning you should have gotten, and would get if you used qr instead is:

    syntax error at -e line 1, near "qr/(.)/g"


The smart-match feature was never intended to replace the =~ operator. It came out of the process of making given/when work like it does.

Most of the time, when(EXPR) is treated as an implicit smart match of $_.
...

Brad Gilbert
  • 33,846
  • 11
  • 78
  • 129
  • That was my idea - replace "=~" with "~~". Does Perl6 have only the smart match operator or will there be a "=~" to? – sid_com Oct 29 '09 at 18:05
0

Is the expected behaviour to output to first match endlessly? Because that's what this code must do in its current form. The problem isn't the smart-match operator. The while loop is endless, because no modification ever occurs to $string. The /g global switch doesn't change the loop itself.

What are you trying to achieve? I'm assuming you want to output the two-digit values, one per line. In which case you might want to consider:

say join("\n", grep { /^\d{2}$/ } split(" ",$string));
RET
  • 9,100
  • 1
  • 28
  • 33
-1

To be honest, I'm not sure you can use the smart match operator for this. In my limited testing, it looks like the smart match is returning a boolean instead of a list of matches. The code you posted (using =~) can work without it, however.
What you posted doesn't work because of the while loop. The conditional statement on a while loop is executed before the start of each iteration. In this case, your regex is returning the first value in $string because it is reset at each iteration. A foreach would work however:

my $string = '12 23 34 45 5464 46';
foreach my $number ($string =~ /(\d\d)\s/g) {
    print $number."\n";
}
Jack M.
  • 30,350
  • 7
  • 55
  • 67
  • Completely wrong and off the mark. First, using `$string =~ s///g` in a `while` loop is not only perfectly OK but is preferable to a `for` loop in a lot of instances. Second, you are missing the fact that the OP is talking about the smart match operator `~~` and not `=~`. – Sinan Ünür Oct 29 '09 at 20:58
  • He's not using `s///g`, though. He's using, essentially, `m///g`. You are correct about the smart operator, I'll edit to fix that bit up. – Jack M. Oct 29 '09 at 21:04
  • I meant `m//` and what I said about `s///` applies to `m//` in `while` versus `for` loops as well. – Sinan Ünür Oct 29 '09 at 21:34
  • Unless you are manipulating (in this case) `$string` inside the loop, are you not creating an infinite loop? In the case of the OP's question, that is why his code didn't output what he expected, is it not? – Jack M. Oct 29 '09 at 21:42
  • No. You are not creating an infinite loop precisely because of the `g` modifier. Try `while ($string =~ /(\d{2})\s/g ) { print "$1\n" }`. The difference between `while (m//g)` and `for (m//g)` is that you get to operate on matches one-by-one with `while` (like reading a file line by line versus slurping it). – Sinan Ünür Oct 29 '09 at 21:55
  • Huh, that I did not know. I head read forever ago in The Perl Cookbook that the while was evaluated each time, and that an m// would still get evaluated at each instance. Good to know. – Jack M. Oct 30 '09 at 01:00