Scheme中向后延续的最简单示例,没有显式变异

时间:2009-06-11 21:21:05

标签: haskell functional-programming scheme continuations

我在C#中编写了一个小型的Scheme解释器,并意识到我实现它的方式,很容易添加对正确延续的支持。

所以我添加了它们......但是想要“证明”我们添加它们的方式是正确的。

然而,我的Scheme解释器不支持“变异”状态 - 一切都是不可变的。

因此,编写一个单元测试以暴露“向上”延续非常容易:

AssertEqual(Eval("(call/cc (lambda (k) (+ 56 (k 3))))"), 3);

然而,我还想写一个单元测试,证明如果延续“逃脱”,那么它仍然有效:

AssertEqual(Eval("(call/cc (lambda (k) k))", <some continuation>);

但是,当然,上面只会测试“我得到了延续”......而不是它实际上是一个有效的延续。

然而,我能找到的所有例子总是最终使用“set!”证明逃脱的继续。

最简单的Scheme示例如何在不依赖突变的情况下显示对向后延续的适当支持?

在没有变异的情况下,向后延续是否有用?我开始怀疑它们不是,因为你只能用它来再次执行完全相同的计算......如果没有副作用,这是没有意义的。这就是Haskell没有延续的原因吗?

4 个答案:

答案 0 :(得分:8)

我不知道这是否是最简单的,但这里是一个使用向后延续而不调用set!或类似的例子:

(apply
  (lambda (k i) (if (> i 5) i (k (list k (* 2 i)))))
  (call/cc (lambda (k) (list k 1))))

这应评估为8

稍微有趣的是:

(apply
  (lambda (k i n) (if (= i 0) n (k (list k (- i 1) (* i n)))))
  (call/cc (lambda (k) (list k 6 1))))

计算6!(也就是说,它应该评估为720)。

您甚至可以使用let*执行相同的操作:

(let* ((ka (call/cc (lambda (k) `(,k 1)))) (k (car ka)) (a (cadr ka)))
      (if (< a 5) (k `(,k ,(* 2 a))) a))

(man,stackoverflow的语法突出显示在方案上大量失败。)

答案 1 :(得分:2)

我认为你是对的 - 没有突变,向后延续不能做任何前进延续不能。

答案 2 :(得分:0)

这是我提出的最好的:

AssertEqual(Eval("((call/cc (lambda (k) k)) (lambda (x) 5))", 5);

并不令人惊讶,但它是一个向后延续,然后我用我希望调用的实际函数“调用”一个返回数字5的函数。

啊,我也把它作为一个好的单元测试案例:

AssertEqual(Eval("((call/cc call/cc) (lambda (x) 5))", 5);

我同意Jacob B - 我认为没有可变状态会有用......但是仍然会对反例持感兴趣。

答案 3 :(得分:0)

功能主题:

您可以使用递归循环来更新没有突变的状态。包括要调用的下一个继续的状态。现在这比给出的其他示例更复杂,但您真正需要的只是thread-1main循环。另一个线程和“更新”函数用于显示延续可用于多个简单的示例。此外,要使此示例正常工作,您需要使用名为let的实现。这可以转换为使用define语句创建的等效形式。

示例:

(let* ((update (lambda (data) data))                ;is identity to keep simple for example
       (thread-1                                    
         (lambda (cc)                               ;cc is the calling continuation
           (let loop ((cc cc)(state 0))
             (printf "--doing stuff       state:~A~N" state)
             (loop (call/cc cc)(+ state 1)))))      ;this is where the exit hapens
       (thread-2
         (lambda (data)                             ;returns the procedure to be used as 
           (lambda (cc)                             ;thread with data bound
             (let loop ((cc cc)(data data)(state 0))
               (printf "--doing other stuff state:~A~N" state)
               (loop (call/cc cc)(update data)(+ state 1)))))))
  (let main ((cur thread-1)(idle (thread-2 '()))(state 0))
    (printf "doing main stuff    state:~A~N" state)
    (if (< state 6)
        (main (call/cc idle) cur (+ state 1)))))

哪个输出

doing main stuff    state:0
--doing other stuff state:0
doing main stuff    state:1
--doing stuff       state:0
doing main stuff    state:2
--doing other stuff state:1
doing main stuff    state:3
--doing stuff       state:1
doing main stuff    state:4
--doing other stuff state:2
doing main stuff    state:5
--doing stuff       state:2
doing main stuff    state:6