【问题标题】:Default recursion on recursive types递归类型的默认递归
【发布时间】:2015-03-29 14:16:38
【问题描述】:

惯用的 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 可能是某种高阶函数,它使用反射来查找类型定义等?

【问题讨论】:

    标签: recursion f#


    【解决方案1】:

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

    不过,您可以使用多种模式来隐藏重复。我觉得特别好的一个是基于ExprShape module from standard F# libraries。这个想法是定义一个活动模式,让您可以将类型视为叶(没有嵌套子表达式)或节点(具有子表达式列表):

    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
    

    【讨论】:

      猜你喜欢
      • 2015-05-25
      • 2018-06-20
      • 2018-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-12-29
      • 1970-01-01
      相关资源
      最近更新 更多