2011-09-21 5 views
7

En mi programa muy simple juguete expresión booleana, que tienen la siguiente función de evaluación:evitar el paso explícito de la tabla de consulta

eval' :: Expr -> M.Map Char Bool -> Bool 

eval' (Const c) values = c 

eval' (Var v) values = M.findWithDefault False v values 

eval' (Not x) values = not (eval' x values) 

eval' (And a b) values = eval' a values && eval' b values 

eval' (Or a b) values = eval' a values || eval' b values 

eval' (Xor a b) values = eval' a values /= eval' b values 

Me preguntaba si había una manera de pasar la tabla values implícitamente? Tal vez con la ayuda de Monads?

Respuesta

4

En este caso, no pase values en absoluto:

eval' :: Expr -> M.Map Char Bool -> Bool 
eval' e values = eval'' e 

    where 
    eval'' (Const c) = c 
    eval'' (Var v) = M.findWithDefault False v values 
    eval'' (Not x) = not (eval'' x) 
    ... 
+0

No solo es más simple, también es más eficiente. Creo que se llama transformación de argumento estático. –

13

Mónadas iba a funcionar, pero en mi opinión el uso de Applicative es más limpio aquí:

eval' :: Expr -> M.Map Char Bool -> Bool 
eval' (Const c) = pure c 
eval' (Var v) = M.findWithDefault False v 
eval' (Not x) = not <$> eval' x 
eval' (And a b) = (&&) <$> eval' a <*> eval' b 
eval' (Or a b) = (||) <$> eval' a <*> eval' b 
eval' (Xor a b) = (/=) <$> eval' a <*> eval' b 

Se trata de utilizar la instancia ((->) r), como la mónada Reader/aplicativo, pero sin el envoltorio newtype.

+0

que habría alcanzado para el 'mónada Reader', como lo hizo acfoltzer, pero me gusta la mirada limpia de esta solución. – pat

+0

'ocurrencia ambigua 'Const': podría referirse a 'Main.Const' o 'Control.Applicative.Const'' Oh, bueno, quería cambiar el nombre de mi' Const' a 'Val', de todos modos :) – fredoverflow

+1

O podría simplemente 'importar Control.Applicative (pure, (<*>), (<$>)) :) – fredoverflow

9

Esto es exactamente lo que el Reader monad es para:

La mónada Reader (también llamada la mónada Medio Ambiente). Representa un cálculo , que puede leer valores de un entorno compartido, pasar los valores de la función a la función y ejecutar subcomputaciones en un entorno modificado .

Como notas Sjoerd, la mónada da más poder aquí de lo que necesita, pero todavía se puede utilizar la mónada Reader para este problema sin mucho más que escribir do:

import qualified Data.Map as M 

import Control.Applicative ((<$>), (<*>)) 
import Control.Monad.Reader 

data Expr = Const Bool 
      | Var Char 
      | Not Expr 
      | And Expr Expr 
      | Or Expr Expr 
      | Xor Expr Expr 

Sólo hay que poner el tipo de entorno como el primer argumento para el constructor de tipo Reader, y su tipo de resultado original como el segundo.

eval' :: Expr -> Reader (M.Map Char Bool) Bool 

En lugar de simplemente c como el valor de la caja Const, utilice return a elevar ésta en la mónada:

eval' (Const c) = return c 

Cuando se necesita el entorno para buscar el valor de una variable, el uso ask. Usando do notación, se puede escribir el caso Var así:

eval' (Var v) = do values <- ask 
        return (M.findWithDefault False v values) 

Creo que es más agradable, sin embargo, utilizar fmap aka <$>:

eval' (Var v) = M.findWithDefault False v <$> ask 

Del mismo modo, el unario not puede fmap PED sobre el resultado de la recursión:

eval' (Not x) = not <$> eval' x 

Fina lly, la Applicative instancia del lector hace que los casos binarios agradable:

eval' (And a b) = (&&) <$> eval' a <*> eval' b 

eval' (Or a b) = (||) <$> eval' a <*> eval' b 

eval' (Xor a b) = (/=) <$> eval' a <*> eval' b 

Entonces, para conseguir que todo empezó, aquí está un ayudante para crear el entorno inicial y ejecutar el cálculo:

eval :: Expr -> [(Char,Bool)] -> Bool 
eval exp env = runReader (eval' exp) (M.fromList env) 
2

¿Te saber la extensión implicit parameters? Podría ser bastante útil.

+2

Hubo [una discusión sobre r/haskell al respecto recientemente] (http://www.reddit.com/r/haskell/comments/kbzjk/what_about_the_implicitparams_haskell_language/). El consenso parece ser que no es tan útil, principalmente porque se filtran en las firmas de tipos, lo que te obliga a no usar firmas de ningún tipo, o terminas con la misma cantidad de repetición que intentabas eliminar. – hammar

+0

@hammar Buen punto. – fuz

Cuestiones relacionadas