How can I write this with the smart match operator (~~)?
use 5.010;
my $string = '12 23 34 45 5464 46';
while ( $string =~ /(\d\d)\s/g ) {
say $1;
}
How can I write this with the smart match operator (~~)?
use 5.010;
my $string = '12 23 34 45 5464 46';
while ( $string =~ /(\d\d)\s/g ) {
say $1;
}
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
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));
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$_
.
...
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";
}