长长的望远镜

news/2024/10/24 2:03:21/

按语:我在送孩子去幼儿园的路上为「不懂编程的人」写了这一系列文章的第九篇,整理于此。它的前一篇是《无名》,讲述了如何一步一步「推演」出 Y 组合子。

我有个长长的望远镜,能一直伸到你的家里面,你说什么做什么,我都能看到。

怎样用 Emacs Lisp 语言描述这样的事?

(defun bar () x)
(defun foo (x) (bar))
(foo '三原色)

(foo '三原色) 进行求值,会在 Emacs 微缓冲区显现 三原色

foo 函数会对 bar 函数进行求值。bar 函数不接受任何参数的函数,但是它的内部却凭空出现了一个变量 x。令 bar 毛骨悚然的是,当 foo 对它求值时,这个 x 竟然有意义的,它的值是符号原子 三原色

foo 的内部,bar 函数觉得自己见了鬼。

这其实是 Emacs Lisp 的动态域(Dynamic domain)在搞鬼。Emacs Lisp 解释器对 (foo '三原色) 求值,得到表达式 (bar),然后它继续对 (bar) 求值,得到表达式 x,最后它继续对 x 求值,结果发现这个 x 是个长长的望远镜,从 foo 的窗户一直伸到了 bar 的家里,这个望远镜的品牌叫 三原色。因此,我们就在微缓冲区看到了匪夷所思的结果。

当 Emacs Lisp 对一个函数表达式求值时,遇到自由变量时,它就会到一个全局的环境中搜索这个自由变量的值,将这个值作为自由变量的求值结果,倘若找不到,就会报错,说变量无效。

动态域的这种特性,对于需要长长的望远镜的机构很有用。不过,对于 bar 函数而言,既然 foo 能把长长的望远镜伸过来,就不要放过它:

(defun bar () (setq x '黑暗))
(defun foo (x) (progn (bar) x))
(foo '三原色)

再次对 (foo '三原色) 进行求值,这次结果为 黑暗。因为 bar 抓住了这个伸到自己家里的望远镜,把它的镜头涂黑了。

动态域,是很古老的变量作用域模型。现代的变量作用域叫词法域,也叫静态域。

在词法域里,每个函数都有自己的环境。当函数中出现自由变量时,它就在自己的环境里搜索变量的值,搜到了就作为自由变量的求值结果,否则就报错——变量无效。

词法域的好处是,没有人能够将长长的望远镜伸到你家里。看下面的例子:

(setq lexical-binding t)
(defun bar () x)
(defun foo (x) (bar))(foo '三原色)

再对 (foo '三原色) 求值,就会在微缓冲区报错,说变量 x 无效。这是因为,在 bar 的环境中,x 是未定义的自由变量,所以无效。虽然 foo 中的 x 是有定义的,但它仅仅是与 bar 中的 x 同名而已,它们是两个不同的变量。

下面的代码是一个匿名函数的求值表达式:

(funcall (funcall (lambda (thing)(lambda (n)(if (= n 0)0(+ n (funcall (funcall thing thing) (- n 1))))))(lambda (thing)(lambda (n)(if (= n 0)0(+ n (funcall (funcall thing thing) (- n 1))))))) 100)

在动态域里,这个函数表达式无法求值,因为当 Emacs Lisp 解释器在对这个表达式进行求值时,最终抵达 (funcall thing thing) 的时候,全局环境里面已经没有了 thing 的定义。因为,当一个函数的求值结果是匿名函数时,在这个匿名函数被求值时,全局环境已经不再是它还在母体时的那个样子了。

例如,Emacs Lisp 解释器对

(funcall (lambda (thing)(lambda (n)(if (= n 0)0(+ n (funcall (funcall thing thing) (- n 1))))))(lambda (thing)(lambda (n)(if (= n 0)0(+ n (funcall (funcall thing thing) (- n 1)))))))

的求值结果是

(lambda (n)(if (= n 0)0(+ n (funcall (funcall thing thing) (- n 1))))))

这时,这个匿名函数里的 thing,在这个匿名函数的母体中是有定义的,它就是作为参数传入的那个匿名函数,但是 Emacs Lisp 对母体求值结束后,thing 的定义也就同时在全局环境中消失了,因此对于这个刚刚从母体中脱胎而出的匿名函数,thing 变成了一个未定义的自由变量,从而导致 Emacs Lisp 解释器报错。

简而言之,在动态域中,你没有办法将匿名函数作为参数传给自身。因此,在动态域中,你看不到世界的本原,这样的世界是一个不确定的世界。这可能就是为什么早期的 Lisp 机器在运行时经常出故障的主要原因。

在词法域里不会有这样的问题,因为每个函数都有自己的环境,并且一个匿名函数从母体脱胎而出的时候,它会对母体的环境有所继承。这种结构称为闭包。

在使用 Emacs Lisp 编程时,用动态域还是词法域呢?倘若你没有长长的望远镜,或者你对这种望远镜深恶痛绝,就用词法域吧,在程序的开头添加:

(setq lexical-binding t)

;;; -*- lexical-binding: t -*-

最后,略微介绍一下 setq。之前,我们只见识过通过函数参数传递的变量。setq 可以将一个符号与一个值或一个匿名函数绑定起来。上面已经见识了它将 lexical-bindingt 绑定了起来。下面的例子展示了如何将符号与匿名函数绑定起来:

(setq Y(lambda (F)(funcall (lambda (thing)(funcall F(lambda (m) (funcall (funcall thing thing) m))))(lambda (thing)(funcall F(lambda (m) (funcall (funcall thing thing) m)))))))
(setq F(lambda (thing*)(lambda (n)(if (= n 0)0(+ n (funcall thing* (- n 1)))))))

这样绑定之后,用 Y 组合子构造匿名的递归函数会更加简洁:

(funcall (funcall Y F) 100)

要试验上述代码,记得开启词法域模式。

下一篇:从混乱到有序


http://www.ppmy.cn/news/211434.html

相关文章

星特朗望远镜怎么样_我们,来看一看月亮?——星特朗SCTW-70天文望远镜测评...

我们,来看一看月亮?——星特朗SCTW-70天文望远镜测评 2019-11-20 09:40:00 0点赞 3收藏 11评论 先放总结: 总结来说,本款产品拥有高颜值的外观,削减了大部分非必要配置,降低了一定成本,简便了操…

全球与中国儿童双筒望远镜市场深度研究分析报告

【报告篇幅】:90 【报告图表数】:140 【报告出版时间】:2022年3月 报告摘要 2021年全球儿童双筒望远镜市场销售额达到了 亿美元,预计2028年将达到 亿美元,年复合增长率(CAGR)为 %&#xff0…

leetcode 55.跳跃游戏

题目描述跳转至leetcode 给定一个非负整数数组 nums ,你最初位于数组的 第一个下标 。 数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标。 来源:力扣(LeetCode) 链接:https://lee…

ESP-BOX官方例程实践

1.下载esp-box项目代码 github仓库:https://github.com/espressif/esp-box gitee仓库:https://gitee.com/EspressifSystems/esp-box 使用git工具和如下命令进行下载: git clone --recursive https://github.com/espressif/esp-box.git or gi…

操作系统复习2.3.5-管程

引入管程 PV操作困难,容易书写出错,引入管程,作为一种高级同步机制 组成 局限于管程的共享数据结构说明对该数据结构进行操作的一组过程对局部于管程的共享数据结构设置初始值的语句管程有一个名字 基本特征 局限于管程的数据只能被局限…

关于性能测试平台的一些想法,想跟大家聊一下

目录 一、任务管理 二、用例管理 三、环境管理 四、压测机管理 五、数据管理 六、监控管理 七、日志管理 八、报表管理 九、配置管理 十、系统管理 组织架构 这里我按照每个不同系统归属的项目组为横向,性能测试团队作为职能部门为纵向的矩阵式组织架构为…

系统应用篇(一)--Settings篇

目录 一、Settings应用的作用和重要性 二、Settings应用的架构和组件 三、Settings应用的定制和扩展 四、总结 一、Settings应用的作用和重要性 Settings应用的作用和重要性如以下表格所示: 作用重要性提供系统设置选项Settings应用是用户在Android设备上访问…

电动车头盔检测系统(毕设)

有没有做这个毕设的伙伴,可以一起交流下(交流群:718700435) 电动车头盔检测系统 技术选型: yolov5pyqtmysql1: 系统:前台检测系统 1.1信息采集 字段 录入 展示 修改 删除1.2实时检测报警 实时画面 违章信息1.3视频留存管理模块 视频保存 视频列表 时间查询2: 后台管理员…