2012-03-18 14 views
6

Imagine que necesita doblar una secuencia, y desea conocer también los valores intermedios en varios puntos a lo largo del rango. Esto es lo que he usado para esto:¿Qué es este patrón de plegado e iteración?

[a,b,c] = map fst . tail $ chain [g i, g j, g k] (zero, sequence) 

g :: Integer -> (a,b) -> (a,b) 

chain (f:fs) x = x : chain fs (f x) 
chain [] x = [x] 

La función g consume una porción específica de una secuencia de entrada (de longitud i, j, etc.), comenzando con un valor inicial y producir resultados de la misma tipo, para ser alimentado en la próxima invocación. El consumo de la secuencia varias veces para diferentes longitudes comenzando desde el principio y el mismo valor inicial sería ineficiente, tanto en el tiempo como en el espacio, por supuesto.

Por lo tanto, por un lado, doblamos esta secuencia de enteros: puntos provisionales en la secuencia; por otro lado iteramos esta función, g. ¿Qué es? ¿Me estoy perdiendo algo básico aquí? ¿Puede esto expresarse de alguna manera con el repertorio regular de pliegues, etc.?

EDIT:   Resuelto:   lo anterior es simplemente

[a,b,c] = map fst . tail $ scanl (flip g) (zero, sequence) [i, j, k] 

interesante cómo una iteración modificable en realidad es plegado de la lista de modificadores.

+6

¿Básicamente significa scanl? http://www.haskell.org/hoogle/?hoogle=scanl – Marcin

+1

@Marcin ah, sí, las cosas básicas. Probablemente si. Lo que me confundió fue que 'g' también se dobla sobre la secuencia. Supongo que la función de escaneo combinaría '(cero, secuencia)' con 'i' y escanearía directamente la lista' [i, j, k] '... ¡Gracias, lo intentaré! –

Respuesta

9

Trate scanl: http://www.haskell.org/hoogle/?hoogle=scanl

scanl es similar a foldl, pero devuelve una lista de sucesivas reducidas los valores de la izquierda:

scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...] 

Tenga en cuenta que

last (scanl f z xs) == foldl f z xs 
4

Para elaborar sobre El comentario de Marcin, que básicamente quiere:

intermediates = scanl step zero sequence 
map (\n -> intermediates !! n) [i, j, k] 

step no es g, sino sólo la parte de g que consume un solo elemento de la lista sequence.

Además, acepte Marcin's como la respuesta correcta.

+0

la secuencia en sí es enorme (bueno, * grande *), y solo necesito dos intermedios y uno final. Todo esto es para reducir el consumo de espacio y darle la oportunidad a ghc de deshacerse de tantas cosas como sea posible. Además, acepté 5 minutos antes de su publicación. :) ¡Gracias! –

+0

Si compila lo anterior con optimizaciones, GHC debería recoger automáticamente los resultados intermedios que no utiliza, por lo que debería ejecutarse en un espacio constante. No es más caro que si hicieras los pliegues intermedios tú mismo a mano. –

+0

Tengo la sospecha de que '(!! n)' exigirá la existencia de toda la secuencia, ya que debe comenzar a contar desde el principio. Pero lo intentaré más tarde, gracias.Cada valor anterior es necesario para producir el siguiente; incluso si ya no es necesario, hay mucha actividad (gc), y la lista en sí misma se mantendrá, por lo que es posible que ni siquiera libere el valor de cada nodo como ya estaba forzado. Al calcular por pasos, eso es exactamente lo que se evita, con suerte. De hecho, vi una caída significativa en el tamaño del espacio con mi enfoque. –

Cuestiones relacionadas