计划延续假人

use*_*016 27 lisp scheme continuations

对于我的生活,我无法理解延续.我认为这个问题源于我不明白它们的用途.我在书籍或网上找到的所有例子都非常简单.他们让我想知道,为什么有人甚至想要延续?

这是一个典型的不切实际的例子,来自TSPL,我认为这是一本非常公认的关于这个主题的书.在英语中,他们将继续描述为计算结果的"做什么".好的,这是可以理解的.

然后,第二个例子给出:

(call/cc
  (lambda (k)
    (* 5 (k 4)))) => 4 
Run Code Online (Sandbox Code Playgroud)

这有什么用?k甚至没有定义!当(k 4)无法计算时,如何评估此代码?更何况,如何call/cc知道撕掉4最内层表达式的参数并返回它?会发生什么事(* 5 ..?如果这个最外层的表达式被丢弃,为什么要写呢?

然后,陈述的"较少"的简单示例是如何使用call/cc来提供递归的非本地退出.这听起来像流控制指令,即break/return在命令式语言中,而不是计算.

通过这些动议的目的是什么?如果有人需要计算结果,为什么不只是存储它并在以后根据需要调用.

GoZ*_*ner 22

call/cc了一会儿.在任何编程语言中,每个表达式/语句都有一个延续 - 也就是说,你对结果做了什么.以C为例,

x = (1 + (2 * 3)); 
printf ("Done");
Run Code Online (Sandbox Code Playgroud)

继续进行数学作业printf(...); 继续(2 * 3)是'加1; 分配给x; 的printf(...)".从概念上讲,无论您是否有权访问它,都会继续存在.想一想继续所需的信息 - 信息是1)堆内存状态(一般),2)堆栈,3)任何寄存器和4)程序计数器.

因此存在延续,但通常它们只是隐含的,不能被访问.

在Scheme和其他一些语言中,您可以访问continuation.基本上,在您的背后,编译器+运行时捆绑了延续所需的所有信息,将其存储(通常在堆中)并为您提供句柄.你得到的手柄是函数'k' - 如果你调用该函数,你将在该call/cc点之后继续.重要的是,您可以多次调用该函数,并且您将始终在该call/cc点之后继续.

我们来看一些例子:

> (+ 2 (call/cc (lambda (cont) 3)))
5
Run Code Online (Sandbox Code Playgroud)

在上面,结果call/cc是3的结果.lambda没有调用继续.

现在让我们调用延续:

> (+ 2 (call/cc (lambda (cont) (cont 10) 3)))
12
Run Code Online (Sandbox Code Playgroud)

通过调用continuation,我们在调用之后跳过任何内容,并在该call/cc点继续.使用(cont 10)延续返回10,将12添加到2.

现在让我们保存延续.

> (define add-2 #f)
> (+ 2 (call/cc (lambda (cont) (set! add-2 cont) 3)))
5
> (add-2 10)
12
> (add-2 100)
102
Run Code Online (Sandbox Code Playgroud)

通过保存延续,我们可以随意使用它来"跳回"任何计算call/cc点.

通常延续用于非本地退出.想想一个将返回列表的函数,除非在哪一点上'()会返回一些问题.

(define (hairy-list-function list)
  (call/cc
    (lambda (cont)
       ;; process the list ...

       (when (a-problem-arises? ...)
         (cont '()))

       ;; continue processing the list ...

       value-to-return)))
Run Code Online (Sandbox Code Playgroud)

  • `(++ 1(call / cc(lambda(cont)3))))和`(+1(call / cc(lambda(cont)(cont 3))))之间有什么细微差别?两者返回相同的结果? (2认同)
  • “ call / cc”的返回值为1)“ lambda”返回的值,或者2)调用延续性时提供的参数。在您的第一种情况下,`lambda`返回3,`call / cc`返回3,总结果为4。在您的第二种情况下,继续以3调用,`call / cc`返回3,结果为4。 (2认同)

Eli*_*lay 9

以下是我班级笔记中的文字:http://tmp.barzilay.org/cont.txt.它基于许多来源,并且得到了很大的扩展.它有动机,基本解释,对它如何完成的更高级的解释,以及从简单到高级的许多例子,甚至是对分隔延续的一些快速讨论.

(我尝试将整篇文章放在这里,但正如我所料,120k的文字并不能令人高兴.


Wil*_*ess 5

TL;DR:延续只是捕获的 GOTO,带有或多或少的值。

你问的例子,

(call/cc
  (lambda (k)
    ;;;;;;;;;;;;;;;;
    (* 5 (k 4))                     ;; body of code
    ;;;;;;;;;;;;;;;;
    )) => 4 
Run Code Online (Sandbox Code Playgroud)

可以近似翻译成例如 Common Lisp,如

(prog (k retval)
    (setq k (lambda (x)             ;; capture the current continuation:
                    (setq retval x) ;;   set! the return value
                    (go EXIT)))     ;;   and jump to exit point

    (setq retval                    ;; get the value of the last expression,
      (progn                        ;;   as usual, in the
         ;;;;;;;;;;;;;;;;
         (* 5 (funcall k 4))        ;; body of code
         ;;;;;;;;;;;;;;;;
         ))
  EXIT                              ;; the goto label
    (return retval))
Run Code Online (Sandbox Code Playgroud)

这只是一个示例;在 Common Lisp 中,我们第一次退出后就无法跳回 PROG 标记体。但在Scheme中,通过真正的延续,我们可以。如果我们在Scheme中调用的函数体内设置一些全局变量call/cc,那么(setq qq k)我们可以在以后的任何时间,从任何地方调用它,重新进入相​​同的上下文(例如(qq 42))。

要点是,call/cc形式的主体可以包含一个if或一个cond表达式。它只能在某些情况下调用延续,而在其他情况下则正常返回,像往常一样评估代码体中的所有表达式并返回最后一个的值。那里可能会发生深度递归。通过调用捕获的延续,可以实现立即退出。

所以我们在这里看到它的k 定义。它是由call/cc调用定义的。当(call/cc g)被调用时,它用当前的延续来调用它的参数:(g the-current-continuation)the current-continuation是一个指向表单返回点的“转义过程”call/cc。调用它意味着提供一个值,就好像它是由call/cc表单本身返回的一样。

所以上面的结果是

((lambda(k) (* 5 (k 4))) the-current-continuation) ==>

(* 5 (the-current-continuation 4)) ==>

; to call the-current-continuation means to return the value from
; the call/cc form, so, jump to the return point, and return the value:

4
Run Code Online (Sandbox Code Playgroud)