Recursion 函数式编程,递归游戏状态循环

Recursion 函数式编程,递归游戏状态循环,recursion,functional-programming,Recursion,Functional Programming,我打算写一个简单的游戏来测试我对函数式编程的理解。执行主循环的功能方法是递归,但这不会随着越来越多的堆栈帧的生成而消耗内存吗 谢谢 来自 您已经使用实现了循环函数,即对循环的递归调用是函数中的最后一项。这允许编译器/解释器(取决于语言)立即弹出当前堆栈帧,并用递归调用的帧替换它 长话短说,按照您实现它的方式,不会出现堆栈溢出,循环可以根据需要运行多长时间。递归是新的迭代:)博客插件: 你说你正在使用Clojure,并且很想知道F 事实证明,基于JVM的语言(Java、Scala、Clojure等

我打算写一个简单的游戏来测试我对函数式编程的理解。执行主循环的功能方法是递归,但这不会随着越来越多的堆栈帧的生成而消耗内存吗

谢谢

来自


您已经使用实现了
循环
函数,即对
循环
的递归调用是函数中的最后一项。这允许编译器/解释器(取决于语言)立即弹出当前堆栈帧,并用递归调用的帧替换它


长话短说,按照您实现它的方式,不会出现堆栈溢出,
循环可以根据需要运行多长时间。

递归是新的迭代:)博客插件:

你说你正在使用Clojure,并且很想知道F

事实证明,基于JVM的语言(Java、Scala、Clojure等)无法在VM级别支持尾部调用优化,因此有一些变通方法,如Clojure的
recur
。基于CLR的语言(F#,C#,VB,…)可以并且确实在IL中使用
.tail
标记,以导致堆栈帧的早期丢弃


尾部调用优化会让调试变得很痛苦,因此例如F#在调试构建中不会这样做(但在发行版中会这样做)。项目设置中有一个复选框可在调试中启用。

您使用的是哪种函数式语言?Clojure,但如果它取决于语言,我也很想知道F#的用法。要进一步了解这一点,请参阅“确定”,因此可以假设这在大多数语言中都会发生(甚至是命令式的)?如果递归调用不是最后一件事,有什么方法可以做到这一点吗?@Peter这种优化通常不会针对命令式语言执行。Clojure也不执行此操作,但作为替代,Clojure提供了
recur
表单,您可以使用该表单代替当前函数的名称进行递归,而不消耗堆栈空间(
recur
只能在尾部位置使用)。如果递归调用不在尾部位置,则只能通过重写它来避免消耗堆栈空间,从而使其处于尾部位置(请注意,如果不能简单地这样做,那么在没有显式堆栈的情况下,也无法简单地将逻辑表示为循环)“@Peter重要的是,打电话的人必须在给被叫人打电话后立即返回。类似这样的东西也是尾部递归的:
如果x那么循环(…)否则循环(…)
;但不是这样:
if x then(loop(…);17)else loop(…)
请注意,JVM语言不能支持一般的尾部调用优化,但可以支持更严格的尾部递归优化。斯卡拉有。使用像
recur
这样的显式尾部递归运算符的原因是,如果您期望的尾部递归实际上不是这样,那么您会收到一个错误,提醒您注意这个事实,而如果发生这种情况,隐式尾部递归消除可能(最终)获取关于不应该发生的堆栈溢出的错误报告。是的,您当然可以执行分支,而不是调用简单(非相互)递归。当您想要执行一个调用并消除堆栈帧时,JVM就不走运了。
// imperative version
pacman = new pacman(0, 0)
while true
    if key = UP then pacman.y++
    elif key = DOWN then pacman.y--
    elif key = LEFT then pacman.x--
    elif key = UP then pacman.x++
    render(pacman)

// functional version
let rec loop pacman =
    render(pacman)
    let x, y = switch(key)
        case LEFT: pacman.x - 1, pacman.y
        case RIGHT: pacman.x + 1, pacman.y
        case UP: pacman.x, pacman.y - 1
        case DOWN: pacman.x, pacman.y + 1
    loop(new pacman(x, y))