[基础软件理论与实践] 第二节作业实现 lxr2010

lxr2010 2022-11-11 13:08:29

作业信息

  1. Implement the substitution function N[v/x] : subst (N: lambda, x: string, v: value) : lambda

  2. Think about how substitution works on arbitrary terms, i.e. N[M/x] where M could contain free variables.

  3. Implement Church numberals and arithmetic functions using lambda calculus

作业实现

任务1&2

实现替换函数subst(N:lambda, x:string, v:lambda)

  • 对于Var(a)类型的lambda表达式,如果a == x,那么使用v替换Var(a);

  • 对于App(m,n)类型的lambda表达式,那么分别在mn中替换v;

  • 对于Fun(f,body)类型的lambda表达式,需要根据fv中的自由变量进行分类讨论:

​ 在body中,对于变量f的引用均指向受控变量f(bounded variable f);

​ 在v中,有可能存在对自由变量f(free variable f)的引用;

​ 两类引用指向的变量名称虽然相同,但是实际含义并不相同。比如(λx.λy.yx)M,应用β规约,应当使用M替换λy.yx中的变量x。注意λy.yx仍然是一个函数,函数体yx中引用了受控变量y。如果M中包含自由变量y,直接使用M替换x得到λy.yM会得到错误的结果。一种极端情况是M = y,替换的结果是λy.yy,显然不正确。

​ 回到Fun(f,body)。对于这种情况,需要在body中存在与函数的受控变量f相同名称的自由变量时,为受控变量f重新命名,保证新名称不和body中的自由变量重复。上例中,如果在使用M替换λy.yx中的变量x前,将y替换为M中没有引用的变量名z,那么替换的结果为λz.zM,当M = y时,替换的结果为λz.zy,是正确的结果。

下面贴下代码。代码中将产生冲突的函数受控变量重命名为@i。顺带给出了lambda表达式open form的求值函数eval:

module Lambda = {
  type rec t = 
    | Var (string)
    | App (t, t)
    | Fun (string, t)

  let rec toString = (t : t) :string => {
    switch t {
      | Var(x) => x
      | App(m, n) => "(" ++ toString(m) ++ toString(n) ++ ")"
      | Fun(f, arg) => "(λ" ++ f ++ "." ++ toString(arg)++")"
    }
  }

  let rec equal = (s:t ,t: t) : bool => {
    switch (s,t) {
    | (Var(a), Var(b)) => a == b 
    | (App(m, n), App(p, q)) => equal(m, p) && equal(n, q)
    | (Fun(f, a), Fun(g, b)) => f == g && equal(a, b)
    | _ => false
    }
  }

  let rec getFreeVariables = (t: t): list<string> => {
    switch t {
    | Var(x) => list{x}
    | App(m, n) => Belt.List.concatMany([getFreeVariables(m), getFreeVariables(n)])
    | Fun(f, arg) => getFreeVariables(arg)->Belt.List.keep((x)=> x != f)
    }
  }
  
  let nameOrder = ref(0)
  // get a new name "@i", where i comes from a mutable int
  let getNewname = () : string => {
    nameOrder := nameOrder.contents + 1
    "@" ++ Js.Int.toString(nameOrder.contents)
  }

// When va contains unbounded variable with the same name of bounded varaibles in body,
// conflicts will occur.
// Solution: assign new name to those bounded varaiables in body which share the same names of unbounded variables.
  let rec subst = (x: string, va:t, body: t) : t => {
    switch body {
      | Var(a) if a == x => va
      | Var(_) => body
      | App(m, n) => App(subst(x, va, m), subst(x, va, n))
      | Fun(f, arg) => if Belt.List.has(getFreeVariables(va),f,String.equal) {
        let newSym = getNewname()
        let newArg = subst(f,Var(newSym),arg)
        if f == x {
          Fun(newSym,subst(newSym,va,newArg))
        }
        else {
          Fun(newSym, subst(x, va, newArg))
        }
      } 
      else {
        if f == x {
          body
        }
        else {
          Fun(f, subst(x, va, arg))
        }
      }
    }
  } 

  let rec eval = (t: t) => {
    switch t {
    | Var(_) => t
    | Fun(x, body) => Fun(x, eval(body))
    | App(f, arg) => {
        switch eval(f) {
        | Fun(x, body) => {
          let va = eval(arg)
          eval(subst(x, va, body))
          }
        | k => App(k, eval(arg))
        }
      }  
    }
  }
}
任务3

使用lambda实现Church number和算数运算。

下面是实现代码。PPT中提到的乘法运算的递归实现无法直接用上节的eval进行求值。涉及到Y组合子和Ω组合子的表达式,使用eval求值无法终止。


let omega = {
  open Lambda
  let smallOmega = Fun("x",App(Var("x"),Var("x")))
  App(smallOmega, smallOmega)
}

let ycomb = {
  open Lambda
  Fun("f",App(Fun("x",App(Var("f"),App(Var("x"),Var("x")))),Fun("x",App(Var("f"),App(Var("x"),Var("x"))))))
}

