Home > Raku > Raku is a match for *

Raku is a match for *

PimDaniel asked an interesting question.

How do i test match is True while matching : this does NOT work :
if my ($type,$a,$b,$c) = ($v ~~ /^ ('horiz'|'vertic') '_' (\d+) '_' (\d+) '_' (\d+) $/)>>.Str { ... }
Well i made it in 2 times 1/ capture and test the match, 2/ convert the match to Str.

There was no prompt answer and no improvement at all. I couldn’t find a nice way to do this quickly either. In fact it took me the better part of an hour to crack this nut. The main issue here is that a failed match will produce Nil that .Str will complain about. So lets separate boolean check of if and the conversion to Str.

my $a = '1 B';

if $a ~~ /(<digit>) \s (<alpha>)/ -> $_ {
    my ($one, $B) = .deepmap: *.Str;
    say "$one $B";
}
# OUTPUT: 1 B

By forcing the result of the condition expression into the topic, we can run any method on the result of the match, but only if Match.bool returns true. I don’t got a degree in CS* but would be very surprised if Raku-signatures would not turn out to turing complete.

if $a ~~ /(<digit>) \s (<alpha>)/ -> Match (Str() $one, Str() $B) {
    dd $one;
    dd $B;
}
# OUTPUT: "1"
          "B"

The signature of the if block coerces the Match to a list. We pick two elements of it and coerce those to Str. Of cause we could coerce to anything we like based on the position of the captures.

Regexes in Raku are compiled to the same byte code then the rest of the program. In fact grammars are just classes with a funky syntax. That’s why we can run Raku code inside a regex with ease. That means we can turn the whole thing inside out.

my @a = <1 B 3 D 4>;
my @b;

my $hit;

for @a -> $e {
    @b.push: ($e ~~ /(<alpha>) || { next } /).Str;
}

say @b;
# OUTPUT: [B D]

Here we skip the .push if the match does not succeed by skipping the rest of the loop body with next. We could fire any control exception inside the regex. That means we could stick the whole thing into a sub and return the value we are looking for from within the regex.

sub cherry-pick-numeric(Str $matchee) {
    $matchee ~~ m/(<digit>) && { return .Numeric }/;
    Empty
}

@b = do .&cherry-pick-numeric for @a;

dd @b;
# OUTPUT: Array @b = [1, 3, 4]

Raku has been in the making for 10 years. This was an gargantuan task. Now comes the hard bit. We have to take that large language and find all the nice idioms. Good things come to those who wait (on IRC).

*) read: Don’t believe anything I write. You have been warned.

Update:

In truly lazy fashion I came up with a way to turn a match into a lazy list after the work should have been done.

$a = '1B3D4';

my \ll := gather $a ~~ m:g/
      [ <alpha> && { take $/<alpha>.Str } ]
    | [ <digit> && { take $/.<digit>.Numeric } ]
    | [ { say 'step' } ]
/;
say ll[0];
say ll[3];
# OUTPUT: 1
          step
          step
          step
          D

The trick is force the match to run all the way to the end of the string with the :g adverb. This run will be interrupted by a take (by throwing CX::Take) and resumed when the next value is asked from the Seq returned by gather. I don’t know if this is memory efficient thought. There may be a Match instance kept around for each take.

Categories: Raku
  1. Bruce Van Allen
    March 22, 2021 at 01:25

    “The signature of the if block coerces the Match to a list.”

    Gotta love that.

  1. March 15, 2021 at 20:10

Leave a comment