2013-03-11 61 views
3

想我必须在今后恢复长时间运行的计算,可以挂起自身的机制:如何使尾recusive方法也可以指本身在非尾递归的方式

sealed trait LongRunning[+R]; 
case class Result[+R](result: R) extends LongRunning[R]; 
case class Suspend[+R](cont:() => LongRunning[R]) extends LongRunning[R]; 

如何运行它们最简单的方法是

@annotation.tailrec 
def repeat[R](body: LongRunning[R]): R = 
    body match { 
    case Result(r) => r 
    case Suspend(c) => { 
     // perhaps do some other processing here 
     println("Continuing suspended computation"); 
     repeat(c()); 
    } 
    } 

问题是创建这样的计算。比方说,我们要实现尾递归阶乘是暂停其计算每10个循环:

@annotation.tailrec 
def factorial(n: Int, acc: BigInt): LongRunning[BigInt] = { 
    if (n <= 1) 
    Result(acc); 
    else if (n % 10 == 0) 
    Suspend(() => factorial(n - 1, acc * n)) 
    else 
    factorial(n - 1, acc * n) 
} 

但是,这并不编译:

error: could not optimize @tailrec annotated method factorial : it contains a recursive call not in tail position

Suspend(() => factorial(n - 1, acc * n)) 

如何留住在非暂停通话尾递归?

+0

仅供参考:'LongRunning'是偏袒单子! – 2013-03-11 17:44:27

+0

@MyseriousDan谢谢,这很有趣。实际上,'LongRunning'是对我原来的问题的简化 - 我正在为Scala [scala-conduit](https://hackage.haskell.org/package/conduit) //github.com/petr/scala-conduit)其中'Pipe'自然形成单子。 – 2013-03-11 18:13:17

+0

是的,这种事通常是某种自由monad的实例化。偏袒者有点奇怪,因为它通常表示为核心迁移的thunk,但是否具有'Free [()=> _,R]或Free [ChunkOfData => _,R]'几乎没有什么根本区别。 – 2013-03-11 18:16:28

回答

4

我找到了一个可能的答案。我们可以将尾递归部分进入内部函数,并参考外一个非尾递归,当我们需要:

def factorial(n: Int, acc: BigInt): LongRunning[BigInt] = { 
    @annotation.tailrec 
    def f(n: Int, acc: BigInt): LongRunning[BigInt] = 
    if (n <= 1) 
     Result(acc); 
    else if (n % 10 == 0) 
     Suspend(() => factorial(n - 1, acc * n)) 
    else 
     f(n - 1, acc * n) 
    f(n, acc) 
} 
+0

我说得对,在每个'factorial'调用上重新创建闭包*实例*? – 2013-03-11 09:08:24

+0

@ om-nom-nom我不太确定Scala如何优化这种关闭。无论如何,它们只是为了暂停而创建的,而这种情况并不经常发生。 (这个例子有点简化。)但是我可以从我的观察中得知内存占用量保持不变。我测试了另一个类似的计算,使用了1000000个暂停。它运行速度非常快,只消耗很少的内存。 – 2013-03-11 09:35:59

+0

“我说得对,这将在每个阶乘调用上重新创建闭包实例”:否,闭包仅在暂停时创建。当'f'调用它自己时,调用确实是尾递归的,因此没有进一步的堆栈使用(因此没有堆栈溢出)。 – 2013-03-12 09:31:17