Lua 中的 call/cc - 可能吗?
维基百科关于Continuation的文章说:
“在任何支持闭包的语言中,都可以以连续传递的方式编写程序并手动实现call/cc。”
要么这是真的,我需要知道如何去做,要么这不是真的,需要更正该说法。
如果这是真的,请告诉我如何在 Lua 中实现 call/cc,因为我不知道如何实现。
我想如果 Lua 有 coroutine.clone 函数,我就可以手动实现 call/cc,如 此处。
如果闭包不足以实现 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)))
; define f function non-CPS
(define (f return)
(return 2)
3)
; these are my past attempts at defining a CPS f function
;(define (cps-f return k)
; (k (return 2)) 3)
;(define (cps-f return k)
; (k (lambda ()
; (return 2)
; 3)))
; this is what I came up with - I'm not sure if this is correctly CPS-transformed but I believe so
(define (cps-f return k)
(return 2)
(k 3))
; call the non-CPS f function
(display (f (lambda (x) x))) ; displays 3
(newline)
; call the non-CPS f function with call/cc (I don't understand what this does)
(display (call/cc f)) ; displays 2
(newline)
; now call the CPS version of the f function
(cps-f (lambda (x) x) main-continuation) ; displays --> 3
; now call the CPS version of the f function with the CPS version of call/cc
(cpscallcc cps-f main-continuation) ; displays --> 2 but then it also displays --> 3 afterwards -> I'm not sure why it displays the 3 afterwards, as it should only display the 2 just like the non-CPS versions above
Lua版本
-- callcc CPS-version
cpscallcc = function(consumer, k)
local cc = function(result)
return k(result) -- ?or k(result)
end
return consumer(cc, k) -- ?or return consumer(cc,k)
end
-- define f function non-CPS
f = function(ret)
ret(2)
return 3
end
-- define f function CPS-version (again, not sure this is correct)
cps_f = function(ret, k)
ret(2)
k(3)
end
-- call the non-CPS f function
print(f(function(x) return x end))
-- we cant call the non-CPS f function with callcc because
-- Lua doesnt have callcc, but the line below displays the correct expected output (maybe by accident)
--cpscallcc(f, print)
-- now call the CPS version of the f function
cps_f( function(x) return x end, print ) -- displays 3
; now call the CPS version of the f function with the CPS version of call/cc
cpscallcc( cps_f, print) -- displays 2 and then 3 just like the Scheme version!!
-- so apparently the translation from Scheme to Lua is correct...
我正在使用 Windows 版的 DrScheme 和 Lua - 对于任何想要提供帮助的人来说,这是两个易于下载和安装的实用工具。
The Wikipedia article on Continuation says:
"In any language which supports closures, it is possible to write programs in continuation passing style and manually implement call/cc."
Either that is true and I need to know how to do it or it is not true and that statement needs to be corrected.
If this is true, please show me how to implement call/cc in Lua because I can't see how.
I think I'd be able to implement call/cc manually if Lua had the coroutine.clone function as explained here.
If closures are not enough to implement call/cc then what else is needed?
The text below is optional reading.
P.S.: Lua has one-shot continuations with its coroutine table. A coroutine.clone function would allow me to clone it to call it multiple times, thus effectively making call/cc possible (unless I misunderstand call/cc). However that cloning function doesn't exist in Lua. Someone on the Lua IRC channel suggested that I use the Pluto library (it implements serialization) to marshal a coroutine, copy it and then unmarshal it and use it again. While that would probably work, I am more interested in the theoretical implementation of call/cc and in finding what is the actual minimum set of features that a language needs to have in order to allow for its manual implementation.
EDIT 1: Ok people, help me out here, this took me a long time because I don't know any Scheme, but I came up with something that should help us out. Please look at the codes below. The first one is a program in Scheme, the second one is the same program but in Lua.
Hopefully this will help us out. I believe we are very close.
P.S.: These examples are taken from the first example on the Wikipedia article on CallCC.
Scheme version
(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)))
; define f function non-CPS
(define (f return)
(return 2)
3)
; these are my past attempts at defining a CPS f function
;(define (cps-f return k)
; (k (return 2)) 3)
;(define (cps-f return k)
; (k (lambda ()
; (return 2)
; 3)))
; this is what I came up with - I'm not sure if this is correctly CPS-transformed but I believe so
(define (cps-f return k)
(return 2)
(k 3))
; call the non-CPS f function
(display (f (lambda (x) x))) ; displays 3
(newline)
; call the non-CPS f function with call/cc (I don't understand what this does)
(display (call/cc f)) ; displays 2
(newline)
; now call the CPS version of the f function
(cps-f (lambda (x) x) main-continuation) ; displays --> 3
; now call the CPS version of the f function with the CPS version of call/cc
(cpscallcc cps-f main-continuation) ; displays --> 2 but then it also displays --> 3 afterwards -> I'm not sure why it displays the 3 afterwards, as it should only display the 2 just like the non-CPS versions above
Lua version
-- callcc CPS-version
cpscallcc = function(consumer, k)
local cc = function(result)
return k(result) -- ?or k(result)
end
return consumer(cc, k) -- ?or return consumer(cc,k)
end
-- define f function non-CPS
f = function(ret)
ret(2)
return 3
end
-- define f function CPS-version (again, not sure this is correct)
cps_f = function(ret, k)
ret(2)
k(3)
end
-- call the non-CPS f function
print(f(function(x) return x end))
-- we cant call the non-CPS f function with callcc because
-- Lua doesnt have callcc, but the line below displays the correct expected output (maybe by accident)
--cpscallcc(f, print)
-- now call the CPS version of the f function
cps_f( function(x) return x end, print ) -- displays 3
; now call the CPS version of the f function with the CPS version of call/cc
cpscallcc( cps_f, print) -- displays 2 and then 3 just like the Scheme version!!
-- so apparently the translation from Scheme to Lua is correct...
I'm using DrScheme and Lua for Windows - for anyone that wants to help up out those are two easy to download and install tools that just work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
根据维基百科的引用,手动实现 call/cc 有两个先决条件:
我怀疑您不会喜欢#2。
要以延续传递风格编写程序:
因此,使用
k
作为延续参数的名称,函数将如下所示:可能使用
print
作为其延续,因此在顶层调用multiplyadd
看起来像:使用该脚手架,我们可以将 call/cc 定义为
注意上面的
multiplyadd< /code> 实际上作弊,因为
*
和+
不在 CPS 中。以 CPS 形式添加所有运算符,用 CPS 等效项替换所有 Lua 库函数,并将所有代码翻译/生成为 CPS 是非常繁琐的;请参阅此处的详细信息。There are two prerequisites to manually implement call/cc per the Wikipedia quote:
I suspect you will not like #2.
To write your program in continuation passing style:
So, using
k
as the name of the continuation argument, a function would look like:The toplevel might use
print
as its continuation, so invokingmultiplyadd
at top level would look like:With that scaffolding we could define call/cc as
Note that the above
multiplyadd
actually cheats since*
and+
are not in CPS. It is very tedious to add all the operators in CPS form, replace all the Lua library functions with CPS equivalents, and translate/generate all your code to CPS; see details here.我猜你忘记了关于在连续传递中编写程序的部分
风格。一旦你这样做了, call/cc 就变得微不足道了(在 Lua 或任何其他语言中),
因为延续将是所有函数的显式参数(call/cc
包括)。
PS:除了闭包之外,您还需要对连续程序进行适当的尾部调用
传递风格。
I guess you forgot the part about writing your program in continuation passing
style. Once you do that, call/cc is trivial (in Lua or in any other language),
as the continuation will be an explicit parameter to all functions (call/cc
included).
PS: besides closures, you also need proper tail calls to program in continuation
passing style.
回答关于Lua中call/cc计划的问题: Lua中没有call/cc计划。捕获延续要么成本太高,要么需要一些远远超出 Lua 编译器能力范围的代码分析。还有一个问题是 Lua 延续可能包含 C 中的部分。
但是,通过协程,我们已经可以在 Lua 中实现 call/cc1 (一次性延续)。这对于延续的许多用途来说已经足够了。
Answering the question about plans for call/cc in Lua: There are no plans for call/cc in Lua. Capturing a continuation is either too expensive or require some code analsis well beyond what the Lua compiler can do. There is also the problem that Lua continuations may include parts in C.
With coroutines, however, we can already implement call/cc1 in Lua (one-shot continuations). That is good enough for many uses of continuations.
关键短语是
(重点是我的。)您可以通过采用常规“直接风格”程序并通过程序转换将它们转换为连续传递风格(CPS)来实现这一点称为CPS 变换。关键是
call/cc
的CPS变换是一个简单的函数。。 CPS 变换有两个用途:
这对程序员来说不切实际 想要对 Lua 代码进行 CPS 转换,尤其是不要手动进行。
The key phrase is
(Emphasis mine.) You do this by taking regular "direct-style" programs and converting them to continuation-passing style (CPS) by a program transformation called the CPS transform. The key is that the CPS transform of
call/cc
is a simple function.This is not practical for programmers. The CPS transform has two uses:
You don't want to go anywhere near doing CPS transforms on Lua code, especially not by hand.
有可能:
TypeScript 到 Lua 编译器 https://github.com/roblox-ts/roblox-ts< /a> +
JS 中的 call-cc https://github.com/zaoqi/callcc .js/blob/master/callcc.js
it's possible:
TypeScript-to-Lua Compiler https://github.com/roblox-ts/roblox-ts +
call-cc in JS https://github.com/zaoqi/callcc.js/blob/master/callcc.js
这是我的 cps-convert 方案,只需将您想要转换的每个函数传递给它即可。
Here's my cps-convert in scheme, just pass it every function you want to convert.