【问题标题】:Can This Be Made Tail Recursive?这可以做尾递归吗?
【发布时间】:2015-08-06 18:15:32
【问题描述】:

我正在访问一个返回分页 JSON 响应的 HTTP 端点。 'meta.next' 的值 在我的响应类型中指向响应的下一页。当此值为空时,没有更多页面 检索。 我正在使用 Spray IO 发出 HTTP 请求。我收集每个感兴趣的条目 页面响应并将其与迄今为止收集的内容连接起来。当下一个变成 null,我返回所有收集的条目。 我的问题:有没有办法让'getJson(...)'函数低于尾递归?

 case class JsonResponse(meta: Meta, items: List[Item])

 val pipeline: Future[HttpRequest => Future[JsonResponse]] = for (
    Http.HostConnectorInfo(connector, _) <-
    IO(Http) ? Http.HostConnectorSetup("somehost.com", port = 80)
  ) yield sendReceive(connector) ~> unmarshal[JsonResponse]


  // .....

  def getJson(relativeUrl: String)(implicit m: Monoid[Future[List[JsObject]]]) : Future[List[JsObject]] = {

    val jsr = pipeline.flatMap(_(Get(relativeUrl)))

    // Grab only those entries that we are interested in
    val objList = jsr.map(js => js.items.collect{ case o if(o.whatever.isDefined) => o.toJson.asJsObject })

    jsr.flatMap(js => js.meta.next.map(next => m.append(getJson(next), objList)).getOrElse(objList))
  }

【问题讨论】:

    标签: scala future spray tail-recursion


    【解决方案1】:

    尾递归在这里并不真正适用。当您使用未来的组合器时,您不会在单个堆栈上进行操作。

    具体来说,getJson 在任何情况下都会立即返回。通过使用jsr.flatMap,您正在注册一个回调,只有在收到 HTTP 请求的响应时才会调用该回调。因此,getJson 的下一次调用也将在此上下文中发生,即在隐式执行上下文提供的回调堆栈上。

    因此,虽然递归仍在算法级别发生,但它不会导致在一个(或任何)线程的堆栈上添加堆栈帧。

    关于破坏堆栈,您的代码很好。您还有其他原因要使用尾递归吗?

    【讨论】:

    • 啊。谢谢!是的,我想使用尾递归的唯一原因是害怕炸掉我的堆栈。这可能最终会在返回之前处理许多页面。非常感谢您的清晰解释。我现在因为没有意识到这一点而感到有点愚蠢,但也很放心。
    • 不,担心。与 Futures 一样强大的是一个新的通用异步执行原语,所以一开始很难掌握在所有不同可能场景中执行流程的方式。
    猜你喜欢
    • 2012-12-30
    • 1970-01-01
    • 2012-03-04
    • 2011-10-29
    • 1970-01-01
    • 2021-10-20
    • 2020-05-03
    • 2011-09-13
    • 1970-01-01
    相关资源
    最近更新 更多