for-each 中的方案延续

scheme continuation inside a for-each

我目前正在我大学的一门课程中学习 Scheme,在看一些练习时我被困在了这个特定的练习上。 教授还没有回复我以前的邮件,所以我有更多的机会在这里更快地收到答案。

鉴于此代码

(define (list-iter-cc lst)
  (call/cc 
    (lambda (return) 
      (for-each               
          (lambda (x)
            (call/cc (lambda (next-step)
                       (return (cons x next-step))))) 
          lst)
     'end)))

我必须用它来编写 iter 语法为

的宏
(iter <a variable symbol> in <a list> <code>)

示例:

(iter x in '(1 2 3) 
    (display x)
    (newline))

看不懂list-iter-cc就去看了解法,我也看不懂。解决方案:

(define-syntax iter2
  (syntax-rules (-> in)
    ((_ var in lst code ...)
     (let loop ((head (list-iter-cc lst)))
       (unless (eq? head 'end)
         (let ((var (car head)))
           code ... 
           (loop ((cdr head)))))))))

为了解开宏,我尝试编写以下内容

> (define head (list-iter-cc '(1 2 3 4)))
> head
'(1 . #<continuation>)
> (let ( (var (car head))) (display var))
1
> (define head2 (cdr head))
> (let ( (var2 (car head2)) ) (display var2))
Xxx X car: contract violation
  expected: pair?
  given: #<continuation>
> 

这正是我所想的。

list-iter-cc 的 return 延续在第一个 lambda 中的 for-each 的第一次迭代中被调用,returning with cons x next-stepx 是列表的第一个元素,next-step 是一个延续。

1). next-step的内容是什么? for-each 的以下迭代?它如何在最后一次迭代后评估为 'end

2).假设在宏中 head (list-iter-cc lst)'(1 . #<continuation>)car1 并且它被显示,但是在循环它的 之后cdr,var (car head)将是car的延续!它怎么可能评估为 2 然后 3 然后 'end,为什么这不会发生在我试图编写以理解它的代码中?

任何帮助将不胜感激,尤其是可以指导我一步一步的帮助。

我们可以改写为

(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 的延续来保存当前延续……这是什么?此时,下一步要做的事情只是return给list-iter-cc的调用者一个值。

这意味着,调用 (return a) 会将 return a 的值立即传递给 list-iter-cc 的调用者,就好像函数 list-iter-cc 完成了一样它的计算。

现在,

        (for-each               
            (lambda (x)
              (call/cc (lambda (next-step)
                         (return (cons x next-step))))) 
            lst)

已进入。它为列表 lst 中的每个元素调用它的 lambda 参数,因此得到名称 x.

那么,对于 lst 中的拳头 x,会发生什么?

              (call/cc (lambda (next-step)
                         (return (cons x next-step))))

被调用。 IE。它设置 next-step 来保存 current 延续和 returns 来自整个函数 list-iter-cc一次!

它是什么return?这对 (x . <next-step>)。而调用(next-step)是什么意思?意思是return进入for-each的正文,lst若有,则进入下一个元素。如果不是,则退出 for-each 的循环体,并且 'end 通常 return 作为函数 [=20= 的最后一个表达式的值], 这样就完成了它的计算!

那么,我们该如何使用呢?例如,像这样:

(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)中的continuation为运行时,控件跳转回list-iter-cc的调用点。还记得,"the next-thing-to-do" 是 "just to return a value to the list-iter-cc's caller" 吗?然后使用列表中的下一个值重新输入外部 let 的正文。

这需要转换成一个宏,这应该很简单。

我注意到你的教授在那里使用了 命名循环 ,宏调用 (loop ((cdr a)))。尽管延续没有 return 它的值,因此 loop 的下一个迭代不会进入 因为调用了 loop。控件 jumps 作为延续的一部分,如我的示例函数所示(它有效,当我在 DrRacket 中测试它时)。


更新: 关于你的成绩单,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>)
> 

为什么?因为 运行ning 一个延续意味着控制 returns 到它的调用站点——在这里,这意味着 "define a variable head to hold the supplied value, and return to the REPL".