scala - 两个函数之间的尾递归

标签 scala recursion tail-recursion

我想对 initConnection 进行初始调用,它会调用 getData,后者会递归调用自身,直到需要刷新连接 ID,然后再调用 initConnection。

@tailrec private def initConnection(): Unit =
  {
    val response: Future[Response] = initHeaders(WS.url(url)).post(auth)
    response.onSuccess {
      case resp => getData(20, resp.asInstanceOf[Response].header("connectionID").get)
    }
    response.onFailure {
      case resp => initConnection()
    }
  }

@tailrec private def getData(requestsLeft: Int, sessionId: String): Unit =
  {
    if (requestsLeft == 0)
      {
        initConnection()
      }
    else
      {
        //send request and process data
        getData(requestsLeft - 1, sessionId)
      }
  }

我在 IntelliJ 中收到“递归调用不在尾部位置”错误,仅针对 initConnection 函数。两个函数之间不能使用尾递归吗?还是只与我的 Future[Response] 有关?

我还尝试删除 Future[Response]

@tailrec private def initConnection(): Unit =
  {
    val response: Response = initHeaders(WS.url(url)).post(auth).value.get.get
    getData(20, response.header("ConnectionID").get)
  }

并得到一个关于 initConnectioncontaining no recursive calls 的错误。然而这显然是无限递归的

最佳答案

递归是指方法调用自身。直接递归是指方法直接调用自身。 Tail-Call 是在方法中评估的最后一次调用。 Tail-Recursion 是一种递归调用,也是一种 Tail-Call。 Direct Tail-Recursion 是一种直接递归调用,也是一种 Tail-Call。

Scala 只保证优化直接尾递归。这是因为至少有一些 Scala 打算运行的平台(尤其是 JVM)对高级控制流的支持有限。 JVM 只支持内部方法 GOTO ,例如,这意味着为了实现比直接尾递归更强大的东西,你会遇到 Clojure 的设计者 Rich Hickey 解释为的问题:互操作性、性能、高级控制流——选两个。 Scala 的设计者选择了互操作性和性能,而不是适当的尾调用、相互尾递归、尾递归模数缺点或类似的更强大的保证。

[注意:您不能在 JVM 上实现适当的尾调用这一经常重复的咒语是不正确的。 JVM 上有大量 Scheme 实现证明并非如此。 的是 JVM 规范本身不保证正确的尾调用。但是实现它们的方法,即:不要使用 JVM 的调用堆栈,实现你自己的。然而,这将使您在性能或 Java Interop 方面付出高昂的代价,可能两者都有。 Scala 的 TailCall库是另一个如何在 JVM 上实现适当的尾调用的示例:Trampolines。]

在您的第一个版本中 initConnection , 递归调用不是尾调用,因为计算的最后一个调用是对 response.onFailure 的调用.

在你的第二个版本中 initConnection ,根本没有递归调用,Tail-Call 是getData .


尾调用本质上等同于GOTO和尾递归本质上等同于 while循环(在 JVM 上使用 GOTO 实现)。但是,JVM 的 GOTO仅适用于 一个方法。因此,如果 Scala 想要实现适当的尾递归,他们要么必须实现自己的堆栈而不使用 JVM,将所有相互递归的方法组合到一个巨大的方法中,这样 GOTO有效,将异常用作蹦床或做类似的令人讨厌的事情,所有这些都会破坏 Java 互操作性、性能或两者。

由于 JVM 是 Scala 设计人员的重要平台,性能和平台互操作性是重要目标,因此他们宁愿放弃有用的语言功能也不愿放弃强大的平台。在规范中强制使用适当的尾递归将使 Scala 在 2015 年之前的 JVM 或 ECMAScript 等平台上几乎无法实现。 (或者更确切地说,它将禁止高性能、高度互操作的实现。)

关于scala - 两个函数之间的尾递归,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35121475/

相关文章:

scala - Sqs Akka Stream 内存不足

java - 如何扫描整数文件的下一行?

java - 使用递归和 Java 以及树结构多线程访问添加、获取、删除方法

Scala:具有复杂结构的树插入尾递归

scala - Chisel 中的 <> 运算符是什么?

scala - Scala 中下划线的用途有哪些?

java - 运行同时包含 Scala 代码的 Java 程序的最佳方式是什么?

algorithm - 在 Haskell 中内存最有效的方法是什么?

algorithm - 有人可以通过代码描述一个用迭代而不是递归回溯的实际例子吗?

Scala 列出了 "var"的用法