标签: tail-recursion

递归序列会泄漏内存吗?

我喜欢递归地定义序列,如下所示:

let rec startFrom x =
    seq {
        yield x;
        yield! startFrom (x + 1)
    }
Run Code Online (Sandbox Code Playgroud)

我不确定这样的递归序列是否应该在实践中使用.的yield! 出现是尾递归,但我不知道100%的自正在从另一个IEnumerable的内部调用.从我的角度来看,代码在每次调用时都会创建一个IEnumerable实例而不关闭它,这实际上会使这个函数泄漏内存.

这个功能会泄漏内存吗?就此而言,它甚至是"尾递归"?

[编辑添加]:我正在摸着NProf寻找答案,但我认为在SO上获得关于递归序列实现的技术解释是有帮助的.

f# memory-leaks tail-recursion sequence

10
推荐指数
1
解决办法
1884
查看次数

erlangs递归函数不仅仅是一个goto?

只是想直截了当.考虑这个Erlang代码的例子:

 test() ->
      receive
          {From, whatever} ->
                %% do something
               test();
          {From, somethingelse} ->
               %% do something else
               test();
 end.
Run Code Online (Sandbox Code Playgroud)

是不是test()调用,只是一个转到?

我问这个是因为在C中我们了解到,如果你进行函数调用,返回位置总是放在堆栈上.我无法想象这里的Erlang一定是这种情况,因为这会导致堆栈溢出.

基本的.我们有两种不同的调用函数的方法:goto和gosub.goto刚刚把程序流引导到其他地方,gosub记得你来自哪里,所以你可以回来.

考虑到这种思维方式,我可以更容易地看一下erlang的递归,因为如果我只读:test()作为goto,那么根本就没有问题.

因此我的问题:不是erlang只是使用goto而不是记住堆栈上的返回地址?

编辑:

只是为了澄清我的观点:

我知道goto可以用在某些语言中来跳过这个地方.但是,只是suupose而不是someFunction()你也可以这样做:在第一个例子中返回流返回的一些函数(),在第二个例子中,流程只在someFunction中继续并且永远不会返回.

所以我们通过跳转到方法起点来限制正常的GOTO行为.

如果你这样看,那么erlang递归函数调用看起来就像一个goto.

(在我看来,goto是一个函数调用,无法返回你来自的地方).这正是erlang示例中正在发生的事情.

erlang recursion tail-recursion goto

10
推荐指数
2
解决办法
1474
查看次数

递归开销 - 有多严重?

可能重复:
递归是否比循环更快?

大约15年前,我第一次接受C语言培训.我的雇主想要高度优化的代码来处理计算困难的任务.我记得不止一次被建议将递归重写为循环,即使是在昂贵的可读性方面,以避免"递归开销".正如我所理解的那样,递归开销是将数据推送到堆栈然后将其弹出所需的额外工作.

现在我用C,Python,Perl编写,有时用Java编写代码,我有时会想到递归.还有什么东西可以通过重写来获得吗?如果它们是尾递归怎么办?现代编译器让所有这些问题都没有实际意义吗?这些担忧是否与解释语言无关?

optimization recursion programming-languages tail-recursion interpreted-language

10
推荐指数
1
解决办法
7151
查看次数

使用最终工作流程时,缺少尾调用优化是一个障碍吗?

我正在使用F#规范的最终工作流程的修改版本,以便在Xbox上进行开发.看来,Xbox上的.net框架不支持尾调用.因此,我必须在编译时禁用尾调用优化.

虽然最初似乎这种限制会阻止在计算表达式中使用任何形式的循环,但我最初认为"步进"可以避免这个问题:计算表达式中的递归函数f不直接调用自身,而是返回一个包含lambda的最终值,该lambda调用f.

实验表明我对while循环是正确的(它们在计算表达式中使用时不会导致堆栈溢出),但不是关于递归函数.

为了澄清,这有效:

