GHC no memoriza funciones.
lo hace, sin embargo, calcular cualquier expresión dada en el código como máximo una vez por cada vez que se introduce su entorno lambda-expresión, o como máximo una vez cada vez si se está en el nivel superior. Determinar dónde los lambda-expresiones son Puede ser un poco complicada cuando se utiliza azúcar sintáctico como en su ejemplo, así que vamos a convertir estos al equivalente de la sintaxis Desazucarado:
m1' = (!!) (filter odd [1..]) -- NB: See below!
m2' = \n -> (!!) (filter odd [1..]) n
(Nota: El informe de Haskell 98 describe realmente un operador de izquierda sección como (a %)
como equivalente a \b -> (%) a b
, pero GHC desugars a (%) a
. Estos son técnicamente diferentes, ya que pueden ser distinguidos por seq
. Creo que podría haber presentado un billete GHC Trac sobre esto.)
Teniendo en cuenta esto, se puede ver que en m1'
, la expresión filter odd [1..]
no está contenida en n cualquier expresión lambda, por lo que solo se computará una vez por ejecución de su programa, mientras que en m2'
, filter odd [1..]
se computará cada vez que se ingrese la expresión lambda, es decir, en cada llamada de m2'
. Eso explica la diferencia en el tiempo que estás viendo.
En realidad, algunas versiones de GHC, con ciertas opciones de optimización, compartirán más valores que la descripción anterior indica. Esto puede ser problemático en algunas situaciones. Por ejemplo, considere la función
f = \x -> let y = [1..30000000] in foldl' (+) 0 (y ++ [x])
GHC podría darse cuenta de que y
no depende de x
y volver a escribir la función de
f = let y = [1..30000000] in \x -> foldl' (+) 0 (y ++ [x])
En este caso, la nueva versión es mucho menos eficiente, ya que tendrá para leer aproximadamente 1 GB de la memoria donde se almacena y
, mientras que la versión original se ejecutará en espacio constante y se ajustará en la memoria caché del procesador. De hecho, bajo GHC 6.12.1, la función f
es casi el doble de rápida cuando se compila sin optimizaciones que la compilada con -O2
.
El costo para evaluar (filtro impar [1 ..]) expresión es casi cero de todos modos - es una lista perezosa después de todo, por lo que el costo real es en (x !! 10000000) aplicación cuando la lista es en realidad evaluado. Además, tanto m1 como m2 parecen evaluarse solo una vez con -O2 y -O1 (en mi ghc 6.12.3) al menos dentro de la siguiente prueba: (test = m1 10000000 'seq' m1 10000000). Sin embargo, hay una diferencia cuando no se especifica ninguna bandera de optimización. Y ambas variantes de su "f" tienen una residencia máxima de 5356 bytes independientemente de la optimización, por cierto (con menos asignación total cuando se usa -O2). –
@ Ed'ka: Pruebe este programa de prueba, con la definición anterior de 'f':' main = interact $ unlines. (mostrar el mapa f. leer). líneas'; compilar con o sin '-O2'; luego 'echo 1 | ./main'. Si escribe una prueba como 'main = print (f 5)', entonces 'y' puede ser basura recolectada mientras se usa y no hay diferencia entre las dos' f's. –
er, eso debería ser 'map (show. F. Read)', por supuesto. Y ahora que descargué GHC 6.12.3, veo los mismos resultados que en GHC 6.12.1. Y sí, tienes razón sobre el 'm1' original y' m2': las versiones de GHC que realizan este tipo de levantamiento con las optimizaciones habilitadas transformarán 'm2' en' m1'. –