トラバーサブルからイテレータを遅延取得できない理由は、本質的にできないからです。Traversable は を定義しforeach
、foreach
停止することなくすべてを実行します。そこに怠惰はありません。
したがって、怠惰にするための2つのオプションがあり、どちらもひどいものです。
まず、毎回全体を繰り返すことができます。(ここでは Scala イテレーターを使用しますが、Java イテレーターも基本的には同じです。)
class Terrible[A](t: Traversable[A]) extends Iterator[A] {
private var i = 0
def hasNext = i < t.size // This could be O(n)!
def next: A = {
val a = t.slice(i,i+1).head // Also could be O(n)!
i += 1
a
}
}
たまたま効率的なインデックス付きスライスがあれば、これで問題ありません。そうでない場合、それぞれの「次」はイテレータの長さに比例して時間がかかり、O(n^2)
それを通過するだけの時間がかかります。しかし、これも必ずしも怠惰ではありません。それがなければならないと主張する場合はO(n^2)
、すべての場合に強制し、実行する必要があります
class Terrible[A](t: Traversable[A]) extends Iterator[A] {
private var i = 0
def hasNext: Boolean = {
var j = 0
t.foreach { a =>
j += 1
if (j>i) return true
}
false
}
def next: A = {
var j = 0
t.foreach{ a =>
j += 1
if (j>i) { i += 1; return a }
}
throw new NoSuchElementException("Terribly empty")
}
}
これは、一般的なコードでは明らかにひどい考えです。
もう 1 つの方法は、スレッドを使用して、進行中の foreach のトラバーサルをブロックすることです。そうです、要素アクセスごとにスレッド間通信を行う必要があります。それがどのように機能するか見てみましょう。Scala は Akka スタイルのアクターへの切り替えの最中なので、ここでは Java スレッドを使用します (ただし、古いアクター、Akka アクター、Scalaz アクター、Lift アクター、または(など)が機能します)
class Horrible[A](t: Traversable[A]) extends Iterator[A] {
private val item = new java.util.concurrent.SynchronousQueue[Option[A]]()
private class Loader extends Thread {
override def run() { t.foreach{ a => item.put(Some(a)) }; item.put(None) }
}
private val loader = new Loader
loader.start
private var got: Option[A] = null
def hasNext: Boolean = {
if (got==null) { got = item.poll; hasNext }
else got.isDefined
}
def next = {
if (got==null) got = item.poll
val ans = got.get
got = null
ans
}
}
これにより災害は回避O(n^2)
されますが、スレッドが拘束され、要素ごとのアクセスが非常に遅くなります。私のマシンでは毎秒約 200 万回のアクセスがありますが、典型的なトラバーサブルでは 100M を超えています。これは、一般的なコードにとって明らかに恐ろしい考えです。
それで、あなたはそれを持っています。Traversable は一般的に遅延ではなく、パフォーマンスを大幅に低下させずに遅延させる良い方法はありません。