避免显式传递查找表

时间:2011-09-21 17:28:19

标签: haskell recursion arguments monads implicit

在我非常简单的布尔表达玩具程序中,我有以下评估函数:

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

我想知道是否有办法隐式传递values表?也许在Monads的帮助下?

4 个答案:

答案 0 :(得分:13)

Monads会起作用,但在我看来,使用Applicative更清晰:

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

这是使用((->) r)实例,如Reader monad / applicative,但没有newtype包装。

答案 1 :(得分:9)

这正是Reader monad的用途:

  

Reader monad(也称为Environment monad)。代表一个   通过计算,可以从共享环境中读取值   从函数到函数的值,并在a中执行子计算   改良环境。

正如Sjoerd所说,monad在这里提供的功能超出了你的需要,但你仍然可以使用Reader monad来解决这个问题,而无需输入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

只需将您的环境类型作为Reader类型构造函数的第一个参数,将原始结果类型作为第二个参数。

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

而不仅仅c作为Const案例的值,请使用return将其提升为monad:

eval' (Const c) = return c

当您需要环境来查找变量的值时,请使用ask。使用do表示法,您可以像这样编写Var案例:

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

我认为使用fmap a.k.a. <$>更好:

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

同样,一元not可以fmap对递归结果进行处理:

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

最后,阅读器的Applicative实例使二进制案例变得愉快:

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

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

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

然后,为了全部启动,这里有一个帮助器来创建初始环境并运行计算:

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

答案 2 :(得分:4)

在这种情况下,请勿完全传递values

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)
    ...

答案 3 :(得分:2)

你知道扩展名implicit parameters吗?这可能非常有用。

相关问题