2010-07-08 16 views
8

que tienen la siguiente función definida:¿Por qué esta afirmación de Haskell no se evalúa perezosamente?

ex 1 x = 1 
ex 0 x = 0 
ex b x = b ** x 

Entonces, cuando yo haga lo siguiente:

1 `ex` (sum [1..]) 

que trata de calcular la suma de la secuencia infinita, en lugar de ser perezoso y regresar 1. ¿Por qué?


EDIT: Tras realizar investigaciones adicionales, he encontrado que la pereza ocurre si defino la función ex en un archivo, pero no si lo defino en GHCi:

$ ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> let ex 1 x = 1 
Prelude> let ex b x = b ** x 
Prelude> ex 1 (sum [1..]) 
<interactive>: out of memory (requested 1048576 bytes) 

Si me tire de la definición ex en un archivo (en este caso, test.hs):

$ ghci 
GHCi, version 6.8.2: http://www.haskell.org/ghc/ :? for help 
Loading package base ... linking ... done. 
Prelude> :load test.hs 
[1 of 1] Compiling Main    (test.hs, interpreted) 
Ok, modules loaded: Main. 
*Main> ex 1 (sum [1..]) 
1.0 

La nueva pregunta, entonces, es ¿por qué?

+1

No, 1 'ex' (suma [1 ..]) devuelve 1.0 en mi computadora. ¿Qué versión de ghc, etc.? ¿Y obtiene el mismo resultado con ex 1 (suma [1 ..])? – ShreevatsaR

+1

Posiblemente un error? –

+0

@ShreevatsaR: buenos puntos, mira mi actualización anterior. – perimosocordiae

Respuesta

17

En GHCi, cada let declaración introduce una definición nueva de ex, en lugar de múltiples casos de patrones como se espera. Por lo tanto, se bloquea porque, cuando ingresa ex 1 (sum [1..]) después, solo existe la versión final ex b x = b ** x.

Si desea definir una función con múltiples casos de patrón en GHCi, tendrá que ponerlo en una sola let declaración, así:

let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x 

Lo mismo se aplica para cualquier otra cosa que normalmente se escribirá en varias líneas, como la notación do. Por ejemplo, una función como esta:

f x = do 
    y <- get 
    put (x + y) 
    return y 

tendría que ser escrita como este en GHCi:

let f x = do { y <- get; put (x + y); return y } 
+0

Ajá, eso es realmente inconveniente. Bueno, es bueno saberlo, gracias! – perimosocordiae

+6

@perimosocordiae: Como un usuario bastante pesado de GHCi, generalmente he tomado el hábito de escribir la mayoría o todas mis definiciones en un archivo externo, cargar el archivo con GHCi y usar REPL sobre todo para evaluar expresiones simples. –

+2

También puede usar ': set + m' para escribir definiciones en múltiples líneas. – aleator

-2

Perdí un punto de pereza, lo que hace que la respuesta a continuación sea falsa.


Debido sum calcula la suma total de todos los elementos de la secuencia. Que es, en tu caso, interminable.

es probable que desee

map ((curry ex) 1) [1..] 

Eso es

map -- map each item x to y 
    (
     (
      curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z 
     ) 
     1 -- then invoke it with 1, which results in y -> z, x being 1 
    ) 
    [1..] -- the infinite sequence to be mapped. 
+0

Hm, no creo que sea eso. Ver mi actualización arriba. – perimosocordiae

+0

Malditas actualizaciones. Sí, bastante obvio ahora cuál es el problema. – Dykam

+0

-1: Solo evaluaría la suma si realmente se necesitara, y en este caso no fue así. – Zifre

1
Prelude> let ex 1 x = 1 
Prelude> let ex b x = b ** x 

No está definiendo una función con dos casos aquí. Define una función con un caso y luego la define de nuevo anulando la definición anterior.

Para definir una función con dos patrones, utilice let ex 1 x = 1; ex b x = b ** x, es decir, separe las cajas con un punto y coma.

Cuestiones relacionadas