2011-09-14 16 views
5

Estoy tratando de encontrar una buena manera de memorizar una función para solo una parte de su dominio (enteros no negativos) en Haskell, usando Data.MemoCombinators.Memorización parcial en Haskell

import Data.MemoCombinators 

--approach 1 

partFib n | n < 0 = undefined 
      | otherwise = integral fib n where 
    fib 0 = 1 
    fib 1 = 1 
    fib k = partFib (k-1) + partFib (k-2) 

--approach 2 

partFib2 n | n < 0 = undefined 
      | otherwise = fib n 
fib = integral fib' 
    where 
    fib' 0 = 1 
    fib' 1 = 1 
    fib' n = partFib2 (n-1) + partFib2 (n-2) 

Enfoque 1 es cómo me gustaría hacerlo, sin embargo, parece que no funciona. Supongo que esto se debe a que la función fib se "recrea" cada vez que se invoca partFib, descartando la memorización. fib no depende de la entrada de partFib, por lo que podría suponer que el compilador podría izarlo, pero aparentemente GHC no funciona de esa manera.

Enfoque 2 es cómo termino haciéndolo. Eerk, muchos cables feos.

¿Alguien sabe de una mejor manera de hacer esto?

Respuesta

6

No estoy seguro de lo que es "feo" para su ojo, pero puede memorizar correctamente mientras usa solo un identificador de nivel superior al sacar la operación de memoria de la función n.

partFib3 = \n -> if n < 0 then undefined else fib' n 
    where fib 0 = 1 
      fib 1 = 1 
      fib k = partFib3 (k-1) + partFib3 (k-2) 
      fib' = integral fib 
+0

Gracias, esto es mejor que lo que tenía. Nada es realmente tan "feo". Solo quiero que la definición se vea lo más similar posible a la implementación ingenua de Fibonacci. – dainichi

+0

¿Por qué esto hace la diferencia? ¿No es 'f n = e' semánticamente exactamente lo mismo que' f = \ n -> e'? –

+2

@Dan, semánticamente, sí. Pero la memorización entra al territorio operacional. La 'n' en el primero tiene un alcance sobre la cláusula' where', mientras que 'n' en el último no lo está, cambiando así la forma en que se comparten los thunks en la cláusula where. – luqui

4

Hmm lo de separar un poco las cosas:

fib 0 = 0 
fib 1 = 1 
fib x = doFib (x-1) + doFib (x-2) 

memFib = Memo.integral fib 

doFib n | n < 0 = fib n 
     | otherwise memFib n 

Ahora tiene que utilizar doFib.

+0

Gracias. Me gusta esto también. Separa la definición de la función limpiamente de la definición de lo que se memoriza. – dainichi

4

No es un combinador en la biblioteca para este propósito:

switch :: (a -> Bool) -> Memo a -> Memo a -> Memo a 

switch p a b utiliza la tabla de notas a siempre p da verdadera y la tabla de notas b siempre p da falsa.

Recordemos que id es técnicamente un memoizer (que no memoize :-), por lo que puede hacer:

partFib = Memo.switch (< 0) id Memo.integral fib' 
    where 
    ... 
+1

Por "un memorando (que no memoriza :-)" ¿quiere decir que su tipo es 'Memo a', por lo que el sistema de tipo le permitirá usarlo como un memorando? – MatrixFrog

+0

@MatrixFrog, sí, exactamente. – luqui