Js.log(Lambda.toString(ycomb))

let succ = {
  open Lambda
  Fun("n",Fun("f",Fun("x",App(Var("f"),App(App(Var("n"),Var("f")),Var("x"))))))
}

let zero = {
  open Lambda
  Fun("f",Fun("x",Var("x")))
}

let one = {
  open Lambda
  Fun("f",Fun("x",App(Var("f"),Var("x"))))
}

let mul = {
  open Lambda
  Fun("n",Fun("m",Fun("f",App(Var("n"),App(Var("m"),Var("f"))))))
}

let exp = {
  open Lambda
  Fun("n",Fun("m",App(Var("m"),Var("n"))))
}

let if_then_else = {
  open Lambda
  Fun("t",Fun("x",Fun("y",App(App(Var("t"),Var("x")),Var("y")))))
}

let trueC = {
  open Lambda
  Fun("x",Fun("y",Var("x")))
}

let falseC = {
  open Lambda
  Fun("x",Fun("y",Var("y")))
}

let add = {
  open Lambda
  Fun("n",Fun("m",Fun("f",Fun("x",App(App(Var("n"),Var("f")),App(App(Var("m"),Var("f")),Var("x")))))))
}

let iszero = {
  open Lambda
  Fun("n",App(App(Var("n"),Fun("z",falseC)),trueC))
}

let pair = {
  open Lambda
  Fun("x",Fun("y",Fun("z",App(App(Var("z"),Var("x")),Var("y")))))
}

let fst = {
  open Lambda
  Fun("p",App(Var("p"),trueC))
}

let snd = {
  open Lambda
  Fun("p",App(Var("p"),falseC))
}


let pred = {
  open Lambda
  Fun("n",App(
    fst,
    App(
      App(
        Var("n"),
        Fun("p",App(
          App(pair,App(
            snd,
            Var("p"))),
          App(succ,App(
            snd,
            Var("p")))))),
      App(
        App(
          pair,
          zero),
        zero))))
}


let mulR = {
  open Lambda
  let f = {
    Fun("f",Fun("n",Fun("m",App(App(App(if_then_else,App(iszero,Var("n"))),zero),App(App(add,Var("m")),App(App(Var("f"),App(pred,Var("n"))),Var("m")))))))
  }
  App(ycomb,f)
}

let toChurchNum = (n :int) => {
  let rec helper = (n : int, churchNum: Lambda.t) : Lambda.t => 
    if n < 0 {assert false}
    else {
      switch n {
        | 0 => churchNum
        | _ => helper(n-1, App(succ,churchNum))
      }
    }
  helper(n,zero)
}

let numbers = {
  list{
    toChurchNum(0),
    toChurchNum(1),
    toChurchNum(2),
    toChurchNum(3),
    toChurchNum(4),
    toChurchNum(5)
  }
}

let churchNumToInt = (t : Lambda.t) :int => {
  open Lambda
  let f = Var("f")
  let x = Var("x")
  let va = eval(App(App(t,f),x))
  let rec decomposeChurch = (va' :t, f' :t, x' : t) => {
    switch va' {
    | Var(_) if va' == x' => 0
    | App(g, y) if g == f' => decomposeChurch(y, f', x') + 1
    | _ => assert false 
    }
  }
  decomposeChurch(va,f,x)
}

let mul2by2 = {
  open Lambda
  App(App(mulR,toChurchNum(2)),toChurchNum(2))
}

下面是测试:

Js.log(Lambda.toString(omega))

Js.log(Lambda.toString(ycomb))

List.iter((x)=>(Js.log(Lambda.toString(Lambda.eval(x)))), numbers)

List.iter((x)=>(Js.log(Lambda.toString(Lambda.eval(App(pred,x))))), numbers)

Js.log(churchNumToInt(App(pred,toChurchNum(15))))

输出:

((λx.(xx))(λx.(xx)))
(λf.((λx.(f(xx)))(λx.(f(xx)))))
(λf.(λx.x))
(λf.(λx.(fx)))
(λf.(λx.(f(fx))))
(λf.(λx.(f(f(fx)))))
(λf.(λx.(f(f(f(fx))))))
(λf.(λx.(f(f(f(f(fx)))))))
(λf.(λx.x))
(λf.(λx.x))
(λf.(λ@5.(f@5)))
(λf.(λ@14.(f(f@14))))
(λf.(λ@26.(f(f(f@26)))))
(λf.(λ@41.(f(f(f(f@41))))))
14
...全文
261 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
编译器助手 2022-11-11
  • 打赏
  • 举报
回复

230

社区成员

发帖
与我相关
我的任务
社区描述
日程:https://bbs.csdn.net/topics/608593392 主页:https://bobzhang.github.io/courses/ B站: “张宏波的基础软件课程”
rescript开发语言 个人社区 广东省·深圳市
社区管理员
  • raelidea
  • MoonBit月兔
  • 幻灰龙
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