标签: tail-call-optimization

Clojure中的尾部呼叫消除?

有人可以将此(plt)Scheme代码重写为Clojure吗?

(define (f n)
   (printf "(f ~a)~n" n)
   (g n))

(define (g n)
   (printf "(g ~a)~n" n)
   (h n))

(define (h n)
   (printf "(h ~a)~n" n)
   (f (+ n 1)))
Run Code Online (Sandbox Code Playgroud)

以这种方式不会将程序f,g和h一起折叠并允许代码无限期地运行而不会崩溃?

scheme tail-recursion clojure tail-call-optimization

17
推荐指数
1
解决办法
639
查看次数

实现尾部呼叫消除的一些好方法是什么?

我用C/C++的混合编写了一个小的Scheme解释器,但我还没有实现正确的尾调用.

我知道MTA算法上的经典切尼,但还有其他很好的实现方法吗?我知道我可以将Scheme堆栈放在堆上,但这仍然不能正确消除,因为标准表示应该支持无限数量的活动尾部调用.

我也摆弄了longjmps,但到目前为止,我认为它只适用于非相互递归尾调用.

基于C的主要方案如何实现正确的尾递归?

c lisp scheme trampolines tail-call-optimization

17
推荐指数
3
解决办法
3743
查看次数

(正向)管道操作员是否可以阻止尾调用优化?

对于工作中的参数优化问题,我写了一个遗传算法来找到一些好的设置,因为蛮力解决方案是不可行的.不幸的是,当我早上回来时,大部分时间我都会被送到StackOverflowException.

我已经使用F#已经有一段时间了所以我知道TCO和需要带累加器参数的函数,并且通常使用该形式.

经过大量的搜索,我认为我能够找到触发异常的代码:

breedPopulation alive |> simulate (generation + 1) lastTime ewma
Run Code Online (Sandbox Code Playgroud)

breedPopulation从当前个体中生成新一代alive.然后通过调用开始下一轮/生成simulate.当我看到反汇编(总noob)时,我发现了一些pop和a ret,所以它看起来不像是对我的常规(非尾部)调用.

mov         rcx,qword ptr [rbp+10h]  
mov         rcx,qword ptr [rcx+8]  
mov         rdx,qword ptr [rbp-40h]  
cmp         dword ptr [rcx],ecx  
call        00007FFA3E4905C0  
mov         qword ptr [rbp-0F0h],rax  
mov         r8,qword ptr [rbp-0F0h]  
mov         qword ptr [rbp-80h],r8  
mov         r8,qword ptr [rbp-78h]  
mov         qword ptr [rsp+20h],r8  
mov         r8d,dword ptr [rbp+18h]  
inc         r8d  
mov         rdx,qword ptr [rbp+10h]  
mov         r9,qword ptr [rbp-20h]  
mov         rcx,7FFA3E525960h  
call        00007FFA3E4A5040 …
Run Code Online (Sandbox Code Playgroud)

stack-overflow f# cil tail-recursion tail-call-optimization

17
推荐指数
1
解决办法
236
查看次数

我怎么知道函数是否在F#中是尾递归的

我写了以下函数:

let str2lst str =
    let rec f s acc =
      match s with
        | "" -> acc
        | _  -> f (s.Substring 1) (s.[0]::acc)
    f str []
Run Code Online (Sandbox Code Playgroud)

如何知道F#编译器是否将其转换为循环?有没有办法在不使用Reflector的情况下找出答案(我没有使用Reflector的经验而且我不知道C#)?

编辑:另外,是否可以在不使用内部函数的情况下编写尾递归函数,或者循环是否需要驻留?

另外,F#std lib中是否有一个函数可以多次运行给定函数,每次都将最后一个输出作为输入?让我说我有一个字符串,我想在字符串上运行一个函数然后再次在结果字符串上运行它等等...

recursion f# tail-recursion tail-call-optimization

15
推荐指数
1
解决办法
2340
查看次数

Java 8是否有尾调用优化?

我尝试在网上挖掘以解答我的问题.我找到了一些与达芬奇项目有关的文件.这被标记为JSR 292,它与在JVM中包含闭包有关.这个项目是否实现了,它是Java 8的一部分吗?

java tail-call-optimization java-8

14
推荐指数
2
解决办法
9512
查看次数

MATLAB是否执行尾调用优化?

我最近学习了Haskell,并试图在可能的情况下将纯函数式传递给我的其他代码.这方面的一个重要方面是将所有变量视为不可变,即常量.为了做到这一点,许多将使用命令式循环实现的计算必须使用递归来执行,这通常由于为每个函数调用分配新的堆栈帧而导致存储器损失.在尾调用的特殊情况下(其中被调用函数的返回值立即返回给被调用者的调用者),然而,这种惩罚可以被称为尾调用优化的过程绕过(在一种方法中,这可以通过在正确设置堆栈后,基本上用jmp替换一个调用).MATLAB默认执行TCO,还是有办法告诉它?

