为什么延续传球风格

Har*_*ier 23 scheme continuations continuation-passing

在Kent Dybvig(第4版)第3.4节的计划编程语言中,他非常清楚地描述延续传递方式是什么.对于为什么他给出了两个原因:

  1. 将多个结果传递给它的continuation,因为实现continuation的过程可以接受任意数量的参数.
  2. CPS还允许一个过程采用单独的延续......,它可以接受不同数量的参数.

由于第一个原因也可以使用values程序和第二个使用case-lambda,我不清楚使用延续传递样式的优点.有人可以告诉我一些关于延续传递风格是否合适的例子,它使代码更好,更清晰等等?

Eli*_*lay 14

Dybvig使用本节中的显式延续来激发call/cc语言的一部分.当他提到在没有它的情况下编写代码需要对所使用的所有代码进行全局转换(包括您调用的函数)时,主要观点是在该部分的末尾附近.因此,在Scheme中,您通常使用宏构建自己的构造,而continuation是这些有用的构造之一 - 但您不能通过宏实现它们,因为它们只实现本地转换.

但直接使用CPS样式仍然有用:例如,正如他所提到的,你可以编写一个具有多个延续的函数,可能具有不同的arrities - 就像一个解析函数,它接收一个单输入函数来发送一个将值解析为和解析失败时调用的nullary failure函数(此函数可能会因错误或回溯而中止并尝试使用其他解析规则).另一种可能的用途是当你想要精确控制延续的内容而不是call/cc抓住完整的上下文时.

还有一种明显的例子,即用一种没有一流延续的语言编写代码,使得CPSed代码成为你唯一的选择.这方面的一个例子是许多使用IO的node.js程序,并且非常强迫您在显式CPS中编写代码.


Lui*_*las 6

由于第一个原因也可以使用值过程和第二个使用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)))
      )))
Run Code Online (Sandbox Code Playgroud)

请注意pmatch-expand-pattern宏表达式中的success-k和failure-k子表单.这些表示用作模式匹配器的略微宽松术语的"延续"的表达式.当所考虑的模式与所考虑的值匹配时,使用成功继续; 如果没有,则使用失败继续.成功的延续取决于我们是否匹配了所有当前的顶级模式,要么是与模式的其余部分匹配的表达式,要么是在模式完成匹配时执行的结果.当模式无法匹配时,将使用失败延续,以便回溯到下一个选择点.

正如我所提到的,在上面的代码中使用了"continuation"这个术语,因为我们使用表达式作为continuation.但这只是关于如何将其作为宏实现的细节 - 该算法可以纯粹在运行时实现,实际过程作为延续.(此外,try-next-clause程序是字面意义上的延续.)