【问题标题】:scheme continuation inside a for-eachfor-each 中的方案延续
【发布时间】:2018-04-08 01:58:57
【问题描述】:

我目前正在为我的大学的一门课程学习 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 的返回延续在第一个 lambda 内的 for-each 的第一次迭代中被调用,返回 cons x next-stepx 是列表的第一个元素,next-step 是一个延续。

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

2)。假设在宏 head (list-iter-cc lst)'(1 . #&lt;continuation&gt;) 中,car1 并且它会被显示,但是在遍历它的 cdr 之后,var (car head) 将是继续的汽车!它怎么可能评估为2,然后是3,然后是'end,为什么在我尝试编写的代码中没有发生这种情况?

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

【问题讨论】:

    标签: foreach scheme continuations callcc


    【解决方案1】:

    我们可以重写为

    (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 的延续来保存当前延续......这是什么?此时,next-thing-to-do只是给list-iter-cc的调用者返回一个值。

    这意味着,调用(return a) 将立即将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 来保持当前 延续和返回 从整个函数list-iter-cc 一次!

    它返回什么?这对(x . &lt;next-step&gt;)什么 打电话给(next-step) 是什么意思?这意味着返回到for-each 的主体,这将继续到lst 中的下一个元素,如果有。如果不是,则退出for-each 的循环体,并通常'end 作为函数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) 中的continuation 运行时,控件跳回到list-iter-cc 的调用站点。还记得,“下一步要做的事情”是“只是为list-iter-cc 的调用者返回一个值”吗?然后使用列表中的下一个值重新输入外部let 的主体。

    这需要被翻译成一个宏,这应该是直截了当的。

    我注意到你的教授在那里使用了命名循环,宏调用(loop ((cdr a)))。但是,延续不会返回其值,因此不会输入loop 的下一次迭代,因为调用了loop。控件跳转作为延续的一部分,如我的示例函数所示(当我在 DrRacket 中测试它时,它起作用了)。


    更新:关于你的成绩单,head2 已经是 #&lt;continuation&gt;,它没有 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”

    【讨论】:

    • 感谢您的解释!我或多或少已经猜到发生了什么,但是当我试图用我的成绩单验证它时它没有用,你的更新告诉我为什么!我应该用((cdr head)) 再次调用并运行延续,而head 将获得新值。非常聪明!请问I notice your prof used [...] 是什么意思,在我看来,通过调用(loop ( (cdr head) ) ),我们不仅将延续传递给下一次迭代,而且还在执行它。我说的对吗?
    • 延续不返回,所以我试着说“运行”而不是“被调用”来暗示这个事实。调用(loop (function call)) 调用(function call),从它​​接收结果,然后将其传递给loop 的下一次迭代。在这里,(cdr a) 被“称为”而不是“运行”。它不会永远返回一个值,因为延续 jump;他们返回他们自己的目的地,而不是触发他们运行的语法调用。请注意,我只写了((cdr a)),然后它重新进入循环体。我没有像你的帖子那样使用(loop ((cdr a)))。 (我假设你的教授给了你这个代码)。
    • 所以我们肯定将延续传递到任何地方。这不是一种“延续”传递风格,其中“延续”只是常规函数,而不是真正的完整延续。混淆命名,对吧?我尝试将它们称为“应急功能”。 --- 在这里,我们有一个 true 延续,它运行,它没有被“调用”,因为它没有返回给它的调用者 - - 一个真正的延续返回到 its 上下文,它的定义位置。 --- 不客气!
    猜你喜欢
    • 1970-01-01
    • 2012-11-03
    • 2013-04-22
    • 1970-01-01
    • 2015-08-28
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多