以下示例涉及跳转到延续和退出。有人可以解释功能的流程。我围绕延续移动了一圈,并且不知道函数的入口和出口点。
(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
感谢。
答案 0 :(得分:4)
该过程迭代一个列表,将非零成员相乘,并在每次找到零时返回结果。 Resume-visit
存储处理列表其余部分的延续,return-result
具有迭代器的调用站点的延续。在开始时,resume-visit
被定义为处理整个列表。每次找到零时,都会捕获一个延续,当被调用时,会继续执行(process-list (rest lst) 1)
,以获得当时lst
的值。当列表用尽时,resume-visit
被设置为虚拟过程。此外,每次程序调用{{1}}时,它都会执行以下操作:
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
表达式的过程。在这种情况下,将存储该延续并调用另一个延续,这会将累积结果rest
返回给p
的调用者。将在下次调用iter
时调用该延续,然后循环继续执行列表的其余部分。这是延续的要点,其他一切都是基本的递归。
答案 1 :(得分:1)
您需要记住的是,对(call/cc f)
的调用会将作为f
参数传递的函数call/cc
应用于当前延续。如果在函数a
内使用某个参数f
调用该延续,则执行将转到相应的call/cc
调用,并且参数a
将返回为返回call/cc
。
您的程序会在变量call/cc
中存储“在iter
中调用return-result
”的延续,并开始处理列表。它在遇到前0之前将列表的前3个非零元素相乘。当它看到0时,继续“处理列表元素0”存储在resume-visit
中,值p
通过调用return-result
返回到续集(return-result p)
。此调用将使执行返回call/cc
中的iter
,并call/cc
返回p
的传递值。所以你看到第一个输出6。
iter
的其余调用是相似的,并且会使执行在这两个延续之间来回传递。手动分析可能有点大脑扭曲,你必须知道恢复延续时的执行上下文。
答案 2 :(得分:1)
你可以这样做:
(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只返回它们应用的值。
闭包是在参数与它们绑定之前携带其环境变量的函数。他们是普通的羔羊。
延续传递样式是一种将闭包作为参数传递以便稍后应用的方法。我们说这些闭包参数是延续的。这里是来自我的数独生成器/求解器的当前代码的一半,作为示例演示了延续传递样式如何简化算法:
#| 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))