Dons ha proporcionado una muy buena respuesta, pero ha dejado fuera lo que es (para mí) una de las características más atractivas de los iterados: hacen que sea más fácil razonar sobre la gestión del espacio porque los datos antiguos deben retenerse explícitamente. Considerar:
average :: [Float] -> Float
average xs = sum xs / length xs
Esta es una pérdida de espacio bien conocida, porque toda la lista xs
debe conservarse en la memoria para calcular tanto sum
y length
. Es posible hacer un consumidor eficiente creando un pliegue:
average2 :: [Float] -> Float
average2 xs = uncurry (/) <$> foldl (\(sumT, n) x -> (sumT+x, n+1)) (0,0) xs
Pero es algo inconveniente tener que hacer esto para cada procesador de flujo. Hay algunas generalizaciones ( Conal Elliott - Beautiful Fold Zipping ), pero no parece que hayan tenido éxito . Sin embargo, los iterados pueden brindarle un nivel de expresión similar.
aveIter = uncurry (/) <$> I.zip I.sum I.length
Esto no es tan eficiente como un pliegue porque la lista todavía se repite varias veces; sin embargo, se recopila en fragmentos para que los datos antiguos se puedan recolectar de manera eficiente. Para romper esa propiedad, es necesario retener explícitamente toda la entrada, como con stream2list:
badAveIter = (\xs -> sum xs / length xs) <$> I.stream2list
El estado de los iterados como modelo de programación es un trabajo en progreso, sin embargo, es mucho mejor que hace un año. Estamos aprendiendo lo combinadores son útiles (por ejemplo zip
, breakE
, enumWith
) y que son por lo menos, con el resultado de que incorporados iteratees y combinadores proporcionan continuamente más expresividad.
Dicho esto, Dons tiene razón en que son una técnica avanzada; Ciertamente no los usaría para cada problema de E / S.