scala iterator lazy-evaluation extend enrich-my-library

Scala, extendiendo el iterador



iterator lazy-evaluation (5)

Estoy buscando ampliar el iterador para crear un nuevo método takeWhileInclusive , que operará como takeWhile pero incluirá el último elemento.

Mi problema es cuál es la mejor práctica para extender el iterador para devolver un nuevo iterador que me gustaría ser evaluado perezosamente. Viniendo de un fondo de C #, normalmente uso IEnumerable y uso la palabra clave yield , pero esa opción no parece existir en Scala.

por ejemplo, podría tener

List(0,1,2,3,4,5,6,7).iterator.map(complex time consuming algorithm).takeWhileInclusive(_ < 6)

entonces en este caso el takeWhileInclusive solo resolvería el predicado en los valores hasta que obtenga un resultado mayor que 6, e incluirá este primer resultado

hasta ahora tengo:

object ImplicitIterator { implicit def extendIterator(i : Iterator[Any]) = new IteratorExtension(i) } class IteratorExtension[T <: Any](i : Iterator[T]) { def takeWhileInclusive(predicate:(T) => Boolean) = ? }


Este es un caso en el que considero que la solución mutable es superior:

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)


Lo siguiente requiere que Scalaz se fold en una tupla (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]}

Aquí está en el trabajo:

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)

Puede hacer su propio doblez sobre un par como este:

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}


Puede usar el método de span de Iterator para hacer esto de forma bastante limpia:

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._

Ahora (0 until 10).toIterator.takeWhileInclusive(_ < 4).toList da List(0, 1, 2, 3, 4) , por ejemplo.


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 } } }

Y hay un error en tu implícito. Aquí está arreglado:

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)

Después de su actualización:

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)

timeConsumeDummy se llama 4 veces. ¿Me estoy perdiendo de algo?