我想要一个无限的非严格系列 x 1, x 2, x 3 ......我可以一次使用一个元素,而不是为了保持我的内存使用量不变而记住结果。为了具体起见,假设它是一系列整数(例如自然数、奇数、素数),尽管这个问题可能适用于更一般的数据类型。
使用无限列表最简单的方法是使用 Scala 的Stream
对象。一个常见的习惯用法是编写一个返回 a 的函数Stream
,使用#::
运算符将一个项添加到系列中,然后递归调用自身。例如,在给定起始值和后继函数的情况下,以下代码会生成无限的整数流。
def infiniteList(n: Int, f: Int => Int): Stream[Int] = {
n #:: infiniteList(f(n), f)
}
infiniteList(2, _*2+3).take(10) print
// returns 2, 7, 17, 37, 77, 157, 317, 637, 1277, 2557, empty
(我意识到上面的内容相当于库调用Stream.iterate(2)(_*2+3)
。我在这里写它作为这个无限Stream
成语的例子。)
但是,流会记住它们的结果,从而使它们的内存需求非常不稳定,并且可能非常大。文档指出,如果您不抓住 a 的头部,就可以避免记忆Stream
,但实际上这可能很棘手。我可能会实现无限列表代码,我认为我不会在其中保留任何流头,但如果它仍然有无限的内存需求,我必须弄清楚问题是否在于我正在以某种方式处理我的流这确实会导致记忆,或者如果它是其他东西。这可能是一项困难的调试任务并且有代码味道,因为我试图欺骗显式记忆的数据结构以返回非记忆的结果。
我想要的是Stream
没有记忆的具有期望语义的东西。Scala 中似乎不存在这样的对象。我一直在尝试使用迭代器来实现无限数字系列,但是当您开始想要对它们进行理解操作时,迭代器的可变性使这变得很棘手。我也尝试从头开始编写自己的代码,但不清楚我应该从哪里开始(我是子类Traversable
吗?)或者如何避免重新实现 , 等中的map
功能fold
。
有人有很好的示例 Scala 代码来实现非严格、不可变、非记忆的无限列表吗?
更一般地说,我理解traversable、iterable、sequence、stream 和 view 的语义,但是我发现这个问题如此令人烦恼的事实让我觉得我误解了一些东西。在我看来,非严格性和非记忆性是完全正交的属性,但 Scala 似乎已经做出了将它们统一起来的设计决定,Stream
并且没有简单的方法将它们分开。这是对 Scala 的疏忽,还是我忽略的非严格性和非记忆化之间存在某种深层联系?
我意识到这个问题相当抽象。这是一些将其与特定问题联系起来的附加上下文。
我在实施 Meissa O'Niell 的论文“ The Genuine Sieve of Eratosthenes ”中描述的素数生成器的过程中遇到了这个问题,并且很难给出一个简单的例子来说明一个Iterator
对象是不合适的而不拉入很多那篇论文的细节。这是一个使用 流的完整实现,它非常优雅,但内存消耗非常大。
这是一个带有迭代器的简化实现,它不会编译,但可以让您了解我想要什么。
import scala.collection.mutable
object ONeillSieve {
class NumericSeries extends BufferedIterator[Int] with Ordered[NumericSeries] {
def hasNext = true
def compare(that: NumericSeries) = that.head.compare(head)
override def toString() = head + "..."
var head = 3
def next() = {
val r = head
head += 2
r
}
}
def main(args: Array[String]) {
val q = mutable.PriorityQueue[NumericSeries]()
val odds = new NumericSeries
q += odds.map(odds.head * _)
odds.next()
q += odds.map(odds.head * _)
println("Sieve = %s\nInput = %s".format(q, odds))
}
}
我需要构建一个PriorityQueue
由它们的最小元素键入的无限数字系列。(因此我使用 aBufferedIterator
而不仅仅是一个普通的Iterator
。)还要注意,这里无限级数的基础是奇数,但最通用的解决方案涉及更复杂的级数。在主函数结束时,我希望队列包含两个无限系列:
- 3x(赔率从 3 开始)(即 9,12,15...)
- 5x(赔率从 5 开始)(即 25,30,35...)
以上无法编译,因为odds.map(...)
返回的是Iterator
a ,而不是 a NumericSeries
,因此无法添加到优先级队列中。
在这一点上,我似乎正在涉足集合类扩展,这很棘手,所以我想确保除非绝对必要,否则我不必这样做。