《老练的Scheme程序员》中try函数定义的语法疑问
关于《The Seasoned Schemer》中
try宏的语法细节疑问 在Felleisen与Friedman所著《The Seasoned Schemer》一书中,引入了try宏。它和辅助宏letcc的定义如下:
(define-syntax letcc (syntax-rules () ((letcc var body ...) (call-with-current-continuation (lambda (var) body ... ))))) (define-syntax try (syntax-rules () ((try var a . b) (letcc success (letcc var (success a)) . b))))
我理解try的功能与用法,但对其形式定义存在困惑:在try定义的(letcc success (letcc var (success a)) . b)中,letcc应用于success时的点,以及(letcc var (success a)) . b中的点具体含义是什么?或者换个问法:try定义中哪一部分规定了若在a中调用var,则try的求值结果为b?
先拆解语法里的“点”含义
首先要明确,这里的点(.)是Scheme宏语法里的“点对”语法,用来处理可变长度的参数或者表达式序列:
- 在
(try var a . b)里,. b表示b是一个表达式序列(可以是一个或多个表达式),比如你测试的(try var (+ 1 1) 4)里,b就是(4);如果是(try var (var 'ok) 5 6 7),b就是(5 6 7)。 - 而展开式里的
(letcc success (letcc var (success a)) . b),这里的. b是把b里的所有表达式直接拼接在letcc success的主体部分最后。换句话说,这个展开式等价于:
其中(letcc success (letcc var (success a)) b1 b2 ...)b1 b2 ...就是你写在try调用里a后面的所有表达式。
为什么调用var会让结果变成b的求值结果?
我们一步步看try的执行逻辑:
- 首先执行
(letcc success ...):这会捕获当前的延续(continuation)绑定到success。这个延续代表的是“try表达式执行完成后,后续要做的事情”。 - 接着执行
(letcc var (success a)):- 这里又捕获了一个延续绑定到
var,这个延续代表的是“从letcc var这里退出后,后续要执行的代码”——也就是b里的所有表达式! - 然后立刻执行
(success a):这会把a的值作为整个try的结果返回(因为success绑定的是try的外层延续),这就是为什么正常情况下(try var (+ 1 1) 4)会输出2。
- 这里又捕获了一个延续绑定到
- 但如果在
a的执行过程中调用了var(比如(try var (var '(2)) 4)),情况就变了:- 调用
var会触发它绑定的延续,也就是直接跳到letcc var之后的代码——也就是b的表达式序列。 - 这时候
success的调用被跳过了,b里的表达式会被依次求值,最后一个表达式的结果就会作为整个try的结果返回,所以你测试的第二个例子会输出4。
- 调用
简单来说,var绑定的延续就是“放弃执行a剩下的部分,直接去执行b里的代码”,而宏定义里的. b就是把b的表达式放在letcc var之后,让var的延续指向这部分代码。
内容的提问来源于stack exchange,提问作者J. Hauser
相关产品推荐
相关产品推荐

