[基础软件理论与实践] 第三节作业实现 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

补全 src/Name.resDebru.eval 函数:

  // 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))  // 尽最大努力地求值
      }
    }
  }

更新:图灵等价的完备求值实现,差别在于对Fn(f)的处理上,定制的bind函数可以保证停机,从而避免过早陷入死循环

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

作业2的实现我采用了偷懒的做法:由于 Let 等价于一个函数抽象操作加一个函数应用操作(例如let x=1 in x+y 等价于 (\x. x+y) 1),所以可以将作业2的AST转化为作业1的AST然后用作业1的函数进行计算。

补充 src/Name.resDebruLet.eval 函数定义如下:

// 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)

如果要是不偷这个懒要怎么做呢?那就得把 Debru 模块里的 shift 函数 subst 函数再复制粘贴一遍,添加关于 Let 的 switch 情况(只有一点点代码),然后 eval 函数中的 swtich 的 Let 分支就写成:

let rec eval = (t:lambda) => {
  switch t {
  ...
  | Let(a,b) => eval(subst(eval(a), 0, eval(b)))
  }
}
作业完成
...全文
336 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
yhgu2000 2022-11-20
  • 打赏
  • 举报
回复

这种过分积极的求值会导致一个问题,如果函数体里包含死循环,那么求值时就会提前停机,这会导致Y组合子无法使用。

例如:(\f. (\.x f x x) (\.x f x x)) (\x y. (\z. z)) 的求值结果显然为 \z. z,但是使用上面的尽最大努力求值,就会在Y组合子处死循环导致无法停机。

那么问题就是:这种积极求值的方法是不是图灵等价的呢?

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

231

社区成员

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

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