I'd like to make a string substitution in a for
block using a named capture. I've expected to get the numbers 1,2,3 as output. But it is Nil
for the first run, and then 1 and 2 for the 2nd and 3rd run. How do I use the .subst
correctly in the loop construct? I see the same behavior when using a map
construct instead the for
loop. It does work as expected, if I replace with a fixed string value.
for <a1 b2 c3> -> $var {
say $var;
say $var.subst(/.$<nr>=(\d)/, $<nr>); #.subst(/.$<nr>=(\d)/, 'X'); #OK
}
#`[
This is Rakudo version 2019.11 built on MoarVM version 2019.11
Output:
a1
Use of Nil in string context
in block at test3.pl6 line 3
b2
1
c3
2
]
Replace a character in a string using for loop in python We can replace a character in a string with another character in python by using the replace() function or sub() function or a for loop.
TL;DR Defer evaluation of $<nr>
until after evaluation of the regex. @JoKing++ suggests one way. Another is to just wrap the replacement with braces ({$<nr>}
).
subst
Before Raku attempts to call the subst
routine, it puts together a list of arguments to pass to it.
There are two values. The first is a regex. It does not run. The second value is $<nr>
. It evaluates to Nil
because, at the start of a program, the current match object variable is bound to something that claims its value is Nil
and any attempt to access the value of a key within it -- $<nr>
-- also returns Nil
. So things have already gone wrong at this point, before subst
ever runs.
Once Raku has assembled this list of arguments, it attempts to call subst
. It succeeds, and subst
runs.
To get the next match, subst
runs the regex. This updates the current match object variable $/
. But it's too late to make any difference to the substitution value that has already been passed to subst
.
With match in hand, subst
next looks at the substitution argument. It finds it's Nil
and acts accordingly.
For the second call of subst
, $<nr>
has taken on the value from the first call of subst
. And so on.
$<nr>
@JoKing suggests considering use of S///
. This construct evaluates the regex (between the first pair of /
s) first, then the replacement (between the last pair of /
s). (The same principle applies if you use other valid S
syntaxes like S[...] = ...
.)
If you use subst
, then, as explained in the previous section, Raku puts together the argument list for it before calling it. It finds a regex (which it does not run) and a closure (which it does not run either). It then attempts to call subst
with those arguments and succeeds in doing so.
Next, subst
starts running. It has received code for both the match (a regex) and the substitution (a closure).
It runs the regex as the matching operation. If the regex returns a match then subst
runs the closure and uses the value it returns as the substitution.
Thus, because we switched from passing $<nr>
as a naked value, which meant it got frozen into Nil
, to passing it wrapped in a closure, which deferred its evaluation until $/
had been set to a match with a populated <nr>
entry, we solved the problem.
Note that this only works because whoever designed/implemented subst
was smart/nice enough to allow both the match and substitution arguments to be forms of Code
(a regex for the match and ordinary closure for the substitution) if a user wants that. It then runs the match first and only then runs the substitution closure if it's been passed one, using the result of that latter call as the final substitution. Similarly, S///
works because that has been designed to only evaluate the replacement after it's first evaluated the substitution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With