在看 RaycatWhoDat 喜欢 Raku 的时候,我才发现,List of Matches 不是 MatchList。
say 'a1'.match(/ \d /).replace-with('#');
say 'a1b2'.match(/ \d /, :g).replace-with('#');
# OUTPUT: a#
# No such method 'replace-with' for invocant of type 'List'
in block <unit> at /home/dex/tmp/tmp-2.raku line 8
第2次调用 replace-with
会失败,因为带 :g
的 .match
会返回一个 Match 的列表。当然,我们可以玩个简单的游戏,直接使用 sub
,当调用 :g
时,它就会做正确的事情。不过这不会是一篇好的博客文章。
为了让 replace-with
在 List 中工作,我们可以使用 where
从句。任何匹配都会有原始 Str
的副本,但不会有原始 Regex 的副本,所以我们实际上必须建立一个包含所有未匹配的 Str 的列表。这可以通过使用存储在 .from
和 .to
中的索引来完成。
multi sub replace-with(\l where (.all ~~ Match), \r --> Str) {
my $orig := l.head.orig;
my @unmatched;
@unmatched.push: $orig.substr(0, l.head.from);
for ^(l.elems - 1) -> $idx {
@unmatched.push: $orig.substr(l[$idx].to, l[$idx+1].from - l[$idx].to);
}
@unmatched.push: $orig.substr(l.tail.to);
(@unmatched Z (|(r xx l.elems), |'')).flat.join;
}
say 'a1vvvv2dd3e'.match(/ \d /, :g).&replace-with('#');
# OUTPUT: a#vvvv#dd#e
如果原始字符串的结尾没有匹配,那么匹配列表就会少一个,无法被直接压缩进去。这也是为什么我必须将替换列表扩展一个空字符串再喂给 Z
的原因。
所以如果 subst
做的很好,为什么还要用 .replace-with
呢?因为有时我们不得不使用 $/
。
if 'a1bb2ccc3e' ~~ m:g/ \d / {
say $/.&replace-with('#');
}
通常我们可以改变代码,但当一个模块的例程返回 Match 或其列表时,我们就没戏了。为了完整起见,我们还需要几个 multies
。
multi sub replace-with(Match \m, \r --> Str) {
m.replace-with(r);
}
multi sub replace-with(Match \m, &r --> Str) {
m.replace-with(r(m));
}
multi sub replace-with(\l where (.all ~~ Match), &r) {
my $orig := l.head.orig;
my @unmatched;
@unmatched.push: $orig.substr(0, l.head.from);
for ^(l.elems - 1) -> $idx {
@unmatched.push: $orig.substr(l[$idx].to, l[$idx+1].from - l[$idx].to);
}
@unmatched.push: $orig.substr(l.tail.to);
(@unmatched Z (|l.map(&r), |'')).flat.join;
}
即使问题是可以解决的,它仍然困扰着我。我们在 Raku 的很多地方都有 :g
提供相当多的 DWIM。在一些地方,这个概念打破了,几乎所有的地方都与列表有关。往往是 ».
来救场。当我们真正需要在列表上工作而不是单个元素时,即使这样也不行。字符串上的方法只是工作,因为在这种情况下,我们刻意避免将字符列表拆开。
如果你关注这个博客,你就会知道我非常倾向于使用运算符。遗憾的是,.
, .?
和 ».
并不是我们可以重载的真正的中缀运算符。我们也不能声明以 .
开头的中缀运算符,或者我们可以引入一个操作符,将它的 LHS 变成一个列表,然后对能够处理特定类型列表的 multis
进行调度。
如果不这样做,我们需要改变 .match
来返回 List 的一个子类,这个子类得到了方法 .replace-with
。我们可以把它插入到 Cool 中,但那里已经很拥挤了。
我们并没有一个很好的方法来增强内置方法的返回值。所以这将不得不在 CORE 中解决。
原文链接: https://gfldex.wordpress.com/2020/09/25/list-breaks-the-chain/