[基础软件理论与实践] 第三节作业实现 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.res 中 Debru.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.res 中 DebruLet.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)))
  }
}
作业完成
...全文
806 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组合子应该就能用了。
附训练好的 YOLO 权重与 PyQt 可视化检测界面,页面底部含可视化效果预览,毕设/课设开箱即用。 【数据集概况】 · 检测类别(中文):[领航车(leader)] · 训练集:414 张 · 验证集:39 张 · 测试集:20 张 · 总计:473 张 该数据集聚焦于室内走廊环境中对领航车的精准识别与定位,具备高度场景专一性与实际应用价值。图像采集于光线均匀、结构规整的建筑内部通道,背景干扰因素少,有利于模型在复杂环境下的目标提取能力训练。领航车作为核心检测对象,在不同距离和角度下均被清晰标注,为智能导航系统、自动化引导设备等提供可靠的数据支持。... 【训练曲线与评估图】 【模型训练配置】 参数 | 值 模型 | yolo26n 训练轮数 | 100 epochs 输入尺寸 | 640x640 批次大小 | 24 优化器 | auto 初始学习率 | 0.01 训练设备 【关键指标汇总】 训练了 77 个 epoch,最终轮指标: 指标 | 数值 mAP50 | **0.9950** mAP50-95 | 0.9141 Precision | 0.9985 Recall | 1.0000 train/box_loss | 0.5224 train/cls_loss | 0.2526 val/box_loss | 0.4307 val/cls_loss | 0.1407 【训练过程分析】 77 轮训练后 mAP50 达到 0.9950,模型收敛良好。Loss 曲线前段快速下降,后段趋于平稳,val_loss 无反弹,没有明显过拟合。mAP50-95 为 0.9141,和 mAP50 差距仅 0.08,框的定位精度也很扎实。 【模型性能评估】 Precision 0.9985、Recall 1.000... 使用场景:毕业设计、课程设计、深度学习入门、工业场景落地验证。
内容概要:本文围绕基于序贯蒙特卡洛模拟法的配电网可靠性评估展开深入研究,提出并实现了结合Matlab的仿真方法,用于精确评估配电网在多种故障场景下的供电可靠性。研究充分考虑了元件故障、时序恢复过程及系统运行的随机性与动态性,采用序贯抽样技术模拟系统状态转移过程,实现了对停电频率、停电时间、供电可用率等关键可靠性指标的量化分析。文中系统阐述了算法的设计流程、事件抽样机制、状态序列生成逻辑以及系统恢复策略,并通过典型算例验证了该方法在复杂配电网络中的有效性与实用性,为配电网的规划、运行与优化提供了科学的评估工具。; 适合人群:具备电力系统分析基础和Matlab编程能力的高校研究生、科研人员,以及从事配电网规划设计、运行维护与可靠性管理的工程技术人员。; 使用场景及目标:①掌握序贯蒙特卡洛模拟法在电力系统可靠性评估中的核心原理与实现方法;②学习利用Matlab构建配电网时序仿真模型,进行可靠性指标计算与分析;③为配电网的设备选型、网络结构优化、故障应对策略制定及韧性提升提供数据支撑与决策依据; 阅读建议:建议读者结合文中提供的Matlab代码实现步骤,动手复现实验过程,深入理解状态抽样、时序序列模拟、故障处理逻辑与指标统计等关键环节,并可进一步将该方法拓展应用于含分布式电源、储能系统等新型元件的现代复杂配电网可靠性评估研究。

231

社区成员

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

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