[英]scheme continuations -need explanation
以下示例涉及跳转到延续和退出。 有人可以解释功能的流程。 我围绕延续移动了一圈,并且不知道函数的入口和出口点。
(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
谢谢。
该过程迭代一个列表,将非零成员相乘,并在每次找到零时返回结果。 Resume-visit
存储继续处理列表的其余部分, return-result
继续迭代器的调用站点。 在开始时,定义resume-visit
来处理整个列表。 每一个零被发现时,继续被捕获,其中,当被调用执行(process-list (rest lst) 1)
的任何值lst
在当时。 当列表用尽时, resume-visit
设置为虚拟过程。 而且,每次程序调用iter
,它都会执行以下操作:
(call/cc
(lambda (k)
(set! return-result k)
(resume-visit 'dummy)))
也就是说,它捕获调用者的延续,调用它会向调用者返回一个值。 存储继续,程序跳转以处理列表的其余部分。 当过程调用resume-visit
,进入循环,当调用return-result
时,退出循环。
如果我们想更详细地检查process-list
,我们假设列表是非空的。 该过程采用基本递归,累积结果直到找到零。 此时, p
是累计值, lst
是包含零的列表。 当我们有一个像(begin (call/cc (lambda (k) first)) rest)
的构造时,我们首先执行first
表达式,其中k
绑定到一个continuation。 它是一个在调用时执行rest
表达式的过程。 在这种情况下,存储该延续并调用另一个继续,它将累积的结果p
返回给iter
的调用者。 将在下次调用iter
调用该延续,然后循环继续执行列表的其余部分。 这是延续的要点,其他一切都是基本的递归。
你需要记住的是,对(call/cc f)
将把作为参数传递的函数f
应用于call/cc
到当前的continuation。 如果在函数f
内部使用某个参数a
调用该延续,则执行将转到相应的call/cc
,并且参数a
将作为该call/cc
的返回值返回。
您的程序在变量return-result
存储“ call/cc
iter
call/cc
in iter
”的继续,并开始处理列表。 它在遇到前0之前将列表的前3个非零元素相乘。当它看到0时,继续“处理列表元素0”存储在resume-visit
,值p
返回到continuation return-result
通过调用(return-result p)
。 此调用将使执行返回到iter
的call/cc
,并且该call/cc
返回p
的传递值。 所以你看到第一个输出6。
其余的对iter
调用是相似的,并且会使执行在这两个延续之间来回传递。 手动分析可能有点大脑扭曲,您必须知道恢复延续时的执行上下文。
你可以这样做:
(define (prod-iter lst) (fold * 1 (remove zero? lst)))
......即使它只能通过一次遍历也能表现得更好。
对于延续,召回(双关语)所有呼叫/ cc所做的是等待以这种方式应用“k”:
(call/cc (lambda (k) (k 'return-value)))
=> return-value
这里的诀窍是你可以让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
这样可以通过将延迟保存在变量中来使连续返回多次。 Continuations只返回它们应用的值。
闭包是在参数与它们绑定之前携带其环境变量的函数。 他们是普通的lambdas。
延续传递样式是一种将闭包作为参数传递以便稍后应用的方法。 我们说这些闭包参数是延续的。 这里是来自我的数独生成器/解算器的当前代码的一半,作为示例演示了延续传递样式如何简化算法:
#| 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.