模式分派
— 焉知非鱼Pattern Dispatch
一直以来乐于助人的 raiph 在回答一个关于模式匹配的问题时希望得到 RakuAST,就像在 Haskell 中一样。有人提出用 MMD 来解决这个问题。这样做,得到一个贯穿的默认路径是无解的。由于 dispatch 简单来说就是模式匹配,我们只需要做一些额外的工作。简而言之,dispatcher 得到一个函数列表和一个带参数的列表。第一个接受所有参数的函数获胜。
class Hold { has $.key; }
class Press { has $.key; }
class Err { has $.msg; }
sub else(&code) { &code }
sub match($needle, *@tests) {
for @tests.head(*-1) -> &f {
if &f.cando(\($needle)) {
return f($needle);
}
}
@tests.tail.();
}
match Hold.new(:key<a>),
-> Hold (:$key) { put „holding $key“; },
-> Press (:$key) { put „pressing $key“; },
-> Err (:$msg) { warn „ERR: $msg“ },
else { fail ‚unsopported‘ };
方法 .cando
需要一个 Capture 来告诉我们一个 Routine 是否可以用一个给定的参数列表来调用。为了创建这样一个捕获,我们使用字面的 \($arguments, $go, $here)
。我们不在最后测试默认值。相反,当没有其他函数匹配时,我们会调用该函数。声明 sub else
只是为了美化。
由于我们是在函数式的土地上,我们可以使用 Raku 提供给我们的所有方便的功能。
my &key-matcher = &match.assuming(*,[
-> Hold (:$key) { put „holding $key“; },
-> Press (:$key) { put „pressing $key“; },
-> Err (:$msg) { warn „ERR: $msg“ },
else { fail ‚unsopported‘ };
]);
sub key-source {
gather loop {
sleep 1;
take (Hold.new(:key<a>), Press.new(:key<b>), Err.new(:msg<WELP!>), 'unsupported').pick;
}
}
.&key-matcher for key-source;
我们要帮助 .assuming
有点理解 slurpies,把函数列表放在一个显式 Array 中。
总有一种函数式的方法来解决一个问题。有时我们甚至可以从中得到一个整齐的语法。
原文链接: https://gfldex.wordpress.com/2021/02/24/pattern-dispatch/