这些语言“本地”不支持相互递归的功能优化,所以我想它一定是蹦床或.. he ..重写为循环)我会错过什么吗?

更新:看来我确实对FSHARP说谎,但是我只是在搜索时没有看到相互尾声的示例

有帮助吗?

解决方案

首先,f#本地支持相互递归的功能,因为它可以从中受益 tailcall .net IL中可用的说明(MSDN)。但是,这有点棘手,可能对.NET的某些替代实现(例如紧凑型框架)不起作用,因此您有时可能需要手动处理此操作。

总的来说,我有几种处理方法:

  • 蹦床 - 当递归深度太高并实现处理异常的顶级循环时(例外将带有信息以恢复呼叫)时,请引发异常。而不是异常,您还可以简单地返回一个值,以指定该函数应再次调用。

  • 使用计时器放开 - 当递归深度太高时,您会创建一个计时器,并给它一个回调,该回调将在很短的时间后由计时器调用(计时器将继续递归,但使用的堆栈将丢弃)。

    可以使用存储需要完成的工作的全局堆栈可以完成同样的事情。您不会安排计时器,而是将功能添加到堆栈中。在顶级,该程序将从堆栈中挑选功能并运行它们。

为了给出第一个技术的具体示例,在f#中,您可以写下:

type Result<´T> =
  | Done of ´T
  | Call of (unit -> ´T)

let rec factorial acc n = 
  if n = 0 then Done acc
  else Call(fun () -> factorial (acc * n) (n + 1))

这也可用于相互递归的功能。命令式循环只会称呼 f 存储在功能中 Call(f) 直到它产生 Done 最终结果。我认为这可能是实施此问题的最干净的方法。

我敢肯定,还有其他用于处理此问题的复杂技术,但是这是我知道的两个(我使用的)。

其他提示

在Scala 2.8上 scala.util.control.TailCalls:

import scala.util.control.TailCalls._ 

def isEven(xs: List[Int]): TailRec[Boolean] = if (xs.isEmpty) 
    done(true) 
  else 
    tailcall(isOdd(xs.tail)) 

def isOdd(xs: List[Int]): TailRec[Boolean] = if (xs.isEmpty) 
    done(false) 
  else 
    tailcall(isEven(xs.tail)) 

isEven((1 to 100000).toList).result

只是为了付出f#相互递归时的代码方便:

let rec isOdd x =
    if x = 1 then true else isEven (x-1)
and isEven x = 
    if x = 0 then true else isOdd (x-1)

printfn "%A" (isEven 10000000)

如果您没有尾巴调用(“调试”模式中的默认值,它将保留堆栈以进行更轻松的调试),这将堆叠在一起,但在使用尾部调用(默认为“发布”模式中的默认值)时运行正常。编译器默认情况下进行尾巴调用(请参阅 -TailCalls选项),大多数平台上的.NET实现荣誉。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top