词法作用域 vs 动态作用域
词法作用域 vs 动态作用域
链接:https://www.jianshu.com/p/cdebb5965000
scheme是一门采用词法作用域(lexical scoping)的lisp方言,这个设计是从alogol语言里借鉴过来的。现在,词法作用域已经被许多lisp方言所吸收,实践表明,这的确是一项正确的设计,避免了很多奇怪的错误,比较符合人类的思维习惯。
但是,在某些场合下,动态作用域又是很有用的特性,比如emacs lisp里面就默认采用动态作用域。
下面的程序演示了词法作用域与动态作用域的不同
(define x 1)
(define y (lambda () x))
(let ([x 2]) (y))
如果是词法作用域,返回1,如果是动态作用域,返回2。
在scheme中模拟动态作用域
从一个简单的例子出发,演示如何在scheme里面实现动态作用域,我们想要写一个try catch宏来处理程序中的异常。其中一个函数叫做current-exception-handler,我们希望它是动态的,随代码运行位置而变化,永远指向当前的异常处理器,下面是我们写的第一个版本
(define current-exception-handler
(lambda (msg) (error "No Top Level Try")))
(define-syntax try
(syntax-rules (catch)
[(_ expr ... (catch msg expr* ...))
(call/cc (lambda (k)
(let ([msg (call/cc (lambda (k1)
(set! current-exception-handler k1)
(let ([result (begin expr ...)])
(k result)
)))])
expr* ...)))]))
(define (throw msg) (current-exception-handler msg))
其中,涉及throw的代码必须被包含在try里面,否则会导致错误。先来测试一下
(try 1
(throw 'foo)
(catch msg (display "catch ") (display msg)))
但是如果我们在后面再加上一行代码
(throw 'test)
这里就出现问题了,按照我们上面的要求throw应该在try catch块里面使用,而这里却不会报错,说明我们上面的代码错了。
进入try catch块时,我们把current-exception-handler设置为当前try catch块的exception-handler,但当运行出try catch块的时候,exception-handler并没有发生变化,我们希望恢复原来的exception-handler,使得不论是正常退出或者是发生错误退出都能恢复原有的exception-handler,所以修改一下代码,就是:
(define current-exception-handler
(lambda (msg) (error "No Top Level Try")))
(define-syntax try
(syntax-rules (catch)
[(_ expr ... (catch msg expr* ...))
(call/cc (lambda (k)
(let ([msg (call/cc (let ([preserved current-exception-handler])
(lambda (k1)
(set! current-exception-handler (lambda (msg)
(set! current-exception-handler preserved)
(k1 msg)))
(let ([result (begin expr ...)])
(set! current-exception-handler preserved)
(k result)
))))])
expr* ...)))]))
(define (throw msg) (current-exception-handler msg))
这样,只要代码出了try catch块,current-exception-handler就会恢复成原来的,从而实现了动态作用域的效果。
fluid-let语句
如果你记得fluid-let语句的话,你就会发现上面的代码效果和fluid-let语句很类似,没错,fluid-let语句就是被设计用来实现dynamic scoping效果的,fluid-let语句的定义如下(摘自chez scheme user guide 8):
(define-syntax fluid-let
(lambda (x)
(syntax-case x ()
[(_ () b1 b2 ...) #'(let () b1 b2 ...)]
[(_ ((x e) ...) b1 b2 ...)
(andmap identifier? #'(x ...))
(with-syntax ([(y ...) (generate-temporaries #'(x ...))])
#'(let ([y e] ...)
(let ([swap (lambda ()
(let ([t x]) (set! x y) (set! y t))
...)])
(dynamic-wind swap (lambda () b1 b2 ...) swap))))])))
具体的实现细节就不说了,给一个例子:
(define x 1)
(define y (lambda () x))
(fluid-let ([x 2]) (y))
这和第一段代码一模一样,只不过用了fluid-let语句,就在scheme里面模拟出了dynamic scoping的效果。
这样,try catch宏就可以写的很简单了:
(define-syntax try
(syntax-rules (catch)
[(_ expr ... (catch msg expr* ...))
(call/cc (lambda (k)
(let ([msg (call/cc (lambda (k1)
(fluid-let ([current-exception-handler k1])
(let ([result (begin expr ...)])
(k result)
))))])
expr* ...)))]))
parameterize语句-更好的选择
但是出于某些原因(效率,赋值,标准库里面没有提供。。),我们并不想用fluid-let语句来解决问题,恰好rnrs标准库里面就提供了一个类似的,parameterize语句,同样可以完成任务。
(define x (make-parameter 1))
(define y (lambda () (x)))
(parameterize ([x 2]) (y))
如果我们在parameterize块外面尝试调用y函数,就会得到1,这说明parameterize只对块内生效,一旦出了parameterize块(不论是通过何种方式),就会恢复成原来的样子。
scheme与其他lisp方言在作用域方面还有一个不同点,它的宏也是采用了词法作用域,而其他lisp方言的宏几乎都是动态作用域。如果你想了解scheme的宏如何实现动态作用域的效果,请看这里:http://schemeworkshop.org/2011/slides/Barzilay2011.pdf
=============== End
词法作用域 vs 动态作用域的更多相关文章
- 深入理解javascript作用域系列第二篇——词法作用域和动态作用域
× 目录 [1]词法 [2]动态 前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极 ...
- js之词法作用域与动态作用域
事实上JavaScript并不具有动态作用域,它只有词法作用域,简单明了,但是this机制某种程度上很像动态作用域 词法作用域:是一套引擎如何寻找变量以及会在何处找到变量的规则,它是定义在词法阶段的作 ...
- JavaScript深入之词法作用域和动态作用域
作用域 作用域是指程序源代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript 采用词法作用域,也就是静态作用域. 静态作用域与动态作用域 因 ...
- Javascript中的词法作用域、动态作用域、函数作用域和块作用域(四)
一.js中的词法作用域和动态作用域 词法作用域也就是在词法阶段定义的作用域,也就是说词法作用域在代码书写时就已经确定了. js中其实只有词法作用域,并没有动态作用域,this的执 ...
- 深入学习js之——词法作用域和动态作用域
开篇 当我们在开始学习任何一门语言的时候,都会接触到变量的概念,变量的出现其实是为了解决一个问题,为的是存储某些值,进而,存储某些值的目的是为了在之后对这个值进行访问或者修改,正是这种存储和访问变量的 ...
- 【转】深入理解javascript作用域——词法作用域和动态作用域
前面的话 大多数时候,我们对作用域产生混乱的主要原因是分不清楚应该按照函数位置的嵌套顺序,还是按照函数的调用顺序进行变量查找.再加上this机制的干扰,使得变量查找极易出错.这实际上是由两种作用域工作 ...
- 前端知识体系:JavaScript基础-作用域和闭包-词法作用域和动态作用域
词法作用域和动态作用域 1.作用域: 作用域是指程序代码中定义变量的区域 JavaScript采用词法作用域,也就是静态作用域 2.词法作用域和动态作用域 因为JavaScript采用的是词法作用域, ...
- JavaScript深入之词法作用域和动态作用域(转载)
作用域 作用域是指程序源代码中定义变量的区域. 作用域规定了如何查找变量,也就是确定当前执行代码对变量的访问权限. JavaScript 采用词法作用域(lexical scoping),也就是静态作 ...
- JavaScript 词法、静态、动态作用域初级理解
开始之前 由于本人也是JavaScript初学者,记录学习经过,怕以后会忘记. 对于JavaScript 初学者来说,最难的不是代码部分,而是对很多书籍中的术语的理解,大多时候想要理解一段JavaSc ...
随机推荐
- 三,ESP8266 SPI(基于Lua脚本语言)
https://www.cnblogs.com/yangfengwu/p/7520260.html 重点是说SPI通信协议,,,, 不要害怕协议因为协议是人规定的,,刚好我也是人......规定的协议 ...
- 创世纪 BZOJ3037 & [Poi2004]SZP BZOJ2068
分析: 树形DP中的一种,基环树DP 针对每一个环跑DP,f[i],g[i]分别表示选或者不选,之后我们注意每次遍历的时候,不要重复遍历. 附上代码: #include <cstdio> ...
- iOS Swift WisdomScanKit图片浏览器功能SDK
iOS Swift WisdomScanKit图片浏览器功能SDK使用 一:简介 WisdomScanKit 由 Swift4.2版编写,完全兼容OC项目调用. WisdomScanKit的 ...
- 20155323刘威良 网络对抗《网络攻防》 Exp1 PC平台逆向破解(5)M
实践目标 本次实践的对象是linux的可执行文件 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时包含另一个代码片段,getShell,会返回一个可 ...
- 20155334 曹翔 Exp3 免杀原理与实践
20155334 曹翔 Exp3 免杀原理与实践 小记:这次实验,困难重重,失败练练,搞得我们是心急如焚,焦头烂额,哭爹喊娘 一.基础问题回答 杀软是如何检测出恶意代码的? 每个杀软都有自己的检测库, ...
- WPF编程,通过Double Animation动态缩放控件的一种方法。
原文:WPF编程,通过Double Animation动态缩放控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...
- POJ 3349&&3274&&2151&&1840&&2002&&2503
(今天兴致大发学了Markdown,第一篇博客) 这次的主要都是hash的题目(当然这就意味这可以用map) hash的方式也有很多: 普通hash hash挂链 双hash以及自然溢出等 当然我还是 ...
- 升级webpack2
更新:webpack3已经出来了,官方说从2到升级到3不用改一行配置,98%的人没有错误. 项目中用的是webpack1.webpack2已经出来一段时间了.决定升级.其实改动不是很大.修改加测试共花 ...
- 自定义CCNode
对Touch事件的获取与处理可以使用CCLayer, CCMenuItem等,但是如果我们需要一个虚拟按键或者需要对特定精灵进行拖动等等,我们就需要自定义Touch类. 自定义Touch事件处理类重要 ...
- 从浏览器输入URL到显示页面到底发生了什么?
首先说明一下,当系统本地缓存了你所请求的资源时,会直接把缓存内容解析并显示,而不会进行以下的一系列行为. 一.DNS域名解析 至今的计算机数量可谓是数不胜数,而它们的唯一识别身份就是ip地址.我们常说 ...