Cada vez que se solicita otro carácter (o notificación de finalización), el siguiente carácter - si hay alguno - se mapea a mayúsculas, que se compara con 'W', entregado si es desigual.
filter (/= 'W') . map toUpper $ "justaword"
~> filter (/= 'W') (toUpper 'j' : map toUpper "ustaword")
~> filter (/= 'W') ('J' : map toUpper "ustaword")
~> 'J' : filter (/= 'W') (map toUpper "ustaword")
Ahora el primer carácter está disponible, por lo que para consultas como null
o funciones como take 1
, sin más trabajo está hecho. Si el consumidor demanda más caracteres, se producirán uno por uno hasta que se llegue al final de la cadena.
Ejemplo:
Prelude Data.Char> take 10 . filter (/= 'W') . map toUpper $ repeat 't'
"TTTTTTTTTT"
repeat
produce una lista infinita, pero siempre y cuando solamente se consume una parte finita, el cálculo termina en tiempo finito. Sin embargo, take 10 . filter (/= 'W') . map toUpper $ repeat 'w'
no terminaría, ya que ninguno de los caracteres producidos pasa el filter
para alcanzar el take 10
.
Gracias por la gran respuesta :) ¿Es posible escribir este código en un lenguaje estricto (sin simular pereza)? O en una lengua estricta, la lista se atravesará dos veces, una para el mapa y otra para el filtro, comenzando desde el principio. – Adi
@Adi: sí, habrá dos recorridos de lista. map devolverá una nueva lista intermedia, que pasará a filter, que pasará por y devolverá otra lista – newacct
En un lenguaje estricto (impaciente), uno debería simular la pereza para obtener el mismo comportamiento. Algunos lenguajes lo hacen más fácil que otros, prefiero hacer eso en erlang o F # que en C (y que aunque conozco C, casi no tengo erlang ni F # :). Si hay dos recorridos o uno en un lenguaje entusiasta depende del compilador. En principio, el filtro y el mapa se pueden fusionar, pero en general el compilador necesitaría probar la pureza de la función mapeada y el predicado del filtro para poder hacer eso. Así que espero dos recorridos en la mayoría de los casos, la prueba es difícil. –