// Wait until "start" or "A" is pressed on one of the gamepads.
// Set "player" when that happens.
let player : PlayerIndex option ref = ref None
while (!player).IsNone do
    for p in all_players do
        let state = GamePad.GetState(p)
        if state.IsConnected
            && (state.Buttons.Start = ButtonState.Pressed
                || state.Buttons.A = ButtonState.Pressed) then
            player := Some p
    do! sys.WaitNextFrame()
Run Code Online (Sandbox Code Playgroud)

这会导致堆栈溢出:

// Wait until "start" is pressed on the controlling gamepad.
let rec wait() = task {
    input.Update()
    if …
Run Code Online (Sandbox Code Playgroud)

f# tail-recursion tail-call-optimization

10
推荐指数
2
解决办法
581
查看次数

在clojure中的尾递归

这是一个使用尾递归的lisp代码.

(defun factorial (f n)
    (if (= n 1)
        f
        (factorial (* f n) (- n 1))))
Run Code Online (Sandbox Code Playgroud)

我把它翻译成clojure代码,期望相同的尾递归优化.

(defn fact [f n]
    (if (= n 1)
        f
        (fact (* f n) (dec n))))
Run Code Online (Sandbox Code Playgroud)

但是我得到了这个整数溢出(不是堆栈溢出),即使是很小的数字,如(fact 1 30).

ArithmeticException integer overflow  clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374)
Run Code Online (Sandbox Code Playgroud)

我尝试过recur,但得到了同样的错误.

(defn factorial [f n]
    (if (= n 1)
        f
        (recur (* f n) (dec n))))
Run Code Online (Sandbox Code Playgroud)

clojure代码有什么问题?

recursion tail-recursion clojure

10
推荐指数
1
解决办法
2650
查看次数

Prolog性能和递归类型

我正在玩permutation几个程序,并偶然发现了这个小实验:

置换方法1:

permute([], []).
permute([X|Rest], L) :-
    permute(Rest, L1),
    select(X, L, L1).
Run Code Online (Sandbox Code Playgroud)

置换方法2:

permute([], []).
permute(L, [P | P1]) :-
    select(P, L, L1),
    permute(L1, P1).
Run Code Online (Sandbox Code Playgroud)

置换方法3(使用内置):

permute(L, P) :- permutation(L, P).
Run Code Online (Sandbox Code Playgroud)

我知道使用尾递归是一种好习惯,通常使用内置函数应该是有效的.但是当我运行以下内容时:

time(findall(P, permute([1,2,3,4,5,6,7,8,9], P), L)).
Run Code Online (Sandbox Code Playgroud)

我得到了以下结果,这些结果在几次运行中相对一致:

方法1:

% 772,064 inferences, 1.112 CPU in 2.378 seconds (47% CPU, 694451 Lips)
Run Code Online (Sandbox Code Playgroud)

方法2:

% 3,322,118 inferences, 2.126 CPU in 4.660 seconds (46% CPU, 1562923 Lips)
Run Code Online (Sandbox Code Playgroud)

方法3:

% 2,959,245 inferences, 1.967 CPU in 4.217 seconds (47% CPU, 1504539 Lips)
Run Code Online (Sandbox Code Playgroud)

因此,非尾递归方法非常实时有效.

一个特定的递归类型通常更实时有效,所有其他条件相同(我知道这并不总是一个简单的前提)?这个实验告诉我的是,我可能不想总是努力进行尾递归,但我可能需要首先进行性能分析,然后权衡性能优势与尾递归确实具有的其他好处.

performance tail-recursion list permutation prolog

10
推荐指数
1
解决办法
1338
查看次数

Scala:树插入复杂结构的尾递归

我在scala中创建了一个自定义对象树,我的insert方法抛出了一个堆栈溢出,因为它不是尾递归的.但是,我无法弄清楚如何使其尾递归.相关的例子我见过使用"累加器"变量,但是它们或者像Integers这样的东西可以被乘法和覆盖,或者我无法适应树的列表.这就是我所拥有的:

我的树木的基础:

