我们可以将其重写为
(define list-iter-cc
(lambda (lst)
(call/cc
(lambda (return)
(for-each
(lambda (x)
(call/cc (lambda (next-step)
(return (cons x next-step)))))
lst)
'end))))
所以它是一个 lambda 函数,参数名为lst
。当这个函数被调用时,lst
像往常一样设置为保存函数调用的实际参数。然后,call/cc
设置名为的延续return
保持当前的延续...这是什么?此时,下一步要做的事只是返回一个值list-iter-cc
的来电者。
这意味着,调用(return a)
将返回值a
立即到list-iter-cc
的调用者,就像函数一样list-iter-cc
完成了计算。
Now,
(for-each
(lambda (x)
(call/cc (lambda (next-step)
(return (cons x next-step)))))
lst)
已输入。它为列表中的每个元素调用其 lambda 参数lst
,因此得到名称x
.
所以,首先x
in a lst
, 会发生什么?
(call/cc (lambda (next-step)
(return (cons x next-step))))
叫做。 IE。它建立了next-step
持有current继续和returns从整个函数list-iter-cc
立刻!
它返回什么?这对(x . <next-step>)
. And what是不是意味着打电话(next-step)
?意思是回到身体里for-each
,这将继续到下一个元素lst
, if any。如果不是,则循环体for-each
已退出,并且'end
is normally作为函数最后一个表达式的值返回list-iter-cc
,这样就完成了计算!
那么,我们该如何使用它呢?例如,像这样:
(define (qq lst)
(let ([a ;; <<= ; control returns here
(list-iter-cc lst)])
(unless (eq? a 'end) ; if it's not past-last-element
(let ([val (car a)]) ; take the actual value
(display val) ; use it
(newline)
((cdr a)))))) ; run the `next-step` continuation
当继续在(cdr a)
运行后,控件跳回到list-iter-cc
的呼叫站点。请记住,“下一步要做的事”是“只是为了返回一个值list-iter-cc
的来电者”?外部let
然后使用列表中的下一个值重新输入 的正文。
然后需要将其转换为宏,这应该很简单。
我注意到你的教授用了命名循环在那里,宏调用(loop ((cdr a)))
。但延续不会返回其值,因此下一次迭代loop
未输入因为打电话给loop
。控制jumps作为延续的一部分,如我的示例函数所示(当我在 DrRacket 中测试它时,它有效)。
update:关于你的成绩单,head2
已经是一个#<continuation>
,它没有car
– 这不是一个pair?
。相反,请参阅以下内容:
> (define head #| <<= |# (list-iter-cc '(1 2 3 4))) ; control returns here
> head
'(1 . #<continuation>)
> (let ( (var (car head))) (display var)) ; your code
1
> ((cdr head)) ; this is how we run it!
> head
'(2 . #<continuation>)
>
什么?你看到刚刚发生的事情了吗?head
被重新定义了!然后再次,
> ((cdr head))
> head
'(3 . #<continuation>)
>
为什么?因为运行延续意味着控件返回到其调用站点 - 在这里,这意味着“定义一个变量head
保存提供的值,并返回 REPL”.