热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

lispxy轴不等比缩放_Lisp的动态作用域

王垠在一篇叫做Lisp已死,Lisp万岁!的文章里介绍了「上古时期」Lisp的动态作用域,并用EmacsLisp做了示范。我想这一定会让很
6b4865a084b65d997069b14923c0a0e4.png

王垠在一篇叫做 Lisp 已死,Lisp 万岁!的文章里介绍了「上古时期」Lisp的动态作用域,并用Emacs Lisp做了示范。我想这一定会让很多程序员感到新鲜(或困惑),因为目前被使用的大部分编程语言要么使用词法作用域,要么干脆不允许函数中有自由变量。

要了解自由变量,我们首先需要知道哪些变量是「不自由」的。在一个函数里面,我们使用一个变量之前,它应该是已经被绑定了的,可能是来自函数内的局部变量、函数的参数变量,以及全局变量。向C这样的语言,只允许存在局部变量、参数变量和全局变量。一些函数式语言允许你在任意位置生成函数,也就必须允许你在更多的位置绑定变量。

假设你已经跟着那篇文章在Emacs中体验过了动态作用域,现在,请试着在Common Lisp试一下类似的代码:

;;; 定义一个函数f,函数定义的时候外面一层存在一个变量x,这个变量在函数f中被使用了
(let ((x 1))(defun f (y)(* x y)));;; 正常调用,结果符合我们的直觉
(f 2) ; 2;;; style-warning: The variable x is defined but never used
(let ((x 2))(f 2)) ; 2 and style-warning

第三块代码报了一个style-warning: The variable x is defined but never used,这里的x没有被函数f使用。这在99.44%(这个数字是我瞎掰的)的情况下都是合理的:我们看到函数f,它接受一个参数y,我们不需要也不应该知道函数内部到底使用了哪些其它变量

但是,如果我们不甘心函数只是一个黑盒子,想要在调用一个函数前改变函数体内的某个变量的值呢?如果可以这样,那么一些特定的任务将会变得非常容易和自然。通过使用动态作用域的变量,IO重定向可以非常简单,自定义异常处理的handler也可以非常简单。

在Common Lisp中,默认使用词法作用域,动态作用域的变量叫做「特殊变量」,让我们对刚才的示例代码稍作修改:

(let ((x 1))(defun f (y)(declare (special x))(* x y)))

函数f里面的x被声明为「特殊变量」,即使用动态作用域。函数f定义的地方有一个同名的变量x,但是函数f却「不认它」,而是在被调用的时候动态查找x的值。

(f 2) ; error: x not defined(defvar x 2)
(f 2) ; 4

Common Lisp中「特殊变量」的价值就在于值在运行时才确定,这一任务最好交给函数,而不是变量。

Common Lisp的姐妹Scheme,在最新的标准R7RS中引入了类似的特性,但是没有采用「特殊变量」,而是使用普通的函数。因为函数的本质就是动态的。Scheme保持了语言的简单性,同时不破坏程序员大脑中的模型,只是给编译器作者增加了难度。因为函数调用的开销问题,所有用来实现这种动态特性的函数必须被内联优化。

在Scheme中,这种东西被叫做parameter,由make-parameter创建。make-parameter接受1个或2个参数。第一个参数是parameter的初始值,第二个(可选的)参数是一个函数,用来约束parameter的值。make-parameter返回一个函数,这个函数接受0个或1个参数。如果不带参数调用它则返回parameter的值,带一个参数调用,则将它设为parameter的值。

还可以在做某件事情的时候使用parameterize临时改变parameter的值,做完之后自动恢复原样。

make-parameter和parameterize可以被定义成这样:

;; Taken from:
;; https://cisco.github.io/ChezScheme/csug9.5/system.html#./system:h13(define make-parameter(case-lambda[(init guard)(let ([v (guard init)])(case-lambda[() v][(u) (set! v (guard u))]))][(init)(make-parameter init (lambda (x) x))]))(define-syntax parameterize(lambda (x)(syntax-case x ()[(_ () b1 b2 ...) #'(begin b1 b2 ...)][(_ ((x e) ...) b1 b2 ...)(with-syntax ([(p ...) (generate-temporaries #'(x ...))][(y ...) (generate-temporaries #'(x ...))])#'(let ([p x] ... [y e] ...)(let ([swap (lambda ()(let ([t (p)]) (p y) (set! y t))...)])(dynamic-wind swap (lambda () b1 b2 ...) swap))))])))

(实际的编译器中为了让parameter总是被内联优化,应该不是这样写的。)

一些被证明了是坏想法的东西,如果能找到一种可靠的方法让它们变成可控的,也许能让某些特定的任务变得非常简单和自然。更重要的是,很多东西都是可以被重新审视的,作为程序员,不应该「谈动态作用域色变」,「谈白盒色变」,「谈goto色变」 ;-)



推荐阅读
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 推荐系统遇上深度学习(十七)详解推荐系统中的常用评测指标
    原创:石晓文小小挖掘机2018-06-18笔者是一个痴迷于挖掘数据中的价值的学习人,希望在平日的工作学习中,挖掘数据的价值, ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • Postgresql备份和恢复的方法及命令行操作步骤
    本文介绍了使用Postgresql进行备份和恢复的方法及命令行操作步骤。通过使用pg_dump命令进行备份,pg_restore命令进行恢复,并设置-h localhost选项,可以完成数据的备份和恢复操作。此外,本文还提供了参考链接以获取更多详细信息。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 使用圣杯布局模式实现网站首页的内容布局
    本文介绍了使用圣杯布局模式实现网站首页的内容布局的方法,包括HTML部分代码和实例。同时还提供了公司新闻、最新产品、关于我们、联系我们等页面的布局示例。商品展示区包括了车里子和农家生态土鸡蛋等产品的价格信息。 ... [详细]
  • 本文介绍了Java集合库的使用方法,包括如何方便地重复使用集合以及下溯造型的应用。通过使用集合库,可以方便地取用各种集合,并将其插入到自己的程序中。为了使集合能够重复使用,Java提供了一种通用类型,即Object类型。通过添加指向集合的对象句柄,可以实现对集合的重复使用。然而,由于集合只能容纳Object类型,当向集合中添加对象句柄时,会丢失其身份或标识信息。为了恢复其本来面貌,可以使用下溯造型。本文还介绍了Java 1.2集合库的特点和优势。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • MySQL多表数据库操作方法及子查询详解
    本文详细介绍了MySQL数据库的多表操作方法,包括增删改和单表查询,同时还解释了子查询的概念和用法。文章通过示例和步骤说明了如何进行数据的插入、删除和更新操作,以及如何执行单表查询和使用聚合函数进行统计。对于需要对MySQL数据库进行操作的读者来说,本文是一个非常实用的参考资料。 ... [详细]
author-avatar
一介山夫1986_878
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有