关于Scheme中cond表达式两种真值写法返回值不同的设计原因问询
Scheme cond表达式行为差异及设计原因
语法规则与实现差异
首先需要明确Scheme标准中cond的语法定义,所有符合R5RS/R6RS/R7RS标准的实现都遵循以下规则:
cond的每个子句必须是列表结构,有三种合法形式:
(<测试表达式> <执行表达式>...):测试表达式求值为真时,依次执行后续所有执行表达式,返回最后一个的结果(<测试表达式>):测试表达式求值为真时,直接返回测试表达式本身的求值结果(else <执行表达式>...):所有前面的测试都失败时触发,执行后续表达式返回结果
你观察到的不同实现的表现差异,核心是Chez Scheme做了非标准的语法扩展:
- 对于
(cond #t (else 2))写法- 标准要求子句必须是列表,#t是原子不符合语法,因此GNU Guile、CHICKEN Scheme直接报语法错误,属于符合标准的行为
- Chez Scheme为了灵活性做了兼容扩展,允许原子作为子句,默认等价于
(<原子> <原子>)的结构,因此#t作为子句等价于(#t #t),测试通过返回#t,这是Chez的特有方言特性,不属于通用标准
- 对于
(cond (> 2 1) (else 2))写法
按照标准语法,第一个子句是(> 2 1),属于单元素列表子句,测试> 2 1求值为#t,应该返回#t。你观察到返回1的情况,实际是子句结构为((> 2 1) 1)的标准行为:测试通过后执行后续的1表达式,返回结果1。 - 对于
(cond (#t) (else 2))写法
完全符合标准单元素子句规则,测试#t为真,直接返回测试结果#t,三个实现表现一致,CHICKEN给出的警告是提示else子句永远不会被执行,属于合理的静态检查提示。
cond设计的核心原因
cond这种设计的核心目的是减少冗余代码,兼顾分支判断和结果复用的需求:
很多场景下我们的分支判断本身就会产生有用的返回值,比如关联表查找、正则匹配、资源申请等操作,这些操作的返回值本身为真就代表操作成功,同时返回值就是我们需要的结果。如果没有单元素子句的设计,你需要写冗余的变量绑定逻辑:
;; 没有单元素子句时的写法 (let ((res (assoc 'key alist))) (if res res 'not-found))
而用cond的单元素子句可以直接简化为:
(cond ((assoc 'key alist)) (else 'not-found))
同时保留多表达式子句的设计,兼容普通分支执行的需求,不需要额外引入新的语法结构。
内容的提问来源于stack exchange,提问作者John Z. Li
相关产品推荐
相关产品推荐

