[英]Haskell Recursion
我一直在为给定表达式中的变量添加计数器,并具有以下测试用例:
prop_add1 = add (And (Var "a") (And (Var "b") (Var "a"))) ==
And (Var "a1") (And (Var "b2") (Var "a1"))
我一直在使用模式匹配和递归来尝试找到解决方案。 尽管有些卡住,但我尝试将原始变量添加到列表中,然后使用列表确定要输出的变量名称,但我不知道如何正确实现它,我的输出仅添加了1到所有变量的末尾。
我想知道是否有更好/更轻松的解决方案?
到目前为止,我的尝试:
add :: Expr -> Expr
add T = T
add (Var x) = Var (x ++ show (check2(check x)))
add (And e1 e2) = And (add e1) (add e2)
add (Not e1) = Not (add e1)
check :: Variable -> [Variable]
check p = [p]
check2 :: [Variable] -> Int
check2 p = length (union p p)
您需要保留一个将变量名映射为数字的环境。 就像是
add :: Expr -> Expr
add expr = fst $ addWithEnv emptyEnv expr
where
emptyEnv = []
addWithEnv env (And e1 e2)
= case addWithEnv env e1 of
(e1', env') ->
case addWithEnv env' e2 of
(e2', env'') -> (And e1' e2', env'')
addWithEnv env (Var name)
= case lookup name env of
Just k -> -- stopping here,it's homework
希望我已经留给您足够的信息。
更新:
在尝试时,您不会跟踪已经看到的变量,因此每个变量似乎都是第一个,并且每次附加“ 1”。 编号项目是有状态的计算,您必须记录迄今为止已看到的变量,才能将以前看到的变量分配给旧编号,并知道要分配下一个尚未看到的变量的编号。 因此,您必须随身携带该记录。 如果您已经知道Monad
类及其使用方法,则可以使用State
monad隐式地携带它,否则必须显式携带它。 然后add
成为包装器,该包装器以初始为空的状态调用工作器(在编号/重新命名开始之前,尚未看到任何变量)。 然后,工作程序查看给定表达式的子表达式(如果有),并重命名变量并在遇到新变量时更新状态。
所以在上面的草图中,我们有
addWithEnv :: [(String,Int)] -> Expr -> (Expr, [(String,Int)])
由于我们无法更改状态,因此我们必须返回新状态以及重命名的表达式。 现在,您必须定义每种表达式类型的结果,
addWithEnv env T = ??
addWithEnv env (Var name) = ??
addWithEnv env (Add e1 e2) = ??
addWithEnv env (Not e) = ??
T
情况当然不会重命名,也不会更新环境。 Var
以前已经见过,在这种情况下,环境保持不变,或者没有,在这种情况下将其添加到环境中。 Not e
对环境的影响与e
,而And e1 e2
e2
对环境的影响首先是e1
然后是e2
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.