简体   繁体   English

避免捕获的替换函数——Lambda演算

[英]Capture-avoiding substitution function -- Lambda calculus

I am trying to write a function that performs capture-avoiding substitution in Lambda calculus.我正在尝试编写一个在 Lambda 演算中执行避免捕获替换的函数。 The code compiles but does not spit out the correct answer.代码编译但没有吐出正确的答案。 I've written what I expect the code to do, is my comprehension correct?我已经写了我期望代码做的事情,我的理解正确吗?

For example, I should get the following output for this input ( numeral 0 is the Church numeral 0)例如,我应该得到这个输入的以下输出( numeral 0是教堂数字 0)

 *Main> substitute "b" (numeral 0) example -- \a. \x. ((\y. a) x) b \c. \a. (\a. c) a (\f. \x. x) -- The incorrect result I actually got \c. \c. (\f. \x. x) (x (\b. a))

NB \y is renamed to \a due to the substitution (\ya)[N/b] (I think I have this covered in the code I have written, but please let me know if I am wrong.)注意\y被重命名为\a由于替换(\ya)[N/b] (我想我已经在我编写的代码中涵盖了这一点,但如果我错了请告诉我。)

import Data.Char
import Data.List

type Var = String

data Term =
    Variable Var
  | Lambda   Var  Term
  | Apply    Term Term
  --  deriving Show

instance Show Term where
  show = pretty

example :: Term        -- \a. \x. ((\y. a) x) b
example = Lambda "a"
            (Lambda "x" (Apply (Apply (Lambda "y" (Variable "a")) 
                                      (Variable "x")) 
                               (Variable "b")))

pretty :: Term -> String
pretty = f 0
    where
      f i (Variable x) = x
      f i (Lambda x m) = if i /= 0 then "(" ++ s ++ ")" else s 
                         where s = "\\" ++ x ++ ". " ++ f 0 m 
      f i (Apply  n m) = if i == 2 then "(" ++ s ++ ")" else s 
                         where s = f 1 n ++ " " ++ f 2 m

substitute :: Var -> Term -> Term -> Term

substitute x n (Variable y)  
    --if y = x, then leave n alone   
    | y == x    = n
    -- otherwise change to y  
    | otherwise = Variable y

substitute x n (Lambda y m)
    --(\y.M)[N/x] = \y.M if y = x 
    | y == x    = Lambda y m
    --otherwise \z.(M[z/y][N/x]), where `z` is a fresh variable name 
    --generated by the `fresh` function, `z` must not be used in M or N, 
    --and `z` cannot be equal `x`. The `used` function checks if a 
    --variable name has been used in `Lambda y m`   
    | otherwise = Lambda newZ newM
                  where newZ = fresh(used(Lambda y m))
                        newM = substitute x n m          

substitute x n (Apply  m2 m1) = Apply newM2 newM1
    where newM1 = substitute x n m2
          newM2 = substitute x n m1

used :: Term -> [Var]
used (Variable n) = [n]
used (Lambda n t) = merge [n] (used t)
used (Apply t1 t2) = merge (used t1) (used t2)

variables :: [Var]
variables =  [l:[] | l <- ['a'..'z']] ++ 
             [l:show x | x <- [1..], l <- ['a'..'z']]

filterFreshVariables :: [Var] -> [Var] -> [Var]
filterFreshVariables lst = filter ( `notElem` lst)

fresh :: [Var] -> Var
fresh lst = head (filterFreshVariables lst variables)

recursiveNumeral :: Int -> Term
recursiveNumeral i
  | i == 0 = Variable "x"
  | i > 0 = Apply(Variable "f")(recursiveNumeral(i-1))

numeral :: Int -> Term
numeral i = Lambda "f" (Lambda "x" (recursiveNumeral i))

merge :: Ord a => [a] -> [a] -> [a]
merge (x : xs) (y : ys)
  | x < y = x : merge xs (y : ys)
  | otherwise = y : merge (x : xs) ys
merge xs [] = xs
merge [] ys = ys

This part in substitute xn (Lambda ym) is not correct: substitute xn (Lambda ym)中的这部分不正确:

  • the comment says " z must not be used in M or N ", but there is nothing preventing that.评论说“ z不得在MN中使用”,但没有什么能阻止这一点。 newZ could be a variable in n , which leads to a problematic capture newZ可能是n中的一个变量,这会导致捕获问题
  • the substitution z/y has not been done替换z/y尚未完成
    | otherwise = Lambda newZ newM
                  where newZ = fresh(used(Lambda y m))
                        newM = substitute x n m

Fix:使固定:

  1. " z must not be used in M or N ": z不得用于MN ”:
newZ = fresh(used m `merge` used n)
  1. " M[z/y][N/x] ": M[z/y][N/x] ”:
newM = substitute x n (substitute y (Variable newZ) m)

Put together:放在一起:

    | otherwise = Lambda newZ newM
    where
      newZ = fresh(used m `merge` used n)
      newM = substitute x n (substitute y (Variable newZ) m)

Note that refreshing all bindings as done above makes it difficult to understand the result and to debug substitution.请注意,如上所述刷新所有绑定会导致难以理解结果和调试替换。 Actually y only needs to be refreshed if y is in n .实际上y只有在yn时才需要刷新。 Otherwise you can keep y , adding this clause:否则,您可以保留y ,添加以下子句:

    | y `notElem` used n = Lambda y (substitute x n m)

Another idea would be to modify fresh to pick a name similar to the old one, eg, by appending numbers until one doesn't clash.另一种想法是修改fresh以选择与旧名称相似的名称,例如,通过附加数字直到不发生冲突。


There is still a bug I missed: newZ should also not be equal to x (the variable originally being substituted).我仍然错过了一个错误: newZ也不应该等于x (最初被替换的变量)。

-- substitute [a -> \f. \x. x] in (\g. g), should be (\g. g)
ghci> substitute "a" (numeral 0) (Lambda "g" (Variable "g"))
\a. \g. \x. x

Two ways to address this:解决这个问题的两种方法:

  1. add x to the set of variables to exclude newZ from:x添加到变量集以排除newZ

     newZ = fresh ([x] `merge` used m `merge` used n)
  2. if you think about it, this bug only manifests itself when x is not in m , in which case there is nothing to substitute, so another way is to add one more branch skipping the work:如果你想一想,这个错误只会在x不在m中时才会出现,在这种情况下没有什么可以替代的,所以另一种方法是添加一个更多的分支来跳过工作:

     | x `notElem` used m = Lambda ym

Put together:放在一起:

substitute x n (Lambda y m)
    --(\y.M)[N/x] = \y.M if y = x 
    | y == x    = Lambda y m
    | x `notElem` used m = Lambda y m
    | y `notElem` used n = Lambda y (substitute x n m)
    | otherwise = Lambda newZ newM
                  where newZ = fresh(used m `merge` used n)
                        newM = substitute x n (substitute y (Variable newZ) m)

Output输出

ghci> example
\a. \x. (\y. a) x b
ghci> numeral 0
\f. \x. x
ghci> substitute "b" (numeral 0) example
\a. \c. (\y. a) c (\f. \x. x)

Note: I haven't tried to prove this code correct (exercise for the reader: define "correct"), there may still be bugs I missed.注意:我没有试图证明这段代码是正确的(读者练习:定义“正确”),可能仍然有我错过的错误。 There must be some course about lambda calculus that has all the details and pitfalls but I haven't bothered to look.必须有一些关于 lambda 演算的课程,其中包含所有细节和陷阱,但我没有费心去看。

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

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