是否使用Scala的Iterator ++(concat)递归堆栈安全?

时间:2017-06-23 18:30:29

标签: scala iterator

假设您使用的API允许您在页面中滚动结果集。每个页面都返回后续页面的ID。

这是一个既定的习惯用法,你可以递归地使用Scala Iterator及其懒惰的concat(++)运算符来执行此操作:

def allResults: Iterator[Result] = {
  def nextPage(pageId: String): Iterator[Result] = {
    val page = invoke api
    Iterator(page.results) ++ nextPage(page.nextPageId)
  }
  val firstPage = invoke api
  Iterator(firstPage.results) ++ nextPage(firstPage.nextPageId)
}

这个成语是否安全?或者还有其他效率问题需要担心吗?

2 个答案:

答案 0 :(得分:2)

我相信这是堆栈安全的,特别是因为++方法采用了call-by-name参数,正如你所提到的那样。

这适用于Iterators和Streams,但不适用于"非懒惰"列表和地图等集合。

在这些情况下,您应该使用累加器并使用@tailrec注释您的方法,以确保您不会像https://stackoverflow.com/a/3114248/854793中那样使用比预期更多的堆栈

答案 1 :(得分:0)

使用Iterator.++时,我在Scala 2.11.11上获得了StackOverflowError,但在使用Stream.#::时却没有。我认为这必须是Scala 2.11.11中Iterator.++中的错误。在Scala 2.12.2上都可以工作:

def iter(n: Int): Iterator[Int] = if (n <= 0) Iterator.empty else { Iterator.single(n) ++ iter(n - 1) } iter(100000).foreach(print)

这导致Scala 2.11.11上的StackOverflowError,但是在Scala 2.12.2上它运行正常。使用Stream.#::也可以正常工作。也许这是Scala 2.11.11中的一个错误?

def stream(n: Int): Stream[Int] = if (n <= 0) Stream.empty else { n #:: stream(n - 1) } stream(1000000).foreach(print)