2012-09-27 64 views
6

Por qué es que,mapa Scala el iterador no produce efectos secundarios

scala> List(1,2,3,4).iterator.map((x: Int) => println(x)) 

no imprime a cabo

1 
2 
3 
4 

mientras

List(1,2,3,4).map((x: Int) => println(x)) 
List(1,2,3,4).foreach((x: Int) => println(x)) 
List(1,2,3,4).iterator.foreach((x: Int) => println(x)) 

todos lo hacen?

En otras palabras, ¿por qué es que un mapa en un iterador que correlaciona el tipo T con la unidad y tiene efectos secundarios incapaces de mostrar esos efectos secundarios?

Editar:

También por qué hace lo siguiente invocación de lazyMap realidad calcula el nuevo iterador (proporcione el nuevo iterador completa) de principio a fin si iterador es perezoso?

def lazyMap[T, U](coll: Iterable[T], f: T => U) = new Iterable[U] { 
    def iterator = coll.iterator map f 
} 

scala> lazyMap(List(1,2,3,4), (x: Int) => x + 1) 
res4: java.lang.Object with Iterable[Int] = (2, 3, 4, 5) 
+0

Por cierto, estás haciendo una extensión incorrecta de Iterable (todas las clases que se extienden Iterable deberían proporcionar el método 'newBuilder') –

Respuesta

7

mapa Causa de iterador es perezoso y necesita un poco de rigor:

scala> List(1,2,3,4).iterator.map((x: Int) => println(x)) 
res0: Iterator[Unit] = non-empty iterator 

// nothing actually happened yet, just remember to do this printing things 

scala> res0.toList 
1 
2 
3 
4 
res1: List[Unit] = List((),(),(),()) 

Al hacer foreach en iterador que es bastante obvio que usted está haciendo efectos secundarios, por lo que será pereza no deseado. No lo hubiera dicho sobre el mapa.

UPD

En cuanto a tu edición: la razón de tal comportamiento, es que hay una llamada implícita de toString para el resultado de la sentencia que a su vez stricts el iterador - prueba este código en su propia:

scala> { lazyMap(List(1,2,3,4), {(x: Int) => println(x); x + 1}); 1 } 

y verá que la función f nunca es llamado

6

El punto de un iterador es la pereza. En otras palabras, cuando crea un iterador, no evaluará nada hasta que vaya a leer los datos. Esto es lo que parece:

scala> val itr = List(1,2,3).iterator 
itr: Iterator[Int] = non-empty iterator 

Bien, tenemos un iterador ahora. Pero en realidad aún no ha mirado la lista.

scala> val mappedItr = itr.map((x: Int) => println(x)) 
mappedItr: Iterator[Unit] = non-empty iterator 

Ahora tenemos un nuevo iterador. Cuando se acceda a los datos, éste aplicará la función que se ha mapeado. Pero nosotros todavía no hemos mirado realmente la lista original.

scala> mappedItr.next 
1 

Ésta es la primera vez que hemos accedido a los datos, por lo que es la primera vez que el iterador ha mirado en la lista. Llamamos al next, así que obtuvimos el primer elemento. Dado que nuestro iterador tiene un map en cola, aplica la función mapeada cuando accedemos a ese elemento. Entonces vemos el resultado de la función aplicada al elemento next.

Podemos hacerlo de nuevo para obtener el siguiente elemento:

scala> mappedItr.next 
2 

Y, de nuevo, se evalúa la función sólo cuando se necesita, con el fin de darnos el resultado final.

+0

ver edición – platypus

Cuestiones relacionadas