有人可以将此(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一起折叠并允许代码无限期地运行而不会崩溃?
对于工作中的参数优化问题,我写了一个遗传算法来找到一些好的设置,因为蛮力解决方案是不可行的.不幸的是,当我早上回来时,大部分时间我都会被送到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) 我写了以下函数:
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中是否有一个函数可以多次运行给定函数,每次都将最后一个输出作为输入?让我说我有一个字符串,我想在字符串上运行一个函数然后再次在结果字符串上运行它等等...
我尝试在网上挖掘以解答我的问题.我找到了一些与达芬奇项目有关的文件.这被标记为JSR 292,它与在JVM中包含闭包有关.这个项目是否实现了,它是Java 8的一部分吗?
我最近学习了Haskell,并试图在可能的情况下将纯函数式传递给我的其他代码.这方面的一个重要方面是将所有变量视为不可变,即常量.为了做到这一点,许多将使用命令式循环实现的计算必须使用递归来执行,这通常由于为每个函数调用分配新的堆栈帧而导致存储器损失.在尾调用的特殊情况下(其中被调用函数的返回值立即返回给被调用者的调用者),然而,这种惩罚可以被称为尾调用优化的过程绕过(在一种方法中,这可以通过在正确设置堆栈后,基本上用jmp替换一个调用).MATLAB默认执行TCO,还是有办法告诉它?
matlab functional-programming tail-recursion tail-call-optimization
所以我之前已经阅读了很多次,技术上.NET 确实支持尾调用优化(TCO),因为它有操作码,只有C#不生成它.
我不确定为什么TCO需要操作码或它会做什么.据我所知,能够进行TCO的要求是递归调用的结果不与当前函数范围中的任何变量组合.如果你没有那个,那么我看不到操作码如何阻止你必须保持堆栈框架打开.如果你有,那么编译器是否总能轻易地将其编译为迭代的东西?
那么操作码有什么意义呢?显然有一些我不知道的东西.在完全可以使用TCO的情况下,不能总是在编译器级别处理,而不是在操作码级别处理?什么是不能的例子?
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) 以下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)
(两者都处于发布模式).开头有一个额外的小窍门,我并不太好奇,但有趣的是附加ldnull和tail.说明.
我的猜测(可能是错误的)是ldnull必要的,如果函数是void这样,它仍然返回something(unit),但这并不能解释tail.指令的目的是什么.如果函数确实在栈上推送了某些东西会发生什么呢?是不是它会被一个不会弹出的额外null所困?
我们使用递归来查找因子并且正在接收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)