Scheme语法规则中simple-let宏的.tail匹配逻辑疑问
simple-let 宏匹配逻辑的疑问解答 先把咱们要分析的宏定义贴出来,方便对照:
(define-syntax simple-let (syntax-rules () ((_ (head ... ((x . y) val) . tail) body1 body2 ...) (syntax-error "expected an identifier but got" (x . y))) ((_ ((name val) ...) body1 body2 ...) ((lambda (name ...) body1 body2 ...) val ...)))
问题1:. tail 在两个测试示例里分别匹配什么?
在syntax-rules的模式里,. tail是点模式,专门用来匹配列表中某个元素之后剩下的所有内容(注意是一次性匹配剩余部分,不是重复匹配)。
对于
(simple-let ([(a . b) 3]) (+ a 4)):
宏的绑定列表只有[(a . b) 3]这一项。第一个分支的模式里,head ...匹配的是[(a . b) 3]之前的所有绑定(也就是空内容),((x . y) val)精准匹配[(a . b) 3],剩下的. tail自然就匹配空列表了——因为绑定列表里已经没有其他元素了。对于
(simple-let ([a 3] [(b . c) 3]) (+ a b)):
绑定列表是([a 3] [(b . c) 3])。这里head ...匹配前面的合法绑定[a 3],((x . y) val)匹配后面的错误绑定[(b . c) 3],. tail同样匹配空列表,因为[(b . c) 3]是最后一个绑定,后面没内容了。
问题2:第三个测试示例为啥没被第一个错误分支捕获?
先看你说的测试用例:(simple-let ([a 3] [(b . c) 3] [d 4]) (+ a b))。理论上这个输入应该被第一个分支捕获,因为它完全符合第一个分支的模式:
head ...匹配前面的合法绑定[a 3]((x . y) val)匹配中间的错误绑定[(b . c) 3]. tail匹配后面剩余的合法绑定[d 4]
如果实际测试中没触发错误,大概率是你测试时的代码拼写有误,或者Guile版本的特殊情况(不过这种可能性极低)。你可以再仔细检查一下测试代码的括号、空格是否正确,或者换个Guile版本试试。
问题3:用...替代.加在tail后报错,原因是什么?
当你把第一个分支改成类似(_ (head ... ((x . y) val) tail ...) body1 body2 ...)时,出现ellipsis错误,核心原因是:syntax-rules不允许在同一个模式层级里出现多个独立的...重复运算符。
原来的模式里,head ...是重复匹配前面的合法绑定,而. tail是点模式(一次性抓取剩余内容,不是重复),这是语法允许的。但改成tail ...后,同一个绑定列表层级里就有了两个...:head ...和tail ...——这会让匹配逻辑产生歧义,编译器无法明确区分哪些元素属于head的重复,哪些属于tail的重复,所以直接报错禁止这种写法。
内容的提问来源于stack exchange,提问作者Zelphir Kaltstahl

