Recursion 递归类型的默认递归

Recursion 递归类型的默认递归,recursion,f#,Recursion,F#,惯用的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 w

惯用的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
。。。哎呀,这并不像写的那样有效<代码>简化\u添加需要在子表达式中重复出现。在这个非常简单的玩具示例中,只需要额外的几行代码,但在实际的程序中会有几十种表达式类型;人们更愿意避免在每个对表达式进行操作的函数中添加几十行样板文件

有没有办法表达“默认情况下,在子表达式上重复出现”?比如:

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
可能是某种使用反射来查找类型定义或类似内容的高阶函数?

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

但是,可以使用各种模式来隐藏重复。一个我觉得特别好的是基于。其思想是定义一个活动模式,该模式为您提供类型的视图,即叶(没有嵌套的子表达式)或节点(有子表达式列表):

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

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
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