我正在学习 OCaml 的大学课程,需要使用容器库将 s-expr 解析为其正确的 AST 表示形式
我需要扩展解析器以包含布尔值,但我找不到一种优雅的方法来模式匹配字符串而不是嵌套更多模式匹配。
所以我想知道是否有更好的方法来做到这一点。
到目前为止,如果解析器检测到“原子字符串”s”,它会“打开”一个嵌套的匹配案例以查看它是 int 还是变量名。
...
| `Atomic s ->
begin
match int_of_string_opt s with
| Some n -> Num n
| None ->
begin
match bool_of_string_opt s with
| Some b -> Bool b
| None -> Var s
end
end
...
这个解决方案有效,但仅通过观察我就知道它可以设计得更好,但我对 OCaml 不够熟悉,不知道如何使其变得更好。
您可以“展平”模式匹配,但代价是可能进行不必要的评估
bool_of_string_opt s
。
match int_of_string_opt s, bool_of_string_opt s with
| Some n, _ -> Num n
| _, Some b -> Bool b
| _, _ -> Var s
另一种选择是使用
when
,例如:
| `Atomic s when is_integer s ->
Int (int_of_string s)
| `Atomic s when is_bool s ->
Bool (bool_of_string s)
| `Atomic s -> Var s
看起来您有一个解析器列表,您想要按顺序尝试它们,直到其中一个成功构建值。假设您的类型如下:
type atom = N of int | B of bool
let n v = N v
let b v = B v
我还定义了
n
和 b
构建器函数,因为构造函数不是函数。
然后,我们可以构建原子解析器,它将(i)产生可选值的解析器与(ii)原子变体构建器结合起来:
let atom_parser parse_opt build s =
s |> parse_opt |> Option.map build
候选解析器列表可以定义如下:
let candidates = [
atom_parser int_of_string_opt n;
atom_parser bool_of_string_opt b
]
列表中的每个值都是一个可能返回
atom
的函数。
最后,我们可以使用
List.find_map
将函数 f
依次应用于所有值,直到一次调用返回 Some
输出。这里的测试函数f
将被赋予我们的候选函数之一,并且必须将其应用于我们想要解析的字符串,所以实际上在我们的例子中f
是这个函数的部分应用:
let parse_input string parser = parser string
希望下面的示例能够阐明这意味着什么:
# List.find_map (parse_input "false") candidates;;
- : atom option = Some (B false)
# List.find_map (parse_input "12") candidates;;
- : atom option = Some (N 12)
# List.find_map (parse_input "12.45") candidates;;
- : atom option = None
我认为我们可以定义一个新的运算符来组合
'a option
值的多个计算。
let ( |-> ) opt f =
match opt with
| Some _ -> opt
| None -> f ()
...
Option.map (fun i -> Num i) (int_of_string_opt s)
|-> (fun () -> Option.map (fun b -> Bool b) (bool_of_string_opt s))
|-> fun () -> Some (Var s)