matlab functional-programming tail-recursion tail-call-optimization

13
推荐指数
1
解决办法
2258
查看次数

为什么尾调用优化需要操作码?

所以我之前已经阅读了很多次,技术上.NET 确实支持尾调用优化(TCO),因为它有操作码,只有C#不生成它.

我不确定为什么TCO需要操作码或它会做什么.据我所知,能够进行TCO的要求是递归调用的结果不与当前函数范围中的任何变量组合.如果你没有那个,那么我看不到操作码如何阻止你必须保持堆栈框架打开.如果你有,那么编译器是否总能轻易地将其编译为迭代的东西?

那么操作码有什么意义呢?显然有一些我不知道的东西.在完全可以使用TCO的情况下,不能总是在编译器级别处理,而不是在操作码级别处理?什么是不能的例子?

.net c# theory cil tail-call-optimization

13
推荐指数
1
解决办法
436
查看次数

lua中的尾调用优化

Lua声称它正确实现尾调用,因此不需要为每个调用维护堆栈,因此允许无限递归,我试图写一个求和函数,一个不是尾调用,一个是尾调用:

非tailcall版本

function sum(n)
    if n > 0 then
        return n + sum(n-1)
    end
end

print(sum(1000000))
Run Code Online (Sandbox Code Playgroud)

stackoverflow正如预期的那样.

tailcall版本

function sum2(accu, n)
    if n > 0 then
        accu.value = accu.value + n
        sum2(accu, n-1)
    end
end
local accu = {value = 0}
sum2(accu, 1000000)
print(accu.value)
Run Code Online (Sandbox Code Playgroud)

我想在这种情况下不存在stackoverflow,因为它是一个tailcall,但由于stackoverflow它仍然失败:

/bin/lua/5.1.4/bin/lua: tailcall.lua:13: stack overflow
stack traceback:
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function 'sum2'
        tailcall.lua:13: in function …
Run Code Online (Sandbox Code Playgroud)

lua tail-recursion tail-call-optimization

12
推荐指数
1
解决办法
2793
查看次数

额外的蜥蜴和尾巴的目的是什么.在F#实现与C#?

以下C#函数:

T ResultOfFunc<T>(Func<T> f)
{
    return f();
}
Run Code Online (Sandbox Code Playgroud)

毫不奇怪地汇编到这个:

IL_0000:  ldarg.1     
IL_0001:  callvirt    05 00 00 0A 
IL_0006:  ret  
Run Code Online (Sandbox Code Playgroud)

但是等效的F#功能:

let resultOfFunc func = func()
Run Code Online (Sandbox Code Playgroud)

汇编到这个:

IL_0000:  nop         
IL_0001:  ldarg.0     
IL_0002:  ldnull      
IL_0003:  tail.       
IL_0005:  callvirt    04 00 00 0A 
IL_000A:  ret 
Run Code Online (Sandbox Code Playgroud)

(两者都处于发布模式).开头有一个额外的小窍门,我并不太好奇,但有趣的是附加ldnulltail.说明.

我的猜测(可能是错误的)是ldnull必要的,如果函数是void这样,它仍然返回something(unit),但这并不能解释tail.指令的目的是什么.如果函数确实在栈上推送了某些东西会发生什么呢?是不是它会被一个不会弹出的额外null所困?

c# f# cil tail-recursion tail-call-optimization

12
推荐指数
1
解决办法
211
查看次数

为什么尾调用优化不在这里?

我们使用递归来查找因子并且正在接收StackOverflow异常.我们已经读过x64计算机上的C#编译器执行尾调用优化:

在运行优化代码而不是调试时,JIT肯定会执行tailcals.

跑步dotnet --configuration release在我们的计划中得到了这么多:

...                      
7214 is a factor of 1234567890
7606 is a factor of 1234567890
10821 is a factor of 1234567890
11409 is a factor of 1234567890                

Process is terminated due to StackOverflowException.
Run Code Online (Sandbox Code Playgroud)

为什么没有发生尾调用优化?

class Program
{
    static void Main(string[] args)
    {
        const long firstCandidate = 1;
        WriteAllFactors(1234567890, firstCandidate);
    }

    private static void WriteAllFactors(long number, long candidate)
    {
        if (number % candidate == 0)
        {
            System.Console.WriteLine($"{candidate} is a factor of {number}");
        }

        candidate …
Run Code Online (Sandbox Code Playgroud)

c# recursion tail-call-optimization factorization

12
推荐指数
1
解决办法
272
查看次数