递归类型的默认递归

时间:2015-03-29 14:16:39

标签: recursion f#

Idiomatic F#可以很好地代表经典的递归表达式数据结构:

type Expression = 
    | Number of int
    | Add of Expression * Expression
    | Multiply of Expression * Expression
    | Variable of string

与其上的递归函数一起:

let rec simplify_add (exp: Expression): Expression = 
    match exp with
    | Add (x, Number 0) -> x
    | Add (Number 0, x) -> x
    | _ -> exp

...哎呀,这不是按照书面形式工作的; simplify_add需要重复出现在子表达式中。在这个简单易行的玩具示例中,只有几行额外的代码,但在实际程序中会有几十种表达类型;人们宁愿避免为每个对表达式进行操作的函数添加几十行样板。

有没有办法表达'默认情况下,重复使用子表达式'?类似的东西:

let rec simplify_add (exp: Expression): Expression = 
    match exp with
    | Add (x, Number 0) -> x
    | Add (Number 0, x) -> x
    | _ -> recur simplify_add exp

其中recur可能是某种高阶函数,它使用反射来查找类型定义或某些东西?

1 个答案:

答案 0 :(得分:4)

不幸的是,F#没有为您提供任何递归函数来处理您的数据类型"免费"。您可以使用反射生成一个 - 如果您有很多递归类型,这将是有效的,但在正常情况下可能不值得。

虽然可以使用各种模式来隐藏重复。我觉得特别好的一个基于ExprShape module from standard F# libraries。我们的想法是定义一个活动模式,它将您的类型视图作为leaf(没有嵌套的子表达式)或node(带有子表达式列表):

type ShapeInfo = Shape of Expression

// View expression as a node or leaf. The 'Shape' just stores
// the original expression to keep its original structure
let (|Leaf|Node|) e = 
  match e with
  | Number n -> Leaf(Shape e)
  | Add(e1, e2) -> Node(Shape e, [e1; e2])
  | Multiply(e1, e2) -> Node(Shape e, [e1; e2])
  | Variable s -> Leaf(Shape e)

// Reconstruct an expression from shape, using new list 
// of sub-expressions in the node case.
let FromLeaf(Shape e) = e
let FromNode(Shape e, args) = 
  match e, args with
  | Add(_, _), [e1; e2] -> Add(e1, e2)
  | Multiply(_, _), [e1; e2] -> Multiply(e1, e2)
  | _ -> failwith "Wrong format"

这是您必须编写的一些样板代码。但好处是我们现在可以使用您的特殊情况和叶子和节点的两个附加模式来编写递归simplifyAdd函数:

let rec simplifyAdd exp =
    match exp with
    // Special cases for this particular function
    | Add (x, Number 0) -> x
    | Add (Number 0, x) -> x
    // This now captures all other recursive/leaf cases
    | Node (n, exps) -> FromNode(n, List.map simplifyAdd exps)
    | Leaf _ -> exp