我一直在浏览整个网络,寻找关于延续的启示,并且令人难以置信的是,最简单的解释如何能够如此完全混淆像我这样的JavaScript程序员.当大多数文章使用Scheme中的代码解释延续或使用monad时尤其如此.
现在我终于认为我已经理解了延续的本质,我想知道我所知道的是否真的是事实.如果我认为真实的事实并非如此,那么它就是无知而不是启蒙.
所以,这就是我所知道的:
在几乎所有语言中,函数显式地将值(和控制)返回给它们的调用者.例如:
var sum = add(2, 3);
console.log(sum);
function add(x, y) {
return x + y;
}Run Code Online (Sandbox Code Playgroud)
现在,在具有第一类函数的语言中,我们可以将控制和返回值传递给回调,而不是显式返回给调用者:
add(2, 3, function (sum) {
console.log(sum);
});
function add(x, y, cont) {
cont(x + y);
}Run Code Online (Sandbox Code Playgroud)
因此,不是从函数返回值,而是继续使用另一个函数.因此,这个函数被称为第一个的延续.
那么延续和回调之间的区别是什么?
我已多次尝试掌握continuation和call/cc的概念.每一次尝试都是失败的.有人可以向我解释这些概念,理想情况下,这些概念比维基百科或其他SO帖子更具现实性.
我有网络编程和OOP的背景.我也理解6502汇编并且与Erlang有一个小的randez-vous.不过,我无法绕过电话/ cc.
我正在尝试在Scheme中掌握call/cc的语义,并且关于continuation的Wikipedia页面以阴阳拼图为例:
(let* ((yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))) )
(yin yang))
Run Code Online (Sandbox Code Playgroud)
它应该输出@*@**@***@****@...,但我不明白为什么; 我希望它输出@*@*********......
有人可以详细解释为什么阴阳拼图的工作方式有效吗?
他们是什么,他们有什么好处?
我没有CS学位,我的背景是VB6 - > ASP - > ASP.NET/C#.任何人都能以清晰简洁的方式解释它吗?
我试图找到如何实现call/cc.我发现的最好的是这个Haskell片段:
callCC f = Cont $ \k -> runCont (f (\a -> Cont $ \_ -> k a)) k
Run Code Online (Sandbox Code Playgroud)
虽然这不是那么简单,我想由于Cont和runCont.我也找到了它的功能描述,尽管从未像实际代码那样清晰.
那么它是如何以最简单的形式实现的呢?我用Scheme和Haskell标记它,因为这是我喜欢的两种语言.
关于Continuation的维基百科文章说:
"在任何支持闭包的语言中,都可以用连续传递方式编写程序并手动实现call/cc."
这是真的,我需要知道如何做或不是真的,这个陈述需要纠正.
如果这是真的,请告诉我如何在Lua中实现call/cc,因为我看不清楚如何.
我想我能够实现来电/立方厘米,如果手动Lua中有coroutine.clone功能解释在这里.
如果闭包不足以实现call/cc那么还需要什么呢?
以下文字是可选阅读.
PS:Lua与其协程表一次性延续.一个coroutine.clone函数允许我克隆它多次调用它,从而有效地使call/cc成为可能(除非我误解了call/cc).但是Lua中不存在克隆功能.Lua IRC频道上的某个人建议我使用Pluto库(它实现序列化)来编组协程,复制它然后解组它并再次使用它.虽然这可能会奏效,但我更感兴趣的是call/cc的理论实现,以及查找语言需要具有的实际最小特征集以便允许其手动实现.
编辑1:好的人,帮我在这里,这花了我很长时间,因为我不知道任何计划,但我想出了一些应该帮助我们的东西.请看下面的代码.第一个是Scheme中的程序,第二个是相同的程序,但在Lua中.
希望这会帮助我们.我相信我们非常接近.
PS:这些例子来自关于CallCC的维基百科文章的第一个例子.
方案版
(define call/cc call-with-current-continuation)
; callcc CPS-transformed (thanks to the people from the #scheme channel at freenode.net)
(define cpscallcc
(lambda (consumer k)
(let ((cc (lambda (result) (k result))))
(consumer cc k))))
; this is the continuation we will use to display the "returned" values
(define main-continuation
(lambda (result)
(display "--> ")
(display result)
(newline)))
; …Run Code Online (Sandbox Code Playgroud) 我认为ContT的正确类型应该是
newtype ContT m a = ContT {runContT :: forall r. (a -> m r) -> m r}
Run Code Online (Sandbox Code Playgroud)
和其他控制操作员
shift :: Monad m => (forall r. (a -> ContT m r) -> ContT m r) -> ContT m a
reset :: Monad m => ContT m a -> ContT m a
callCC :: ((a -> (forall r. ContT m r)) -> ContT m a) -> ContT m a
Run Code Online (Sandbox Code Playgroud)
不幸的是,我无法进行callCC类型检查,也不知道该怎么做.我设法制作shift并reset打字检查
reset :: Monad m => …Run Code Online (Sandbox Code Playgroud) 最近,我一直在调查Scheme和Common Lisp之间关于这两种语言对延续的方法的区别.
我注意到Common Lisp方法比Scheme方法更保守.
此外,Scheme提供了一个原始的call-with-current-continuation,通常是缩写的call/cc,它在ANSI Common Lisp规范中没有等价物(尽管有一些库试图实现它们).
有没有人知道为什么决定不在ANSI Common Lisp规范中创建类似的原语?
提前致谢.
我一直在关注asyncc#5.0中新功能的新公告.我对继续传递样式以及新的c#编译器对Eric Lippert的帖子中的代码片段所做的转换有基本的了解:
async void ArchiveDocuments(List<Url> urls)
{
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
}
Run Code Online (Sandbox Code Playgroud)
我知道有些语言通过call-with-current-continuation(callcc)本地实现continuation ,但我真的不明白它是如何工作的或它究竟是做什么的.
所以这就是问题:如果安德斯等人.已经决定咬紧牙关,只是callcc在c#5.0而不是async/ await特殊情况下实现,上面的代码片段会是什么样子?
有谁知道是否call/cc可以用lambdas和闭包实现?
它似乎会call/cc中断程序的流程(就像异常一样),但lambdas和闭包不能这样做.因此我认为call/cc无法通过lambdas和闭包实现.
还有什么想法吗?
callcc ×10
scheme ×5
haskell ×2
lisp ×2
async-await ×1
asynchronous ×1
c# ×1
common-lisp ×1
javascript ×1
lambda ×1
lua ×1