简体   繁体   English

计划延续 - 需要解释

[英]scheme continuations -need explanation

The following example involves jumping into continuation and exiting out. 以下示例涉及跳转到延续和退出。 Can somebody explain the flow of the function. 有人可以解释功能的流程。 I am moving in a circle around continuation, and do not know the entry and exit points of the function. 我围绕延续移动了一圈,并且不知道函数的入口和出口点。

(define (prod-iterator lst)
  (letrec ((return-result empty)
        (resume-visit (lambda (dummy) (process-list lst 1)))
        (process-list
         (lambda (lst p)
           (if (empty? lst)
               (begin
                 (set! resume-visit (lambda (dummy) 0))
                 (return-result p))
               (if (= 0 (first lst))
                   (begin
                     (call/cc ; Want to continue here after delivering result     
                      (lambda (k)
                        (set! resume-visit k)
                        (return-result p)))
                     (process-list (rest lst) 1))
                   (process-list (rest lst) (* p (first lst))))))))
    (lambda ()
      (call/cc
       (lambda (k)
         (set! return-result k)
         (resume-visit 'dummy))))))

(define iter (prod-iterator '(1 2 3 0 4 5 6 0 7 0 0 8 9)))
(iter) ; 6                                                                        
(iter) ; 120                                                                      
(iter) ; 7                                                                        
(iter) ; 1                                                                        
(iter) ; 72                                                                       
(iter) ; 0                                                                        
(iter) ; 0

Thanks. 谢谢。

The procedure iterates over a list, multiplying non-zero members and returning a result each time a zero is found. 该过程迭代一个列表,将非零成员相乘,并在每次找到零时返回结果。 Resume-visit stores the continuation for processing the rest of the list, and return-result has the continuation of the call-site of the iterator. Resume-visit存储继续处理列表的其余部分, return-result继续迭代器的调用站点。 In the beginning, resume-visit is defined to process the entire list. 在开始时,定义resume-visit来处理整个列表。 Each time a zero is found, a continuation is captured, which when invoked executes (process-list (rest lst) 1) for whatever value lst had at the time. 每一个零被发现时,继续被捕获,其中,当被调用执行(process-list (rest lst) 1)的任何值lst在当时。 When the list is exhausted, resume-visit is set to a dummy procedure. 当列表用尽时, resume-visit设置为虚拟过程。 Moreover, every time the program calls iter , it executes the following: 而且,每次程序调用iter ,它都会执行以下操作:

(call/cc
   (lambda (k)
     (set! return-result k)
     (resume-visit 'dummy)))

That is, it captures the continuation of the caller, invoking it returns a value to the caller. 也就是说,它捕获调用者的延续,调用它会向调用者返回一个值。 The continuation is stored and the program jumps to process the rest of the list. 存储继续,程序跳转以处理列表的其余部分。 When the procedure calls resume-visit , the loop is entered, when return-result is called, the loop is exited. 当过程调用resume-visit ,进入循环,当调用return-result时,退出循环。

If we want to examine process-list in more detail, let's assume the list is non-empty. 如果我们想更详细地检查process-list ,我们假设列表是非空的。 Tho procedure employs basic recursion, accumulating a result until a zero is found. 该过程采用基本递归,累积结果直到找到零。 At that point, p is the accumulated value and lst is the list containing the zero. 此时, p是累计值, lst是包含零的列表。 When we have a construction like (begin (call/cc (lambda (k) first)) rest) , we first execute first expressions with k bound to a continuation. 当我们有一个像(begin (call/cc (lambda (k) first)) rest)的构造时,我们首先执行first表达式,其中k绑定到一个continuation。 It is a procedure that when invoked, executes rest expressions. 它是一个在调用时执行rest表达式的过程。 In this case, that continuation is stored and another continuation is invoked, which returns the accumulated result p to the caller of iter . 在这种情况下,存储该延续并调用另一个继续,它将累积的结果p返回给iter的调用者。 That continuation will be invoked the next time iter is called, then the loop continues with the rest of the list. 将在下次调用iter调用该延续,然后循环继续执行列表的其余部分。 That is the point with the continuations, everything else is basic recursion. 这是延续的要点,其他一切都是基本的递归。

What you need to keep in mind is that, a call to (call/cc f) will apply the function f passed as argument to call/cc to the current continuation. 你需要记住的是,对(call/cc f)将把作为参数传递的函数f应用于call/cc到当前的continuation。 If that continuation is called with some argument a inside the function f , the execution will go to the corresponding call to call/cc , and the argument a will be returned as the return value of that call/cc . 如果在函数f内部使用某个参数a调用该延续,则执行将转到相应的call/cc ,并且参数a将作为该call/cc的返回值返回。

Your program stores the continuation of "calling call/cc in iter " in the variable return-result , and begins processing the list. 您的程序在变量return-result存储“ call/cc iter call/cc in iter ”的继续,并开始处理列表。 It multiplies the first 3 non-zero elements of the list before encountering the first 0. When it sees the 0, the continuation "processing the list element 0" is stored in resume-visit , and the value p is returned to the continuation return-result by calling (return-result p) . 它在遇到前0之前将列表的前3个非零元素相乘。当它看到0时,继续“处理列表元素0”存储在resume-visit ,值p返回到continuation return-result通过调用(return-result p) This call will make the execution go back to the call/cc in iter , and that call/cc returns the passed value of p . 此调用将使执行返回到itercall/cc ,并且该call/cc返回p的传递值。 So you see the first output 6. 所以你看到第一个输出6。

The rest calls to iter are similar and will make the execution go back and forth between such two continuations. 其余的对iter调用是相似的,并且会使执行在这两个延续之间来回传递。 Manual analysis may be a little brain-twisting, you have to know what the execution context is when a continuation is restored. 手动分析可能有点大脑扭曲,您必须知道恢复延续时的执行上下文。

You could achieve the same this way: 你可以这样做:

(define (prod-iter lst) (fold * 1 (remove zero? lst)))

... even though it could perform better by traversing only once. ......即使它只能通过一次遍历也能表现得更好。

For continuations, recall (pun intended) that all call/cc does is wait for "k" to be applied this way: 对于延续,召回(双关语)所有呼叫/ cc所做的是等待以这种方式应用“k”:

(call/cc (lambda (k) (k 'return-value)))
  => return-value

The trick here is that you can let call/cc return its own continuation so that it can be applied elsewhere after call/cc has returned like this: 这里的诀窍是你可以让call / cc返回它自己的continuation,这样它就可以在call / cc返回之后应用到别处:

;; returns twice; once to get bound to k, the other to return blah
(let ([k (call/cc (lambda (k) k))]) ;; k gets bound to a continuation
  (k 'blah)) ;; k returns here
  => blah

This lets a continuation return more than once by saving it in a variable. 这样可以通过将延迟保存在变量中来使连续返回多次。 Continuations simply return the value they are applied to. Continuations只返回它们应用的值。

Closures are functions that carry their environment variables along with them before arguments get bounded to them. 闭包是在参数与它们绑定之前携带其环境变量的函数。 They are ordinary lambdas. 他们是普通的lambdas。

Continuation-passing style is a way to pass closures as arguments to be applied later. 延续传递样式是一种将闭包作为参数传递以便稍后应用的方法。 We say that these closure arguments are continuations. 我们说这些闭包参数是延续的。 Here's half of the current code from my sudoku generator/solver as an example demonstrating how continuation-passing style can simplify your algorithms: 这里是来自我的数独生成器/解算器的当前代码的一半,作为示例演示了延续传递样式如何简化算法:

#| the grid is internally represented as a vector of 81 numbers
example: (make-vector 81 0)

this builds a list of indexes |#
(define (cell n) (list (+ (* (car 9) (cadr n))))
(define (row n) (iota 9 (* n 9)))
(define (column n) (iota 9 n 9))
(define (region n)
  (let* ([end (+ (* (floor-quotient n 3) 27)
                 (* (remainder n 3) 3))]
         [i (+ end 21)])
    (do ([i i
          (- i (if (zero? (remainder i 3)) 7 1))]
         [ls '() (cons (vector-ref *grid* i) ls)])
      ((= i end) ls))))

#| f is the continuation

usage examples:
(grid-ref *grid* row 0)
(grid-set! *grid* region 7) |#
(define (grid-ref g f n)
  (map (lambda (i) (vector-ref g i)) (f n)))
(define (grid-set! g f n ls)
  (for-each (lambda (i x) (vector-set! g i x))
    (f n) ls))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM