如何避免基于长模式匹配的功能?

时间:2015-01-02 20:24:14

标签: f# ml

当使用具有相当多构造函数的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构造函数而增长。 我怎样才能避免这种情况并清理这些代码?长模式匹配结构是不可避免的吗?

2 个答案:

答案 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所需的签名一致,因此如果您决定需要将此函数用作另一个匹配表达式中的大小写,则可以轻松地将其以活动模式包装到得到很好的语法。