Scheme中的调用/ cc与Python和JavaScript中的yield相同吗?

时间:2017-06-13 07:25:09

标签: scheme generator yield

Scheme中的call/cc与Python和JavaScript中的yield相同吗?

我不清楚发电机。在我看来,yield使语言能够无痛地生成迭代器。但我不确定我是否正确。

Scheme中的call/cc是否与其他语言中的yield有关?如果是这样,它们是相同的,还是有什么区别?

谢谢!

3 个答案:

答案 0 :(得分:1)

这里是定义生成器的代码:

(define-syntax define-generator
  (lambda (x)
    (syntax-case x (lambda)
      ((stx name (lambda formals e0 e1 ...))
         (with-syntax ((yield (datum->syntax (syntax stx) 'yield)))
           (syntax (define name
             (lambda formals
               (let ((resume #f) (return #f))
                 (define yield
                   (lambda args
                     (call-with-current-continuation
                      (lambda (cont)
                        (set! resume cont)
                        (apply return args)))))
                 (lambda ()
                   (call-with-current-continuation
                    (lambda (cont)
                      (set! return cont)
                      (cond (resume (resume))
                      (else (let () e0 e1 ...)
                            (error 'name "unexpected return"))))))))))))
        ((stx (name . formals) e0 e1 ...)
          (syntax (stx name (lambda formals e0 e1 ...)))))))

my blog处有使用发电机的例子。生成器使用call-with-current-continuation,类似于Python中的yield,但更通用。

答案 1 :(得分:1)

您可以使用call / cc实现生成器。这是一个例子:

coroutines.ss

它们的工作方式与python和ECMAScript生成器类似。

答案 2 :(得分:1)

call/cc是一种比生成器更通用的语言功能。因此,您可以使用call/cc创建生成器,但不能使用生成器生成call/cc

如果你有一个计算值并在其他地方使用这些值的程序,它基本上就是一个步骤机器。人们可能会认为它是一个程序,每个步骤都有一个函数,其余步骤的延续。因此:

(+ (* 3 4) (* 5 6))

可以解释为:

((lambda (k)
  (k* 3 4 (lambda (v34)
            (k* 5 6 (lambda (v56)
                      (k+ v34 v56 k)))))
 halt)

k前缀只表示它是原语的CPS版本。因此,他们将最后一个参数称为结果函数。另请注意,在此重写中实际上选择了在Scheme中未定义的评估顺序。在这个美丽的语言call/cc就是这样:

(define (kcall/cc kfn k)
  (kfn (lambda (value ignored-continuation)
         (k value))
       k))

所以当你这样做时:

(+ (* 3 4) (call/cc (lambda (exit) (* 5 (exit 6))))) 
; ==> 18

在幕后发生这种情况:

((lambda (k)
  (k* 3 4 (lambda (v34)
            (kcall/cc (lambda (exit k)
                        (exit 6 (lambda (v6)
                                 (k* 5 v6 k)))
                      k))))
 halt)

通过使用替换,我们可以证明这实际上完全符合预期。由于调用了exit函数,因此永远不会调用原始continuation,因此取消了计算。与call/cc相比,给我们这种延续似乎并不明显,这在CPS中毫无魔力。因此call/cc的大部分魔力都在编译阶段。

(define (make-generator procedure)
  (define last-return values)
  (define last-value #f)
  (define (last-continuation _) 
    (let ((result (procedure yield))) 
      (last-return result)))

  (define (yield value)
    (call/cc (lambda (continuation)
               (set! last-continuation continuation)
               (set! last-value value)
               (last-return value))))

  (lambda args
    (call/cc (lambda (return)
               (set! last-return return)
               (if (null? args)
                   (last-continuation last-value)
                   (apply last-continuation args))))))

(define test 
 (make-generator
   (lambda (collect)
     (collect 1)
     (collect 5)
     (collect 10)
     #f)))

(test) ; ==> 1
(test) ; ==> 5
(test) ; ==> 10
(test) ; ==> #f (procedure finished)

有人可能make a macro to make the syntax more similar,但这只是糖。

有关更多示例I love Matt Mights page,其中有很多关于如何使用continuation的示例。