abstract class GeoTree
case object EmptyTree extends GeoTree
case class Node(elem:GeoNode, left:GeoTree, right:GeoTree) extends GeoTree
Run Code Online (Sandbox Code Playgroud)

用于递归创建树的insert方法(导致堆栈溢出的方法):

  def insert(t:GeoTree, v: GeoNode): GeoTree = t match {
    case EmptyTree => new Node(v, EmptyTree, EmptyTree)
    case Node(elem:GeoNode, left:GeoTree, right:GeoTree) => {
      if (v < elem) new Node(elem, insert(left, v), right)
      else new Node(elem, left, insert(right, v))
    }
  }
Run Code Online (Sandbox Code Playgroud)

我不认为它的代码GeoNode实际上特别相关,因为它非常简单.这个类有两个Long属性和<,>以及==适当的树中使用重写运营商.有人可以提出如何使用累加器为我的insert功能,或其他一些方法使其尾递归?

binary-tree scala tail-recursion

10
推荐指数
1
解决办法
2026
查看次数

Haskell:二叉树深度的尾递归版本

首先,我有两个不同的实现,我认为是正确的,并且已经对它们进行了描述并认为它们具有相同的性能:

depth::Tree a -> Int
depth Empty        = 0
depth (Branch b l r) = 1 + max (depth l) (depth r)


depthTailRec::Tree a -> Int
depthTailRec = depthTR 0 where
           depthTR d Empty          = d 
           depthTR d (Branch b l r) = let dl = depthTR (d+1) l; dr = depthTR (d+1) r in max dl dr 
Run Code Online (Sandbox Code Playgroud)

我只是想知道是不是人们都在谈论尾部递归如何有利于性能?很多问题都在我脑海中浮现:

  1. 如何让深度功能更快?
  2. 我读到了关于Haskell的懒惰如何减少尾递归的需要,是真的吗?
  3. 事实是每个递归都可以转换成尾递归吗?
  4. 最后,尾递归可以更快,更节省空间,因为它可以转换为循环,从而减少了推送和弹出堆栈的需要,我的理解是正确的吗?

tree recursion binary-tree haskell tail-recursion

10
推荐指数
2
解决办法
3644
查看次数

JavaScript的尾递归优化?

我向所有人道歉,因为以前版本的这个模糊不清.有人决定对这个新女孩表示同情并帮我改写这个问题 - 这是一个我希望能够解决问题的更新(并且,感谢迄今为止所有那些慷慨解答的人):


问题

我是Uni的第一年,我是一名新的计算机科学专业的学生.对于我的算法类的最终项目,我们可以选择我们想要的任何语言,并实现一种"细化"/"效率"算法,该算法在本地(内部?)用另一种语言找到,但在我们选择的语言中缺失.

我们刚刚在课堂上研究了递归,我的教授简要地提到JavaScript没有实现Tail Recursion.从我的在线研究中,新的ECMA脚本6规范包含此功能,但它目前不在任何(/大多数?)JavaScript版本/引擎中?(对不起,如果我不确定哪个是......我是新来的).

我的任务是为缺少的功能提供2个(编码)WORK AROUND的选项.

所以,我的问题是......是否有人,比我更聪明,更有经验,对我如何实现以下方面有任何想法或例子:

解决了缺乏尾递归优化?

javascript algorithm optimization recursion tail-recursion

10
推荐指数
3
解决办法
1716
查看次数

(怎么样)我可以使这个monadic绑定尾递归?

我有这个名为Desync的monad -

[<AutoOpen>]
module DesyncModule =

    /// The Desync monad. Allows the user to define in a sequential style an operation that spans
    /// across a bounded number of events. Span is bounded because I've yet to figure out how to
    /// make Desync implementation tail-recursive (see note about unbounded recursion in bind). And
    /// frankly, I'm not sure if there is a tail-recursive implementation of it...
    type [<NoComparison; NoEquality>] Desync<'e, 's, 'a> =
        Desync of ('s -> 's * …
Run Code Online (Sandbox Code Playgroud)

monads f# tail-recursion computation-expression

10
推荐指数
1
解决办法
384
查看次数