2012-02-17 17 views
10

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

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#, uso normal IEnumerable y uso la palabra clave yield, pero esta 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) 

por lo que en este caso el takeWhileInclusive sólo tendría que resolver el predicado en los valores hasta que consiga el resultado superior a 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) = ? 
} 
+0

tenía usted tenía un vistazo al corriente? –

+0

Una secuencia definitivamente podría ser más apropiada aquí en el caso de ejemplo, sin embargo, todavía tengo el mismo problema con respecto a la mejor manera de compilar el método de extensión –

+2

Oh, 'takeWhileInclusive'. Mi viejo ['takeTo'] (https://issues.scala-lang.org/browse/SI-2963) .... –

Respuesta

7

Este es un caso donde encuentro la solución mutable superiores:

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) 
+0

Esta es definitivamente una solución elegante a mi problema, ¡salud! –

+0

Tomaré la versión funcional sin 'var's ni' val's, ¡gracias! –

+0

@oxbow_lakes - Si no te importa la sobrecarga adicional, esa es una buena alternativa. (Normalmente no utilizaría un val para la función, solo estaba tratando de separar las cosas para mayor claridad aquí). –

0
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 la 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?

+0

Lo siento, el ejemplo no es el caso específico que estoy buscando para resolver, incluiré un ejemplo más profundo para ilustrar lo que estoy buscando –

+0

@JPullar: Tu toma (2) se ha desvanecido y cambiado de lugar con (_ <6), mientras que el método timeConsuming está ahora a la izquierda de (_ <6). Entonces, ¿(timeConsumingMethod) produce un Int como resultado, que se compara con (_ <6) ahora, o es el elemento List inicial, que tiene que estar por debajo de 6? –

+0

Lo que se muestra es correcto y lo que busco en la evaluación perezosa.Sin embargo, mi problema es emular cómo se evalúa la función de filtro de forma perezosa en un método de extensión personalizado –

10

Usted puede utilizar el método de Iteratorspan hacer esto bastante limpiamente:

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.

+1

La última línea de su método se puede escribir de manera más sucinta como 'a ++ (b toma 1)' –

2
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 su implícito. Aquí se fija:

object ImplicitIterator { 
    implicit def extendIterator[T](i : Iterator[T]) = new IteratorExtension(i) 
} 
+0

Guau, hubo algunos errores graves en mi versión anterior. –

+0

¡Aquí era donde mi pensamiento se estaba dirigiendo, gracias por llegar allí para mí! Ofrece un buen enfoque generalizado. Ojalá hubiera una solución general más elegante que tener que construir un nuevo iterador. –

3

El siguiente requiere scalaz para obtener 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 rodar su propia veces más que un par de esta manera:

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}