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

lxr2010 2022-11-21 11:29:08

作业信息

  1. Complete the de Bruijn index based interpreter in natural semantics

  2. Apply the de Bruijn index for extended lambda calculus (+ Let)

作业实现

任务1

实现德布朗指数在自然语义下的解释器。

可能是翻译问题,德布朗指数(de Bruijn index)的index理解成索引可能更自然一些。参考

实现解释器,需要对Lambda表达式进行分类讨论:

  • 对于Var(i),仍然解释为Var(i);

  • 对于Fn(b),递归地解释b;

  • 对于App(f, arg),首先解释f,如果得到一个函数,那么再解释arg,并将f应用到arg上。在应用β规约的时候,应当注意farg中自由变量的存在情况:

    假设f具有λ.M的形式,arg具有N的形式:

    • MN中都不存在自由变量:直接使用N代换M中的index 0。即M[N/0]
    • M中存在自由变量yN中不存在自由变量:注意在对f应用β规约之后,对于y而言,其外部的λ层数减少了1,所以需要对代换后的结果中自由变量的索引 shift -1。即shift(-1,M[N/0])
    • N中存在自由变量:在对f应用β规约时,需要强调操作的顺序。需要先将N放到M中进行代换,再将λ层数减少1。如果调换顺序,那么在将λ层数减少1后,M中的自由变量索引 shift -1,可能和M中最外层的受控变量索引混淆,导致无法将N正确的代换到shift(-1,M)中。在保证上述顺序的情况下,NM中进行代换时,其自由变量外部的λ层数增加了1,在代换结束后,代换结果中全部自由变量外部的λ层数都减少了1。所以有shift(-1,M[shift(1,N)/0])

    对于上述几种情况,综合来看,由于shift操作对受控变量无影响,上述几种情况可以总结为shift(-1,M[shift(1,N)/0])

    应用β规约后,递归地解释规约的结果。

  • 对于App(f, arg),如果解释f得到的不是函数,那么保留解释f的结果,递归地解释arg

代码如下:

module Debru = {
  type rec lambda = 
  | Var(int)
  | Fn(lambda) 
  | App(lambda, lambda)

  let rec toString = (t: lambda): string => switch t {
  | Var(i) => Js.Int.toString(i)
  | Fn(b) => "(λ." ++ toString(b) ++")"
  | App(m, n) => "(" ++ toString(m) ++" " ++ toString(n) ++ ")"
  }

  let print_lambda = l => {
    let print_paren = (b, s) => {
      if b { "(" ++ s ++ ")" } else { s }
    }
    let rec go = (l, p) => {
      switch l {
        | Var(x) => Js.Int.toString(x)
        | Fn(a) => print_paren(p>0, "fun -> " ++ go(a, 0))
        | App(a, b) => print_paren(p>1, go(a, 1) ++ " " ++ go(b, 2))

      }
    }
    go(l, 0)
  }


  // Var(j) becomes Var(i+j) if j >= d
  let rec shift_aux = (i, d, u) => {
    switch u {
    | Var(j) => { if j >= d { Var(i+j) } else { u } }
    | Fn(b) => Fn(shift_aux(i, d+1, b))
    | App(a, b) => 
        App(shift_aux(i, d, a), shift_aux(i, d, b))
    }
  }
  let shift = (i, u) => shift_aux(i, 0, u)

  // t[u/i]: use u to replace Var(i) in term t
  let rec subst = (t, i, u) => {
    switch t {
    | Var(j) => if j == i { u } else { t }
    | Fn(b) => Fn(subst(b, i+1, shift(1, u)))
    | App(a, b) => App(subst(a, i, u), subst(b, i, u))
    }
  }
  
  // Homework: implement the complete interpreter
  let eval = (t: lambda) => {
    let rec evalHelper = (t:lambda) => {
      switch t {
      | Var(_) => t
      | Fn(b) => Fn(evalHelper(b))
      | App(f, arg) => {
        switch evalHelper(f) {
        | Fn(b) => {
          let va = evalHelper(arg)
          let va_shifted = shift(1,va)
          let b_substed = subst(b, 0, va_shifted)
          let b_substed_deshifted = shift(-1, b_substed)
          evalHelper(b_substed_deshifted)
        }
        | k => App(k, evalHelper(arg))
        }
      }
      }
    }
    evalHelper(t)
  }
}

下面是测试代码:


module DebruTest = {
  open! Debru

  let id = Fn(Var(0))
  let trueB = Fn(Fn(Var(1)))
  let falseB = Fn(Fn(Var(0)))
  let zero = Fn(Fn(Var(0)))
  let one = Fn(Fn(App(Var(1),Var(0))))
  let succ = Fn(Fn(Fn(App(Var(1),App(App(Var(2),Var(1)),Var(0))))))
  let pair = Fn(Fn(Fn(App(App(Var(0),Var(2)),Var(1)))))
  let fst = Fn(App(Var(0),trueB))
  let snd = Fn(App(Var(0),falseB))
  let pred = 
  Fn(App(
    fst,App(
      App(
        Var(0),
        Fn(App(
          App(pair,App(snd,Var(0))),
          App(succ,App(snd,Var(0)))))),
      App(App(pair,zero),zero))))
  let toChurchNumB = (n : int) => {
    let rec helper = (n: int, churchNumB: lambda) : lambda => 
      if n < 0 {assert false}
      else {
        switch n {
        | 0 => churchNumB 
        | _ => helper(n-1, App(succ, churchNumB))
        }
      }
    helper(n, zero)
  }

