List 哈斯克尔递归
我一直在为给定表达式中的变量添加计数器,并有以下测试用例:List 哈斯克尔递归,list,haskell,recursion,List,Haskell,Recursion,我一直在为给定表达式中的变量添加计数器,并有以下测试用例: prop_add1 = add (And (Var "a") (And (Var "b") (Var "a"))) == And (Var "a1") (And (Var "b2") (Var "a1")) 我一直在使用模式匹配和递归来尝试找到解决方案。虽然我遇到了一些问题,但我尝试将原始变量添加到列表中,然后使用该列表确定要输出的变量名,但我不知道如何正确实现它,我的输出只是在所有变量的末尾添加了1 我想知道是否有更好/更简单的解决
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
成为一个包装器,它以一个初始为空的状态调用worker(在开始编号/重命名之前,还没有看到任何变量)。然后,工作者查看给定表达式(如果有)的子表达式,并在遇到新变量时重命名变量和更新状态
在上面的草图中,我们有
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
以前见过,在这种情况下,环境保持不变,或者没有,在这种情况下,它被添加到环境中。非e
对环境的影响与e
相同,和e1 e2
对环境的影响是先e1
然后e2
。你能详细解释一下你打算做什么吗,您当前的解决方案是什么?您不喜欢它的哪些方面?我只想输出与输入中给出的表达式几乎相同的表达式,除了为变量指定数字。我目前的解决方案——当我遇到一个变量时,我调用一个函数,将变量添加到列表中,然后调用另一个函数,该函数获取该列表,只获取该列表的长度,并将其添加到变量的末尾。我的理论是,随着列表的增长,数量也会增加,但这将不可避免地导致“a1”和“a2”,而不仅仅是“a1”。一旦理解了这种模式,就有必要知道它被称为“State monad”,它无处不在,存在于名为Control.monad.State
的库中,允许您编写更短、更好的代码(没有env'
的语法混乱)。嗯,尝试找出模式-您能在这里解释backticks的用法吗?到目前为止,我只见过它像“foo”一样应用,这里没有背景标记,只有素数/撇号,这是一种通用的模式,用于命名从原始版本中获得的变量,目的相同,因此,例如,env'
是通过使用初始的env
一步获得的环境。另一种常用的命名方法是env1
,env2
等。而不是env'
,env'
。。。由于值是不可变的,所以我们必须使用新名称(有时可以使用相同的名称并对以前的绑定进行阴影处理,但这很容易导致非终止循环,因此应该非常小心)。谢谢您的解释,我明白了。虽然你的解决方案看起来不错,但我有点不知所措,我将向你展示我最初的尝试,通过编辑我的原始帖子来适应它。此外,当我完成时,我在函数addWithEnv中不断得到错误非详尽模式,或者至少尝试过,但无论如何,谢谢。