Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
List 哈斯克尔递归_List_Haskell_Recursion - Fatal编程技术网

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中不断得到错误非详尽模式,或者至少尝试过,但无论如何,谢谢。