【问题标题】:Tail-recursion on trees树上的尾递归
【发布时间】:2013-09-28 18:58:35
【问题描述】:

我有一个数据结构,

datatype 'a tree = Leaf | Branch of 'a tree * 'a * 'a tree

我想编写一个按某种顺序遍历这棵树的函数。它做什么都没关系,所以它可能是treefold : ('a * 'b -> 'b) -> 'b -> 'a tree -> 'b。我可以这样写这个函数:

fun treefold f acc1 Leaf = acc1
  | treefold f acc1 (Branch (left, a, right)) =
    let val acc2 = treefold f acc1 left
        val acc3 = f (a, acc2)
        val acc4 = treefold f acc3 right
    in acc4 end

但是因为我在最后一种情况下不可避免地有两个分支,所以这不是尾递归函数。

如果允许扩展类型签名,是否可以创建一个,并且成本是多少?我也想知道它是否值得尝试;也就是说,它在实践中是否会带来任何速度优势?

【问题讨论】:

标签: functional-programming sml


【解决方案1】:

您可以使用连续传递样式实现尾递归树折叠:

fun treefold1 f Leaf acc k = k acc
  | treefold1 f (Branch (left, a, right)) acc k =
    treefold1 f left acc (fn x => treefold1 f right (f(a, x)) k)

fun treefold f t b = treefold1 f t b (fn x => x)

例如:

fun sumtree t = treefold op+ t 0

val t1 = Branch (Branch(Leaf, 1, Leaf), 2, Branch (Leaf, 3, Leaf))

val n = sumtree t1

按预期得到 n = 6。

【讨论】:

    【解决方案2】:

    就像@seanmcl 所写,将函数转换为尾递归的系统方法是使用延续传递样式。

    之后,您可能想要具体化您的延续并使用更具体的数据类型,例如列表:

    fun treefoldL f init tree =
        let fun loop Leaf acc [] = acc
              | loop Leaf acc ((x, right) :: stack) =
                loop right (f(x,acc)) stack
              | loop (Branch (left, x, right)) acc stack =
                loop left acc ((x, right) :: stack)
        in  loop tree init [] end
    

    【讨论】:

    • 谢谢你,肯。我问的主要是因为我变得不耐烦,潜伏在其他人问 SML 的问题上,并认为我会问一个人们可能想回答的问题。 :) (似乎 H&R 的新 F# 书中增加了关于延续的新章节。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 2019-07-29
    • 2016-03-21
    • 2018-03-17
    • 1970-01-01
    相关资源
    最近更新 更多