由于第一个原因也可以使用 values 过程来完成,第二个原因使用 case-lambda 我不清楚使用延续传递样式的优点。
...除了 的定义values
指定它用多个参数调用它的延续。
我最喜欢的关于延续传递风格很有帮助的问题示例是编写模式匹配器。这是一种类似于case
类固醇的宏;它接受一个值并尝试将其结构与由对、符号(代表变量)和非符号原子(代表值)构建的模式序列进行匹配。如果匹配成功,则它将模式中的标识符绑定到值的相应子部分,并为该模式执行一个结果。如果失败,则尝试下一个模式。
以一种延续传递风格的形式编写这种宏非常简单,使用两个不同的延续来表示“如果匹配成功怎么办”(成功延续)和“如果匹配失败怎么办”(失败续)。
以我曾经编写的模式匹配宏的这个(简化)片段为例(如果您不知道语法大小写或语法规则,我深表歉意;并且由于我是在运行中对其进行了调整,因此我当然希望它也能正常工作!)。我将专注于匹配一对模式的规则。这是一个由一对两个图案组成的图案,一个头部图案和一个尾部图案;它匹配头部与头部模式匹配且尾部与尾部模式匹配的对。
;;;
;;; Outer "driver" macro; the meat is in pmatch-expand-pattern.
;;;
(define-syntax pmatch
(syntax-rules ()
((pmatch value-expr (pattern . exprs) . clauses)
(let* ((value value-expr)
(try-next-clause
(lambda () (pmatch value . clauses))))
(pmatch-expand-pattern pattern
value
;; success-k
(begin . exprs)
;; failure-k
(try-next-clause))))))
(define-syntax pmatch-expand-pattern
(lambda (stx)
(syntax-case stx ()
;; Cases for constants and quoted symbols omitted, but they're trivial.
;; Match a pair pattern. Note that failure-k is expanded three times;
;; that's why pmatch encapsulates its expansion inside a thunk!
((pmatch-expand-pattern (head-pat . tail-pat) value success-k failure-k)
(syntax
(if (pair? value)
(pmatch-expand-pattern head-pat
(car value)
;; If we successfully match the head, then
;; the success continuation is a recursive
;; attempt to match the tail...
(pmatch-expand-pattern tail-pat
(cdr value)
success-k
failure-k)
failure-k))
failure-k))
;; Match an identifier pattern. Always succeeds, binds identifier
;; to value
((pmatch-expand-pattern identifier value success-k failure-k)
(identifier? (syntax identifier))
(syntax (let ((identifier value)) success-k)))
)))
pmatch-expand-pattern
注意宏表达式中的success-k 和failure-k 子形式。这些表示被用作模式匹配器的“延续”的表达式,用稍微宽松的术语来说。当所考虑的模式与所考虑的值匹配时,使用成功延续;如果没有,则使用失败继续。成功的延续取决于我们是否已经匹配了所有当前顶级模式,要么是一个匹配模式其余部分的表达式,要么是我们在模式完成匹配时执行的结果。当模式无法匹配时,使用失败延续,以便回溯到下一个选择点。
正如我所提到的,在上面的代码中,术语“延续”的使用有点松散,因为我们使用表达式作为延续。但这只是关于如何将其实现为宏的细节——该算法可以纯粹在运行时实现,并以实际过程作为延续。(此外,这些try-next-clause
过程是字面意义上的延续。)