# [基础软件理论与实践] 第三节作业实现 yhgu2000

yhgu2000 2022-11-20 16:30:53

#### 作业信息

1. Complete the de Bruijn index based interpreter in natural semantics
2. Apply the de Bruijn index for extended lambda calculus (+ Let)

#### 作业实现

##### 任务1

``````  // Homework: implement the complete interpreter
let rec eval = (t: lambda) => {
switch t {
| Var(_) => t  // ⊥ 值
| Fn(f) => Fn(eval(f))  // 尽最大努力地求值
| App(a, b) =>
switch eval(a) {
| Fn(f) => eval(subst(f, 0, eval(b))) // 注意，替换后再递归求值
| a => App(a, eval(b))  // 尽最大努力地求值
}
}
}
``````

``````let rec bind = (t: lambda) => {
switch t {
| Var(_) => t
| Fn(f) => Fn(bind(f))
| App(a, b) =>
switch bind(a) {
| Fn(f) => subst(f, 0, bind(b)) // 唯一的区别：不会对替换结果进一步求值，从而保证一定能停机
| a => App(a, bind(b))
}
}
}

let rec eval = (t: lambda) => {
switch t {
| Var(_) => t
| Fn(f) => Fn(bind(f))
| App(a, b) =>
switch eval(a) {
| Fn(f) => eval(subst(f, 0, eval(b))) // 注意，替换后再递归求值
| a => App(a, eval(b))  // 尽最大努力地求值
}
}
}
``````

``````  let rec print_lambda = (t:lambda) => {
switch t {
| Var(j) => Belt.Int.toString(j)
| Fn(b) => "(\\. " ++ print_lambda(b) ++ ")"
| App(a, b) => print_lambda(a) ++ " " ++ print_lambda(b)
}
}
``````

``````// (\x. x x) (\x. x) = \x.x
// (\. 0 0) (\. 0) = \. 0
let test_1 = App(Fn(App(Var(0), Var(0))), Fn(Var(0)))
Js.Console.log(print_lambda(eval(test_1)))

// (\y. y (\x.x \x.x)) = (\y. y (\x.x))
// (\. 0 (\.0 \.0)) = (\. 0 (\.0))
let test_2 = Fn(App(Var(0), App(Fn(Var(0)), Fn(Var(0)))))
Js.Console.log(print_lambda(eval(test_2)))

// \y.(\x.x y) \y.(\x.x y) = \x.(x \y.(\x.x y))
// (\. \. 0 1) (\. \. 0 1) = \. 0 (\. \. 0 1)
let test_3 = App(
Fn(Fn(App(Var(0), Var(1)))),
Fn(Fn(App(Var(0), Var(1)))),
)
Js.Console.log(print_lambda(eval(test_3)))
``````

``````(\. 0)
(\. 0 (\. 0))
(\. 0 (\. (\. 0 1)))
``````
##### 任务2

``````// Homework: support let
module DebruLet = {
type rec lambda =
| Var (int)
| App (lambda, lambda)
| Fn (lambda)
| Let (lambda, lambda)  // let x=1 in x+y 等价于 (\x. x+y) 1

let rec eval_cheat = (t: lambda) => {
let trans = switch t {
| Var(i) => Debru.Var(i)
| App(a, b) => Debru.App(eval_cheat(a), eval_cheat(b))
| Fn(a) => Debru.Fn(eval_cheat(a))
| Let(a, b) => Debru.App(Debru.Fn(eval_cheat(b)), eval_cheat(a))
}
Debru.eval(trans)
}
}
``````

``````  // let x=(\x. x) in (x x) = \x.x
let test_1 = Let(Fn(Var(0)), App(Var(0), Var(0)))
Js.Console.log(Debru.print_lambda(eval_cheat(test_1)))
``````

``````(\. 0)
``````

``````let rec eval = (t:lambda) => {
switch t {
...
| Let(a,b) => eval(subst(eval(a), 0, eval(b)))
}
}
``````
##### 作业完成
...全文
196 4 打赏 收藏 转发到动态 举报

4 条回复

yhgu2000 2022-11-20
• 打赏
• 举报

yhgu2000 2022-11-20
• 举报

@yhgu2000 按我的理解，λ演算的自然语义应该就是这种积极求值策略，所以问题就变成“自然语义求值顺序是否是图灵等价的？”
lxr2010 2022-11-21
• 举报

@yhgu2000 个人想法：图灵等价描述的是两种逻辑系统能否相互模拟。积极求值的方法实际上是将普通的lambda表达式转换成了另一种规范的形式，姑且称之为lambda'。通过观察积极求值的算法， 我们可以发现，像Y组合字和Omega组合子这样的形式在lambda'形式中是不允许存在的（求值的结果不允许出现App(Fn(...),...)的形式，即App类型的表达式第一项不能是函数）。因此，除非我们能够找到一种方法，在lambda'形式中找到能够模拟上述两个组合子行为的表达式，否则就不能认为转换前后的两种形式是图灵等价的。 我个人认为积极求值方法转换前后两种表达形式是不等价的,lambda'形式的表达能力比lambda表达式要弱。
yhgu2000 2022-11-26
• 举报

@lxr2010 今天问了老师之后，我感觉我这个解释方式（不是自然语义的求值顺序）是不等价的。我觉得问题出在对Fn的解释上：不应该递归地调用eval求值函数体，而应该调用另外一个保证停机的绑定函数，这个函数只进行简单的替换操作，例如，不会对App进行Beta归约。我预计这种情况下的Y组合子仍然会堆栈溢出，但等价的Z组合子应该就能用了。

231

rescript开发语言 个人社区 广东省·深圳市

• 近7日
• 近30日
• 至今