两个函数

时间:2016-02-01 00:50:27

标签: scala recursion tail-recursion

我想对initConnection进行初始调用,调用getData以递归方式调用自身,直到需要刷新connection-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]

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

并获得有关initConnection不包含递归调用的错误。然而,这显然是无限递归的

1 个答案:

答案 0 :(得分:7)

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

Scala只保证优化Direct Tail-Recursion。这是因为至少有一些Scala打算运行的平台(特别是JVM)对高级控制流的支持有限。例如,JVM仅支持方法内GOTO,这意味着为了实现比Direct Tail-Recursion更强大的功能,你会遇到Clojure的设计者Rich Hickey所解释的问题:互操作性,性能,高级控制流程 - 选择二。而Scala的设计师选择了Interop和Performance over Proper Tail Calls,Mutual Tail-Decursion,Tail-Recursion Modulo Cons,或类似的更强大的保证。

[注意:你不能在JVM上实现Proper Tail-Calls的经常重复的口头禅是不正确的。 JVM上有很多Scheme实现,否则就会证明这一点。 的真实之处在于JVM规范本身不保证正确的尾调用。但实现它们的方法,即:不使用JVM的调用堆栈,实现自己的。但是,这可能会使您在Performance或Java Interop中花费很多,可能两者都有。 Scala的TailCall库是如何在JVM上实现Proper Tail-Calls的另一个例子:Trampolines。]

在您的initConnection的第一个版本中,递归调用不是Tail-Call,因为评估的最后一个调用是对response.onFailure的调用。

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

Tail-Calls本质上等同于GOTO,Tail-Recursion本质上等同于while循环(JVM上使用GOTO实现)。但是,JVM的GOTO仅在单个方法中。因此,如果Scala想要实现Proper Tail-Recursion,他们要么必须实现自己的堆栈而不使用JVM,要将所有相互递归的方法组合成一个单一的巨型方法,以便GOTO有效,使用异常作为蹦床或做类似讨厌的东西,所有这些都会破坏Java Interop,Performance或两者兼而有之。

由于JVM是Scala设计人员的重要平台,而性能和平台Interop是重要目标,他们选择放弃有用的语言功能而不是放弃强大的平台。在规范中强制要求正确的尾部递归会使Scala在2015年之前的JVM或ECMAScript等平台上实际上无法实现。 (或者更确切地说,它将禁止高性能,高度互操作的实现。)