2011-09-21 13 views
7

Nel mio programma molto semplice giocattolo espressione booleana, Ho la seguente funzione di valutazione:evitare esplicito passaggio di tabella di ricerca

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 

mi chiedevo se ci fosse un modo per passare il tavolo values implicitamente? Forse con l'aiuto di Monads?

risposta

4

In questo caso, non passare values affatto:

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

Non solo è più semplice, probabilmente è anche più efficiente. Credo che si chiami la trasformazione dell'argomento statico. –

13

Monadi avrebbe funzionato, ma a mio parere utilizzando Applicative è più pulita qui:

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 

Questo sta usando l'istanza ((->) r), come il Reader Monade/applicativa, ma senza l'involucro newtype.

+0

avrei raggiunto per la Reader' Monade ', come acfoltzer ha fatto, ma mi piace l'aspetto pulito di questa soluzione. – pat

+0

'Occorrenza ambigua 'Const': potrebbe riferirsi a 'Main.Const' o 'Control.Applicative.Const'' Oh beh, volevo cambiare il mio' Const' a 'Val', comunque :) – fredoverflow

+1

Oppure potevo solo importare Control.Applicativo (puro, (<*>), (<$>)) ':) – fredoverflow

9

Questo è esattamente ciò che il Reader monad è per:

La monade Reader (chiamato anche l'ambiente Monade). Rappresenta un calcolo , che può leggere i valori da un ambiente condiviso, passare i valori dalla funzione alla funzione ed eseguire i calcoli secondari in un ambiente modificato .

Come osserva Sjoerd, la monade dà più potere qui che è necessario, ma è comunque possibile utilizzare la monade Reader per questo problema senza nemmeno digitando 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 

Basta mettere il vostro tipo di ambiente come primo argomento per il costruttore del tipo Reader e il tipo di risultato originale come secondo.

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

Invece di c come il valore della causa Const, utilizzare return per sollevare in monade:

eval' (Const c) = return c 

Quando è necessario l'ambiente per la ricerca del valore di una variabile, l'uso ask. Utilizzando do la notazione, è possibile scrivere il caso Var in questo modo:

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

Penso che sia più bello, però, di utilizzare fmap aka <$>:

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

Allo stesso modo, l'unario not può essere fmap PED over il risultato della ricorsione:

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

Fina lly, l'istanza Applicative di Reader rende i casi binari piacevole:

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

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

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

Poi, per ottenere tutto è cominciato, ecco un aiuto per creare l'ambiente iniziale ed eseguire il calcolo:

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

Ti conosci l'estensione implicit parameters? Potrebbe essere abbastanza utile.

+2

C'era [una discussione su r/haskell su di esso recentemente] (http://www.reddit.com/r/haskell/comments/kbzjk/what_about_the_implicitparams_haskell_language/). Il consenso sembra essere che non è così utile, principalmente perché perdono nelle firme del tipo, costringendo quindi a non utilizzare affatto le firme del tipo, o alla fine con la stessa quantità di testo che si stava cercando di eliminare. – hammar

+0

@hammar Buon punto. – fuz

Problemi correlati