如何在haskell中替换表达式中的变量?

时间:2015-09-29 19:39:53

标签: haskell lambda-calculus

我正在使用haskell中实现的lambda演算。表达式:

%x.e  -- lambda abstraction, like \x->e in Haskell
e e'  -- application
x,y,z -- variables
succ, pred, ifzero, 0, 1, 2....

语法:

type Id = String

-- The "builtin" functions.
data Op = Succ | Pred | IfZero
       deriving (Show, Ord, Eq)

-- Expressions of L
data Exp = Var Id
     | Nat Integer
     | Op Op
     | Lam Id Exp
     | App Exp Exp
       deriving (Ord, Eq, Show)

还有一个将常用表示转换为Exp的函数:

    parse "%f. f 1 2" = Lam "f" App (App (Var "f") (Nat 1)) (Nat 2)`

我必须编写subst x e e'函数,用e代替所有" free"在(Var x)中出现e'。 "免"表示变量不是由周围的%声明的。例如,在表达式

x (%x. x (%y.xz)) (%y.x)

x次出现5次。第一个是"免费"。第二个是%表达式的参数,永远不会替换。第三次和第四次出现是指封闭%表达式的参数。第五个是免费的。因此,如果我们将0替换为x,我们会0 (%x. x (%y.xz)) (%y.0)

我需要使用的是模式匹配和递归。我所能写的只是函数原型

subst :: Id -> Exp -> Exp -> Exp
subst x (App z z') (App e e') =

如果有人能给我一个如何实现这个功能的提示,那就太好了。任何帮助都非常感谢

1 个答案:

答案 0 :(得分:1)

我想首先指出模式匹配(subst x (App z z') (App e e'))并非详尽无遗。 (大多数)其他模式都是微不足道的,所以很容易忘记它们。我建议不要使用模式匹配第三个参数,因为如果你所做的只是把它归入第二个参数,你就不在乎它是否是App lication或Nat ural。

大多数递归函数的第一步是考虑案例。基本案例是什么?在这种情况下,第二个参数等于Var x

-- Replace x with y in a Var. If the Var is equal to x, replace
-- otherwise, leave alone.
subst x (Var a) y
    | a == x    = y
    | otherwise = (Var a)

然后我们需要考虑步骤情况,如果它是一个应用程序,如果它是一个lambda抽象怎么办?

-- Replace x with y in an application. We just need to replace
-- x with y in z and in z', because an application doesn't
-- bind x (x stays free in z and z').
subst x (App z z') y = (App new_z new_z')
    where new_z  = -- Recursively call subst here.
          new_z' = -- And recursively call subst here, too.

-- Replace x with y in a lambda abstraction. We *do* need to
-- check to make sure that the lambda doesn't bind x. If the
-- lambda does bind x, then there's no possibility of x being
-- free in e, and we can leave the whole lambda as-is.
subst x (Lam z e) y
    | z == x    = (Lam z e)
    | otherwise = (Lam z new_e)
                  where new_e = -- Recursively call subst here.

最后,所有琐碎的案例都是一样的(我们单独留下Nat和Op,因为我们没有机会替换它们):

subst :: Id -> Exp -> Exp -> Exp
subst _ nat_or_op _ = nat_or_op