在我非常简单的布尔表达玩具程序中,我有以下评估函数:
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的帮助下?
答案 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吗?这可能非常有用。