当使用具有相当多构造函数的union类型时,我几乎总是发现自己在单个函数中实现了许多逻辑,即在一个函数中处理所有情况。有时我想提取单个案例的逻辑来分离函数,但是一个函数不能只接受一个“构造函数”作为参数。
实施例: 假设我们有典型的“表达式”类型:
type Formula =
| Operator of OperatorKind * Formula * Formula
| Number of double
| Function of string * Formula list
[...]
然后,我们想计算表达式:
let rec calculate efValues formula =
match formula with
| Number n -> [...]
| Operator (kind, lFormula, rFormula) -> [...]
| [...]
这样的函数会很长,并且会随着每个新的Formula构造函数而增长。 我怎样才能避免这种情况并清理这些代码?长模式匹配结构是不可避免的吗?
答案 0 :(得分:3)
您可以使用显式元组定义Operator
联合的Formula
个案:
type Formula =
| Operator of (string * Formula * Formula)
| Number of double
如果这样做,编译器将允许您使用Operator(name, left, right)
和使用单个参数Operator args
进行模式匹配,因此您可以编写如下内容:
let evalOp (name, l, r) = 0.0
let eval f =
match f with
| Number n -> 0.0
| Operator args -> evalOp args
我觉得这有点令人困惑,所以最好在类型定义中更明确并使用一个命名元组(相当于上面的代码):
type OperatorInfo = string * Formula * Formula
and Formula =
| Operator of OperatorInfo
| Number of double
或者更明确地使用记录:
type OperatorInfo =
{ Name : string
Left : Formula
Right : Formula }
and Formula =
| Operator of OperatorInfo
| Number of double
然后,您可以使用以下方法之一进行模式匹配:
| Operator args -> (...)
| Operator { Name = n; Left = l; Right = r } -> (...)
答案 1 :(得分:2)
我想说你通常想要在一个函数中处理所有的情况。这是工会的主要卖点 - 它们迫使你以某种方式处理所有案件。也就是说,我可以看到你来自哪里。
如果我有一个大联盟并且只关心一个案例,我会像这样处理它,将结果包装成一个选项:
let doSomethingForOneCase (form: Formula) =
match form with
| Formula (op, l, r) ->
let result = (...)
Some result
| _ -> None
然后以任何适合呼叫站点的方式处理无。
请注意,这与partial active patterns所需的签名一致,因此如果您决定需要将此函数用作另一个匹配表达式中的大小写,则可以轻松地将其以活动模式包装到得到很好的语法。