我在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没有延续的原因吗?
答案 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-1
和main
循环。另一个线程和“更新”函数用于显示延续可用于多个简单的示例。此外,要使此示例正常工作,您需要使用名为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