简体   繁体   English

Haskell递归

[英]Haskell Recursion

I've been working on adding a counter to variables in a given expression and have the following test case: 我一直在为给定表达式中的变量添加计数器,并具有以下测试用例:

prop_add1 = add (And (Var "a") (And (Var "b") (Var "a"))) ==
And (Var "a1") (And (Var "b2") (Var "a1"))

I have been using Pattern Matching and Recursion to try and find a solution. 我一直在使用模式匹配和递归来尝试找到解决方案。 Although I have come a little stuck, I have tried adding the original variables to a list and then using the list to determine the variable name to be output but I couldn't figure out how to correctly implement it and my output just adds a 1 to the end of all variables. 尽管有些卡住,但我尝试将原始变量添加到列表中,然后使用列表确定要输出的变量名称,但我不知道如何正确实现它,我的输出仅添加了1到所有变量的末尾。

I'm wondering is there a better/easier solution to this? 我想知道是否有更好/更轻松的解决方案?

My Attempt so far: 到目前为止,我的尝试:

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)

You need to keep an environment mapping variable names to numbers. 您需要保留一个将变量名映射为数字的环境。 Something like 就像是

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

I hope I've left enough for you to fill in. 希望我已经留给您足够的信息。

Update: 更新:

In your attempt, you don't keep track of which variables you have already seen, so every variable seems to be the first, and every time a '1' is appended. 在尝试时,您不会跟踪已经看到的变量,因此每个变量似乎都是第一个,并且每次附加“ 1”。 Numbering items is a stateful computation, you must have a record of which variables have been seen so far to assign previously seen variables the old number and know which number to assign the next not-yet-seen variable. 编号项目是有状态的计算,您必须记录迄今为止已看到的变量,才能将以前看到的变量分配给旧编号,并知道要分配下一个尚未看到的变量的编号。 So you must carry that record around in the worker. 因此,您必须随身携带该记录。 If you already know about the Monad class and how to use that, you can implicitly carry it around using the State monad, otherwise you have to carry it around explicitly. 如果您已经知道Monad类及其使用方法,则可以使用State monad隐式地携带它,否则必须显式携带它。 Then add becomes a wrapper that calls the worker with an initially empty state (before the numbering/renaming starts, no variable has yet been seen). 然后add成为包装器,该包装器以初始为空的状态调用工作器(在编号/重新命名开始之前,尚未看到任何变量)。 The worker then looks at the subexpressions of the given expression (if any) and renames variables and updates the state when a new variable is encountered. 然后,工作程序查看给定表达式的子表达式(如果有),并重命名变量并在遇到新变量时更新状态。

So in the sketch above, we have 所以在上面的草图中,我们有

addWithEnv :: [(String,Int)] -> Expr -> (Expr, [(String,Int)])

since we cannot mutate the state, we have to return the new state along with the renamed expression. 由于我们无法更改状态,因此我们必须返回新状态以及重命名的表达式。 Now you have to define what the result shall be for each type of expression, 现在,您必须定义每种表达式类型的结果,

addWithEnv env T = ??
addWithEnv env (Var name) = ??
addWithEnv env (Add e1 e2) = ??
addWithEnv env (Not e) = ??

The T case of course does no renaming and doesn't update the environment. T情况当然不会重命名,也不会更新环境。 A Var has either been seen before, in which case the environment remains unchanged, or not, in which case it is added to the environment. Var以前已经见过,在这种情况下,环境保持不变,或者没有,在这种情况下将其添加到环境中。 A Not e has the same influence on the environment as e , and an And e1 e2 has the combined effects of first e1 then e2 on the environment. Not e对环境的影响与e ,而And e1 e2 e2对环境的影响首先是e1然后是e2

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM