在 Scala 中,从初始对象和生成下一个对象的函数创建 O(1) 内存 Iterable

发布于 2024-09-24 13:56:04 字数 1015 浏览 1 评论 0原文

我想要一种生成 Iterable 的便捷方法,给定一个初始对象和一个从当前对象生成下一个对象的函数,该函数消耗 O(1) 内存(即,它不缓存旧的对象)结果;如果您想第二次迭代,则必须再次应用该函数)。

似乎没有库支持这一点。在 Scala 2.8 中,方法 scala.collection.Iterable.iterate 具有签名,

def iterate [A] (start: A, len: Int)(f: (A) ⇒ A) : Iterable[A]

因此它要求您提前指定您感兴趣的迭代函数应用程序的数量,我对文档的理解是Iterable.iterate 实际上会立即计算所有这些值。另一方面,方法 scala.collection.Iterator.iterate 的签名

def iterate [T] (start: T)(f: (T) ⇒ T) : Iterator[T]

看起来很棒,但我们只得到一个迭代器,它不能提供 Iterator 的所有便利。代码>地图,<代码>过滤器和朋友。

是否有方便的库方法来生成我想要的内容?

如果没有,

有人可以建议执行此操作的“口语”Scala 代码吗?

总而言之,给定一个初始对象 a: A 和一个函数 f: A =>; A,我想要一个 TraversableLike (例如,可能是一个 Iterable)来生成 a, f(a), f(f(a )), ...,并使用 O(1) 内存,以及 mapfilter 等函数,这些函数也返回 O(1) 的内容记忆中。

I want a convenient way to generate an Iterable, given a initial object and a function to produce the next object from the current one, that consumes O(1) memory (i.e., it doesn't cache old results; if you want to iterate a second time, the function has to be applied again).

It doesn't seem like there's library support for this. In Scala 2.8, the method scala.collection.Iterable.iterate has signature

def iterate [A] (start: A, len: Int)(f: (A) ⇒ A) : Iterable[A]

so it requires that you specify how many iterated function applications you're interested in ahead of time, and my understanding of the documentation is that Iterable.iterate actually computes all these values immediately. On the other hand, the method scala.collection.Iterator.iterate has signature

def iterate [T] (start: T)(f: (T) ⇒ T) : Iterator[T]

which looks great, but we only get an Iterator which doesn't offer all the convenience of map, filter and friends.

Is there a convenient library method to produce what I want?

and if not,

Can someone suggest the 'colloquial' Scala code for doing this?

To summarise, given an initial object a: A, and a function f: A => A, I'd like a TraversableLike (e.g., probably an Iterable) that generates a, f(a), f(f(a)), ..., and uses O(1) memory, with map, filter etc. functions that also return something that is O(1) in memory.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

萌无敌 2024-10-01 13:56:04

Stream 会做你想做的事,只是不要保留单元格;仅迭代值。

这是一个常见的误解,认为 Stream 本质上会缓存它们计算的每个值。

如果你这样写:

val s1: Stream[Thing] = initialValue #:: «expression computing next value»

那么确实会保留流产生的每个值,但这不是必需的。如果你写:

def s2: Stream[Thing] = initialValue #:: «expression computing next value»

并且如果调用者只是迭代流的值但不记住流值本身(特别是它的任何 cons 单元),那么就不会发生不需要的保留。当然,在这个公式中,每次调用都会从固定的初始值开始创建一个新的 Stream。这不是必需的:

def s3(start: Thing): Stream[Thing] = start #:: «expression computing next value»

您必须注意的一件事是将 Stream 传递给方法。这样做将捕获方法参数中传递的流的头部。解决这个问题的一种方法是使用尾递归代码处理流。

Stream will do what you want, just don't hold on to the cells; only iterate over the values.

It is a sadly common misconception floating around that Streams inherently cache every value they compute.

If you write this:

val s1: Stream[Thing] = initialValue #:: «expression computing next value»

then indeed every value produced by the stream is retained, but this is not necessary. If you write:

def s2: Stream[Thing] = initialValue #:: «expression computing next value»

and if the caller just iterates over the stream's values but does not remember the Stream value itself (specifically any of its cons cells), then no unwanted retention will occur. Of course, in this formulation, every call creates a new Stream starting from a fixed initial value. That's not necessary:

def s3(start: Thing): Stream[Thing] = start #:: «expression computing next value»

The one thing you have to watch out for is passing a Stream to a method. Doing so will capture the head of the stream passed in the method parameter. One way around this is to process the stream with tail-recursive code.

夏九 2024-10-01 13:56:04

带过滤器的 Iterator.iterate 演示:(

object I {
  def main(args:Array[String]) {
    val mb = 1024 * 1024
    val gen = Iterator.iterate(new Array[Int](10 * mb)){arr => 
      val res = new Array[Int](10 * mb)
      arr.copyToArray(res)
      println("allocated 10mb")
      res(0) = arr(0) + 1 // store iteration count in first elem of new array
      res
    }
    // take 1 out of 100
    val gen2 = gen filter (arr => arr(0) % 100 == 0) 
    // print first 10 filtered
    gen2.take(10).foreach { arr => println("filtered " + arr(0)) } 
  }
}

这可能在 REPL 中不起作用,因为 PRINT 步骤可能会扰乱内存管理)

JAVA_OPTS="-Xmx128m" scala -cp classes I 将显示过滤有效并且是惰性的。如果它不是在常量内存中完成,则会导致堆错误(因为它分配了类似 900*10mb 的东西)。

使用 JAVA_OPTS="-Xmx128m -verbose:gc" scala -cp classes I 查看垃圾收集事件。

Iterator.iterate demo with filter:

object I {
  def main(args:Array[String]) {
    val mb = 1024 * 1024
    val gen = Iterator.iterate(new Array[Int](10 * mb)){arr => 
      val res = new Array[Int](10 * mb)
      arr.copyToArray(res)
      println("allocated 10mb")
      res(0) = arr(0) + 1 // store iteration count in first elem of new array
      res
    }
    // take 1 out of 100
    val gen2 = gen filter (arr => arr(0) % 100 == 0) 
    // print first 10 filtered
    gen2.take(10).foreach { arr => println("filtered " + arr(0)) } 
  }
}

(this may not work in the REPL as the PRINT step may mess up with memory management)

JAVA_OPTS="-Xmx128m" scala -cp classes I will show that the filtering works and is lazy. If it wasn't done in constant memory that would cause a heap error (since it's allocating something like 900*10mb).

Use JAVA_OPTS="-Xmx128m -verbose:gc" scala -cp classes I to see the garbage collection events.

年华零落成诗 2024-10-01 13:56:04

迭代器正是您想要的东西。而且迭代器确实有map,filter,takeWhile和许多其他方法,在内存中的复杂度为O(1)。我不认为还有另一种集合类型在内存中的复杂度为O(1)。

Iterator is the very thing what you want. And iterator do has map,filter,takeWhile and many other methods which is O(1) in memory.I don't think there is another collection type with O(1) in memory.

等风也等你 2024-10-01 13:56:04
val it = new Iterable[Int] {
  def iterator = Iterator.iterate(0)(_+1)
  override
  def toString: String = "Infinite iterable"
}

不要在 REPL 上尝试(除非将其嵌入到对象或类中),因为 REPL 会尝试打印它,并且它不使用 toString

val it = new Iterable[Int] {
  def iterator = Iterator.iterate(0)(_+1)
  override
  def toString: String = "Infinite iterable"
}

Don't try it out on REPL (except by embedding it inside an object or class), as REPL will try to print it, and it doesn't use toString.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文