[基础软件理论与实践] 第二节作业实现 lxr2010
作业信息
Implement the substitution function N[v/x] : subst (N: lambda, x: string, v: value) : lambda
Think about how substitution works on arbitrary terms, i.e. N[M/x] where M could contain free variables.
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表达式,那么分别在m和n中替换v;对于
Fun(f,body)类型的lambda表达式,需要根据f和v中的自由变量进行分类讨论:
在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
相关推荐
lxr-0.3.tar.gz_lxr_lxr tar
项目源码察看工具lxr-0.3.tar.gz
lxr-0.9.5.zip_lxr_lxr-0.9.5
lxr 是linux 浏览网上开源代码,如linux内核等等,的好工具
lxr-0.9.10.gz_lxr_lxr source download
kernel source code browser : lxr
LXR:LXR鼓合成器固件的源文件
Sonic Potions LXR Drumsynth固件 LXR是基于32位Cortex-M4处理器和Atmega644 8位CPU的数字鼓合成器。 由朱利安·施密特(Julian Schmidt)开发。 The 'front' folder contains the AVR code The 'mainboard' folder contains the STM32F4 code The 'tools' folder contains the firmware image builder tool, to combine AVR and Cortex code into a single file, usable by the bootloader. 请注意,该项目的主板代码中使用了ST和ARM的库。 它们都位于项目的“库”子文件夹中。 那些带有自己的许可证。 这些库是: ARM
lxr-0.9.6.tgz
Linux下的源代码阅读工具0.9.6版


