Scalaz iteriert von Scala Iterator
Frage
Ich habe den Code unten bearbeitet, da ich glaube, dass ich die ITerv -Objekte falsch über dem kombiniert habe iter.next
Ausgabe.
Ich experimentiere mit Iteratee
In Scalaz und ich frage mich, warum Folgendes nicht funktioniert. Hier ist was ich habe:
import scalaz._
import Scalaz._
import IterV._
implicit val iteratorEnumerator = new Enumerator[Iterator] {
def apply[E,A](iter: Iterator[E], i: IterV[E,A]): IterV[E,A] =
if (iter.isEmpty) i
else i.fold(done = (acc,input) => i,
cont = k => apply(iter, k(El(iter.next))))
}
/* probably incorrect
val iter = Iterator(1,2,3)
println("peek(iter) " + peek(iter).run)
println("peek(iter) " + peek(iter).run)
*/
def peekpeek[E]: IterV[E, (Option[E],Option[E])] =
for (a <- peek; b <- peek) yield (a,b)
def peekheadpeek[E]: IterV[E, (Option[E],Option[E],Option[E])] =
for (a <- peek; b <- head; c <- peek) yield (a,b,c)
peekpeek(Iterator(1,2,3,4)).run
peekheadpeek(Iterator(1,2,3,4)).run
Dies kehrt zurück:
res0: (Option[Int], Option[Int]) = (Some(1),Some(2))
res1: (Option[Int], Option[Int], Option[Int]) = (Some(1),Some(2),Some(3))
Wo ich erwartet hatte (Some(1),Some(1))
und (Some(1),Some(1),Some(2))
.
Ich vermute, das hat damit zu tun iter.next
Seiteneffekt sein. Was ist der beste Weg, um damit umzugehen?
Zum Vergleich, Dies direkt aus den Quellcode -Beispielen aus dem Scalaz genommen Webseite Funktioniert einwandfrei:
implicit val StreamEnumerator = new Enumerator[Stream] {
def apply[E, A](e: Stream[E], i: IterV[E, A]): IterV[E, A] = e match {
case Stream() => i
case x #:: xs => i.fold(done = (_, _) => i,
cont = k => apply(xs, k(El(x))))
}
}
Lösung
Ich glaube, ich habe das herausgefunden. Es schien in erster Linie zu sein El
Verwenden eines Parameters mit Namen, der neu berechnet iter.next
- sowie wie ich die Berechnung zunächst falsch mit zwei verschiedenen nannte peek(iter).run
. Ich habe den Enumerator neu geschrieben, um zuerst zugewiesen zu werden iter.next
zu einem val (und machte es auch im Prozess rekursiv):
implicit val iteratorEnumerator = new Enumerator[Iterator] {
@annotation.tailrec
def apply[E,A](iter: Iterator[E], i: IterV[E,A]): IterV[E,A] = i match {
case _ if iter.isEmpty => i
case Done(acc, input) => i
case Cont(k) =>
val x = iter.next
apply(iter, k(El(x)))
}
}
Dann:
def peekpeek[A] =
for (a1 <- peek[A]; a2 <- peek[A]) yield (a1,a2)
def peekheadpeek[A] =
for (a1 <- peek[A]; a2 <- head[A]; a3 <- peek[A]) yield (a1,a2,a3)
def headpeekhead[A] =
for (a1 <- head[A]; a2 <- peek[A]; a3 <- head[A]) yield (a1,a2,a3)
peekpeek(Iterator(1,2,3)).run
peekheadpeek(Iterator(1,2,3)).run
headpeekhead(Iterator(1,2,3)).run
length(Iterator.from(1).take(1000000)).run
würde dies zurückgeben:
res13: (Option[Int], Option[Int]) = (Some(1),Some(1))
res14: (Option[Int], Option[Int], Option[Int]) = (Some(1),Some(1),Some(2))
res15: (Option[Int], Option[Int], Option[Int]) = (Some(1),Some(2),Some(2))
res16: Int = 1000000
Andere Tipps
Sie haben Recht mit iter.next und verursachen Nebenwirkungen. Ich denke, dass die Frage auf den Unterschied zwischen einem Strom und einem Iterator hinausgeht. Diese Frage hat relevante Informationen.
Iteratees sind faul. Nebenwirkungen (Iterator) und Faulheit mischen sich nicht. Vielleicht tut das das Richtige:
implicit val iteratorEnumerator = new Enumerator[Iterator] {
def apply[E,A](iter: Iterator[E], i: IterV[E,A]): IterV[E,A] =
iter.foldRight(i)((x, y) =>
y.fold(done = (acc, input) => y,
cont = k => apply(iter, k(El(x))))
)
}
Andererseits, vielleicht auch nicht. Es wird nur die Quelle von FoldRight wissen. Nebenwirkungen sind so.
Sie können Nebenwirkungen vermeiden, indem Sie einen Wegwerf Iterator mit der ersten Größe erstellen:
implicit val iteratorEnumerator = new Enumerator[Iterator] {
def apply[E,A](iter: Iterator[E], i: IterV[E,A]): IterV[E,A] =
if (iter.isEmpty) i
else i.fold(done = (acc,input) => i,
cont = k => apply(iter, k(El(iter.take(1).next))))
}