  let test = () => {
    Js.log(print_lambda(eval(App(pred,toChurchNumB(8)))))
  }
}

let _ = DebruTest.test()

测试输出:

fun -> fun -> 1 (1 (1 (1 (1 (1 (1 0))))))
任务2

实现扩展Lambda表达式(+Let)的de Bruijn index形式的解释器。

注意到eval Let(l1, l2) == eval App(Fn(l2),l1)

在实现时将所有Let(l1, l2)替换成App(Fn(l2),l1)即可。

// Homework: support let
module DebruLet = {
  type rec lambda = 
  | Var (int)
  | App (lambda, lambda)
  | Fn (lambda)
  | Let (lambda, lambda)

  let rec toString = (t: lambda): string => switch t {
  | Var(i) => Js.Int.toString(i)
  | Fn(b) => "(λ." ++ toString(b) ++")"
  | App(m, n) => "(" ++ toString(m) ++" " ++ toString(n) ++ ")"
  | Let(l, b) => "(" ++ toString(l) ++ "->" ++ toString(b) ++ ")"
  }

  let print_lambda = l => {
    let print_paren = (b, s) => {
      if b { "(" ++ s ++ ")" } else { s }
    }
    let rec go = (l, p) => {
      switch l {
        | Var(x) => Js.Int.toString(x)
        | Fn(a) => print_paren(p>0, "fun -> " ++ go(a, 0))
        | App(a, b) => print_paren(p>1, go(a, 1) ++ " " ++ go(b, 2))
        | Let(l, b) => print_paren(p>1, "let " ++ go(l,1) ++ " in " ++ go(b, 2))
      }
    }
    go(l, 0)
  }


  // Var(j) becomes Var(i+j) if j >= d
  let rec shift_aux = (i, d, u) => {
    switch u {
    | Var(j) => { if j >= d { Var(i+j) } else { u } }
    | Fn(b) => Fn(shift_aux(i, d+1, b))
    | App(a, b) => 
        App(shift_aux(i, d, a), shift_aux(i, d, b))
    | Let(l, b) => shift_aux(i,d,App(Fn(b),l))
    }
  }
  let shift = (i, u) => shift_aux(i, 0, u)

  // t[u/i]: use u to replace Var(i) in term t
  let rec subst = (t, i, u) => {
    switch t {
    | Var(j) => if j == i { u } else { t }
    | Fn(b) => Fn(subst(b, i+1, shift(1, u)))
    | App(a, b) => App(subst(a, i, u), subst(b, i, u))
    | Let(l, b) => subst(App(Fn(b),l),i,u)
    }
  }

// eval Let(l1, l2) == eval App((lambda.l2),l1)
  let eval = (t: lambda) => {
    let rec evalHelper = (t:lambda) => {
      switch t {
      | Var(_) => t
      | Fn(b) => Fn(evalHelper(b))
      | Let(loc, body) => evalHelper(App(Fn(body),loc))
      | App(f, arg) => {
        switch evalHelper(f) {
        | Fn(b) => {
          let va = evalHelper(arg)
          let va_shifted = shift(1,va)
          let b_substed = subst(b, 0, va_shifted)
          let b_substed_deshifted = shift(-1, b_substed)
          evalHelper(b_substed_deshifted)
        }
        | k => App(k, evalHelper(arg))
        }
      }
      }
    }
    evalHelper(t)
  }
}

下面是测试代码:

module DebruLetTest = {
  open! DebruLet


  let id = Fn(Var(0))
  let trueB = Fn(Fn(Var(1)))
  let falseB = Fn(Fn(Var(0)))
  let zero = Fn(Fn(Var(0)))
  let one = Fn(Fn(App(Var(1),Var(0))))
  let succ = Fn(Fn(Fn(App(Var(1),App(App(Var(2),Var(1)),Var(0))))))
  let pair = Fn(Fn(Fn(App(App(Var(0),Var(2)),Var(1)))))
  let fst = Fn(App(Var(0),trueB))
  let snd = Fn(App(Var(0),falseB))
  let pred = Let(
                Fn(App(
                  App(pair,App(snd,Var(0))),
                  App(succ,App(snd,Var(0))))),
                Let(
                  App(App(pair,zero),zero),
                  Fn(App(fst,App(App(Var(0),Var(2)),Var(1))))
          ))
  let toChurchNumB = (n : int) => {
    let rec helper = (n: int, churchNumB: lambda) : lambda => 
      if n < 0 {assert false}
      else {
        switch n {
        | 0 => churchNumB 
        | _ => helper(n-1, App(succ, churchNumB))
        }
      }
    helper(n, zero)
  }

  let test = () => {
    Js.log(print_lambda(eval(Let(pred,App(Var(0),toChurchNumB(8))))))
  }
}

let _ = DebruLetTest.test()

测试结果:

fun -> fun -> 1 (1 (1 (1 (1 (1 (1 0))))))
...全文
477 1 打赏 收藏 转发到动态 举报
写回复
用AI写文章
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
SoftwareTeacher 2022-11-22
精选
  • 打赏
  • 举报
回复
10.00元

赞持续学习

230

社区成员

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

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