Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 如何使尾部递归方法也可以以非尾部递归的方式引用自身_Scala_Recursion_Tail Recursion - Fatal编程技术网

Scala 如何使尾部递归方法也可以以非尾部递归的方式引用自身

Scala 如何使尾部递归方法也可以以非尾部递归的方式引用自身,scala,recursion,tail-recursion,Scala,Recursion,Tail Recursion,假设我有一个长时间运行的计算机制,可以暂停它们自己,以便稍后恢复: 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

假设我有一个长时间运行的计算机制,可以暂停它们自己,以便稍后恢复:

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)
}

如何在非挂起调用上保留尾部递归?

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

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)
}
def阶乘(n:Int,acc:BigInt):长时间运行[BigInt]={
@注释.tailrec
def(n:Int,acc:BigInt):长时间运行[BigInt]=
if(n阶乘(n-1,acc*n))
其他的
f(n-1,附件*n)
f(n,acc)
}

我说的对吗?这将在每个
阶乘调用上重新创建闭包实例?@om nom nom我不确定Scala对此类闭包的优化程度如何。不管怎么说,它们只是为停赛而设计的,停赛并不经常发生。(这个例子有点简化。)但我可以从我的观察中说,内存占用保持不变。我测试了另一个类似的计算,使用了1000000个悬浮液。它运行得非常快,只消耗了很少的内存。“我说的对吗,这将在每个阶乘调用上重新创建闭包实例”:不,闭包只在挂起时创建。当
f
调用自身时,调用实际上是尾部递归的,因此没有进一步的堆栈使用(因此没有堆栈溢出)。仅供参考:
longlunning
是偏爱单子@谢谢,这很有趣。实际上,
longlunning
是对我最初问题的简化——我正在为Scala开发一个类似于库的库,其中
Pipe
自然形成一个monad。是的,这种东西通常是某种免费monad的实例化。偏爱一个有点奇怪,因为它通常表示为一个corecursive thunk,但是无论您是否有
Free[()=>,R]
Free[ChunkOfData=>\uUr]
都没有什么根本区别。另外,请注意,这实际上已经存在于标准库中:
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)
}