当前位置: 首页 > 知识库问答 >
问题:

简单rosetree上的尾部递归映射

充昌勋
2023-03-14

我正在努力将非平凡函数映射为尾递归。

就拿简单的玫瑰树来说

type Tree<'a> = 
    | Leaf of 'a
    | Branch of List<Tree<'a>>

带地图

let rec map : ('a -> 'b) -> Tree<'a> -> Tree<'b> = 
    fun f -> 
        function
        | Leaf a -> 
            f a 
            |> Leaf
        | Branch xs ->
            xs 
            |> List.map (map f) 
            |> Branch

(更不用说绑定了)

使这种尾部递归似乎很痛苦,我已经看过了FSharpx之类的例子,但它们不是尾部递归的。我找到了这个

https://www.gresearch.co.uk/article/advanced-recursion-techniques-in-f/

但是,从基于连续性的最后一个示例开始的飞跃似乎相当符合他们的示例(max),我似乎无法理解它。

在某个地方是否有这个非常规范的示例的示例实现?

那么简单的部分应该是这样的

let map2 : ('a -> 'b) -> Tree<'a> -> Tree<'b> =
    fun f ta ->
        let rec innerMap : Tree<'a> -> (Tree<'b> -> Tree<'b>) -> Tree<'b> =
            fun ta cont ->
                match ta with
                | Leaf a ->
                    f a |> Leaf |> cont
        innerMap ta id

但我错过了branch的艰难时刻

共有1个答案

终波涛
2023-03-14

如果您使用链接中的建议,则实现如下:

let rec mapB : ('a -> 'b) ->  Tree<'a> -> (Tree<'b>-> Tree<'b>) -> Tree<'b> = 
    fun f ta k -> 

        match ta with
        | Leaf a -> k (Leaf (f a))
        | Branch ys ->
                       let continuations = ys |> List.map (mapB f)
                       let final (list:List<Tree<'b>>) = k (Branch list)
                       Continuation.sequence continuations final

正如您已经注意到的,叶子的情况很简单:应用F,将其包装回叶子并应用延续。

对于分支,我们生成了许多部分函数,用于映射分支中的子级。我们利用连续性。为我们执行这些部分函数的序列。然后,我们获取结果,将其包装在分支中,并应用(最终)延拓。

基本测试:

let t = Branch ([Leaf 3; Leaf 4;Leaf 5;Branch([Leaf 6])])
let t4 = mapB (fun x->x+1) t id

printfn "%A" t4

产量

Branch [Leaf 4; Leaf 5; Leaf 6; Branch [Leaf 7]]

顺便说一下,您想做什么?使用数千个场景运行Montecarlo模拟?即使使用您的原始实现,我也还没有遇到堆栈溢出错误。

 类似资料:
  • 我有一个关于如何将“递归”转换为“尾部递归”的问题。 这不是家庭作业,只是在我试图润色算法书籍中的递归定理时出现的一个问题。 我熟悉使用递归的两个典型示例(阶乘和斐波那契序列),也知道如何以递归方式和尾部递归方式实现它们。 我的代码如下(我使用Perl只是为了使其简单,但可以轻松地转换为C/Java/C)。 运行代码时,输出如下: 递归函数在返回之前使用不同的参数调用自己两次。我尝试了几种方法将其

  • 假设我编写这样的代码: 我如何让Kotlin优化这些相互递归的函数,以便在不抛出StackOverflower错误的情况下运行main?tailrec关键字适用于单函数递归,但没有更复杂的功能。我还看到一个警告,在使用关键字tailrec的地方没有找到尾部调用。也许这对编译器来说太难了?

  • 我对函数式编程很陌生,尤其是下面使用的Scheme。我正在尝试使以下函数是递归的,尾递归的。基本上,该函数的作用是对两个字符串的对齐方式进行评分。当给定两个字符串作为输入时,它会比较每个“列”字符,并根据在称为 scorer 的函数中实现的评分方案(由下面的代码中的函数调用)来累积该对齐的分数。 我有一个想法,用一个帮助函数来累积分数,但我不太确定如何去做,因此我该如何让下面的函数尾递归呢?

  • 尾递归优化 recur 尾递归优化是函数式编程不能缺少的一个性能优化方案. 没有尾递归, 常有的递归调用也会形成很深的调用栈消耗内存. cljs 和 Clojure 类似, 都需要通过声明 recur 进行优化. 最终代码会被编译为 white 结构的 js 代码,从而提高性能. (defn factorial [acc n] (if (<= n 1) acc (recur (* ac

  • 我想知道是否有一些通用方法可以用foo(…)转换“正常”递归foo(…) 作为尾部递归的最后一个调用。 例如(scala): 函数语言将递归函数转换为等价尾部调用的一般解决方案: 一种简单的方法是将非尾部递归函数包装在蹦床单子中。 所以pascal函数不再是递归函数。然而,蹦床单子是需要完成的计算的嵌套结构。最后,是一个尾递归函数,它遍历树状结构,解释它,最后在基本情况下返回值。 Rúnar Bj

  • 我想我理解了教科书中对尾部递归函数的定义:在函数调用后不执行任何计算的函数。我还发现,作为一个结果,尾部递归函数的内存效率会更高,因为它每次调用只需要一条记录,而不是每次都需要保留一条记录(就像在普通递归中那样)。 我不太清楚的是,这个定义如何应用于嵌套调用。我将提供一个例子: 我最初给出的答案是,根据定义,它不是尾部递归的(因为外部调用是在计算内部调用之后执行的,所以其他计算是在第一次调用之后完