rakulang, dartlang, nimlang, golang, rustlang, lang lang no see

模式分派

焉知非鱼

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) { putholding $key; },
    -> Press (:$key) { putpressing $key; },
    -> Err (:$msg) { warnERR: $msg},
    else { failunsopported};

方法 .cando 需要一个 Capture 来告诉我们一个 Routine 是否可以用一个给定的参数列表来调用。为了创建这样一个捕获,我们使用字面的 \($arguments, $go, $here)。我们不在最后测试默认值。相反,当没有其他函数匹配时,我们会调用该函数。声明 sub else 只是为了美化。

由于我们是在函数式的土地上,我们可以使用 Raku 提供给我们的所有方便的功能。

my &key-matcher = &match.assuming(*,[
        -> Hold (:$key) { putholding $key; },
        -> Press (:$key) { putpressing $key; },
        -> Err (:$msg) { warnERR: $msg},
        else { failunsopported};
]);

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/