Scala, die den Iterator erweitert
-
27-10-2019 - |
Frage
Ich möchte den Iterator erweitern, um eine neue Methode zu erstellen takeWhileInclusive
, die wie wie takeWhile
Fügen Sie aber das letzte Element ein.
Mein Problem ist, was die beste Praxis ist, um den Iterator zu erweitern, um einen neuen Iterator zurückzugeben, den ich gerne faul werden möchte. Ich komme aus einem C# -Hintergrund, den ich normal benutze IEnumerable
und benutze die yield
Schlüsselwort, aber eine solche Option scheint in Scala nicht zu existieren.
Zum Beispiel könnte ich haben
List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6)
Also in diesem Fall die takeWhileInclusive
Hätte das Prädikat auf den Werten nur auflösen, bis ich ein Ergebnis von mehr als 6 erhalte, und es wird dieses erste Ergebnis enthalten
Bisher habe ich:
object ImplicitIterator {
implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i)
}
class IteratorExtension[T <: Any](i : Iterator[T]) {
def takeWhileInclusive(predicate:(T) => Boolean) = ?
}
Lösung
Dies ist ein Fall, in dem ich die veränderliche Lösung überlegen finde:
class InclusiveIterator[A](ia: Iterator[A]) {
def takeWhileInclusive(p: A => Boolean) = {
var done = false
val p2 = (a: A) => !done && { if (!p(a)) done=true; true }
ia.takeWhile(p2)
}
}
implicit def iterator_can_include[A](ia: Iterator[A]) = new InclusiveIterator(ia)
Andere Tipps
Du kannst den ... benutzen span
Methode von Iterator
Um das ziemlich sauber zu tun:
class IteratorExtension[A](i : Iterator[A]) {
def takeWhileInclusive(p: A => Boolean) = {
val (a, b) = i.span(p)
a ++ (if (b.hasNext) Some(b.next) else None)
}
}
object ImplicitIterator {
implicit def extendIterator[A](i : Iterator[A]) = new IteratorExtension(i)
}
import ImplicitIterator._
Jetzt (0 until 10).toIterator.takeWhileInclusive(_ < 4).toList
gibt List(0, 1, 2, 3, 4)
, zum Beispiel.
Im Folgenden muss Scalaz bekommen fold
auf einem Tupel (A, B)
scala> implicit def Iterator_Is_TWI[A](itr: Iterator[A]) = new {
| def takeWhileIncl(p: A => Boolean)
| = itr span p fold (_ ++ _.toStream.headOption)
| }
Iterator_Is_TWI: [A](itr: Iterator[A])java.lang.Object{def takeWhileIncl(p: A => Boolean): Iterator[A]}
Hier ist es bei der Arbeit:
scala> List(1, 2, 3, 4, 5).iterator takeWhileIncl (_ < 4)
res0: Iterator[Int] = non-empty iterator
scala> res0.toList
res1: List[Int] = List(1, 2, 3, 4)
Sie können Ihre eigene Falten über ein Paar wie dieses rollen:
scala> implicit def Pair_Is_Foldable[A, B](pair: (A, B)) = new {
| def fold[C](f: (A, B) => C): C = f.tupled(pair)
| }
Pair_Is_Foldable: [A, B](pair: (A, B))java.lang.Object{def fold[C](f: (A, B) => C): C}
class IteratorExtension[T](i : Iterator[T]) {
def takeWhileInclusive(predicate:(T) => Boolean) = new Iterator[T] {
val it = i
var isLastRead = false
def hasNext = it.hasNext && !isLastRead
def next = {
val res = it.next
isLastRead = !predicate(res)
res
}
}
}
Und es gibt einen Fehler in Ihrem impliziten. Hier ist es behoben:
object ImplicitIterator {
implicit def extendIterator[T](i : Iterator[T]) = new IteratorExtension(i)
}
scala> List(0,1,2,3,4,5,6,7).toStream.filter (_ < 6).take(2)
res8: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res8.toList
res9: List[Int] = List(0, 1)
Nach Ihrem Update:
scala> def timeConsumeDummy (n: Int): Int = {
| println ("Time flies like an arrow ...")
| n }
timeConsumeDummy: (n: Int)Int
scala> List(0,1,2,3,4,5,6,7).toStream.filter (x => timeConsumeDummy (x) < 6)
Time flies like an arrow ...
res14: scala.collection.immutable.Stream[Int] = Stream(0, ?)
scala> res14.take (4).toList
Time flies like an arrow ...
Time flies like an arrow ...
Time flies like an arrow ...
res15: List[Int] = List(0, 1, 2, 3)
Die Zeitspanne wird 4 -mal bezeichnet. Vermisse ich etwas?