【问题标题】:What is the standard way to optimise mutual recursion in F#/Scala?在 F#/Scala 中优化相互递归的标准方法是什么?
【发布时间】:2010-05-09 19:16:22
【问题描述】:

这些语言“本机”不支持相互递归函数优化,所以我猜它一定是蹦床或者..呵呵..重写为循环)我错过了什么吗?

更新:似乎我确实对 FSharp 撒了谎,但我只是没有看到谷歌搜索时相互尾随的示例

【问题讨论】:

    标签: optimization scala f# mutual-recursion trampolines


    【解决方案1】:

    首先,F# 本身就支持相互递归函数,因为它可以受益于 .NET IL (MSDN) 中提供的 tailcall 指令。但是,这有点棘手,并且可能不适用于 .NET 的某些替代实现(例如 Compact Frameworks),因此您有时可能需要手动处理。

    总的来说,我认为有几种方法可以处理它:

    • Trampoline - 当递归深度太高时抛出异常并实现处理异常的顶级循环(异常将携带信息以恢复调用)。除了异常,您还可以简单地返回一个值,指定应该再次调用该函数。

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

      使用存储需要完成的工作的全局堆栈也可以完成同样的事情。您可以将函数添加到堆栈中,而不是调度计时器。在顶层,程序将从堆栈中选择函数并运行它们。

    举一个第一种技术的具体例子,在 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))
    

    这也可以用于相互递归的函数。命令式循环将简单地调用存储在Call(f) 中的f 函数,直到它产生带有最终结果的Done。我认为这可能是实现这一点的最干净的方式。

    我确信还有其他复杂的技术可以解决这个问题,但我知道(并且我使用过)这两个。

    【讨论】:

      【解决方案2】:

      在 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
      

      【讨论】:

      • 如果有人好奇,我相信TailCalls 库实现了蹦床。如果我错了,请有人纠正我。
      【解决方案3】:

      只是为了让你在 Bing for 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)
      

      如果您在没有尾调用的情况下进行编译(“调试”模式下的默认设置,它会保留堆栈以便于调试),这将导致 StackOverflow,但在使用尾调用(“发布”模式下的默认设置)编译时运行得很好。编译器默认执行尾调用(参见--tailcalls option),大多数平台上的 .NET 实现都支持它。

      猜你喜欢
      • 1970-01-01
      • 2013-02-03
      • 2017-06-19
      • 1970-01-01
      • 1970-01-01
      • 2011-01-25
      • 1970-01-01
      • 2021-01-02
      • 1970-01-01
      相关资源
      最近更新 更多