呼叫/ cc在Lua - 可能吗?

时间:2010-05-13 14:33:49

标签: lua continuations callcc

关于Continuation的维基百科文章说:
“在任何支持闭包的语言中,都可以用延续传递方式编写程序,手动实现调用/ cc 。”

这是真的,我需要知道如何做或不是真的,这个陈述需要纠正。

如果这是真的,请告诉我如何在Lua中实现call / cc,因为我看不出如何。

我认为如果Lua具有coroutine.clone功能,我可以手动实现call / cc,如here所述。

如果闭包不足以实现call / cc那么还需要什么呢?

下面的文字是可选阅读。
P.S。:Lua与其协程表一次性延续。一个coroutine.clone函数允许我克隆它多次调用它,从而有效地使call / cc成为可能(除非我误解了call / cc)。但是Lua中不存在克隆功能。 Lua IRC频道上的某个人建议我使用Pluto库(它实现序列化)来编组协程,复制它然后解组它并再次使用它。虽然这可能会奏效,但我对call / cc的理论实现以及查找语言需要具有的实际最小特征集以便实现其手动实现感兴趣。

编辑1:好的人,帮我在这里,这花了我很长时间,因为我不知道任何计划,但我想出了一些应该帮助我们的东西。请看下面的代码。第一个是Scheme中的程序,第二个是相同的程序,但在Lua中。希望这会帮助我们。我相信我们非常关闭。

P.S。:这些例子来自the Wikipedia article on 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...



我正在使用DrScheme和Lua for Windows - 对于任何想要帮助它们的人来说,这两个版本都是易于下载和安装的工具。

6 个答案:

答案 0 :(得分:19)

根据Wikipedia引用手动实现call / cc有两个先决条件:

  1. 语言必须支持闭包
  2. 你必须用延续传递方式(CPS)编写程序
  3. 我怀疑你不喜欢#2。

    以延续传递方式编写程序:

    1. 每个函数都必须采用延续参数
    2. 函数必须通过调用它们的继续来返回
    3. 因此,使用k作为continuation参数的名称,函数将如下所示:

      function multiplyadd(k, x, y, z) return k(x * y + z) end
      

      toplevel可能会使用print作为其延续,因此在顶层调用multiplyadd将如下所示:

      multiplyadd(print, 2, 4, 1)
      

      使用该脚手架,我们可以将call / cc定义为

      function callcc(k,f) return f(k,k) end
      

      请注意,上述multiplyadd实际作弊,因为*+不属于CPS。以CPS形式添加所有运算符,用CPS等价物替换所有Lua库函数,并将所有代码转换/生成到CPS是非常繁琐的;见details here

答案 1 :(得分:17)

我猜你忘记了在继续传递中编写程序的部分 样式。一旦你这样做,呼叫/ cc是微不足道的(用Lua或任何其他语言), 因为continuation将是所有函数的显式参数(call / cc 包括在内)。

PS:除了闭包之外,你还需要适当的尾调用来继续编程 传递风格。

答案 2 :(得分:7)

回答有关Lua电话/ cc计划的问题:Lua没有打电话/ cc的计划。捕获延续要么过于昂贵,要么需要一些代码分析,远远超出Lua编译器的功能。还有一个问题是Lua延续可能包括C中的部分。

然而,使用协同程序,我们已经可以在Lua中实现call / cc1(一次性连续)。这对延续的许多用途来说已经足够了。

答案 3 :(得分:2)

关键词是

  

可以用延续传递样式

实现程序

(强调我的。)你通过定期的“直接式”程序并通过名为 CPS转换的程序转换将它们转换为延续传递方式(CPS)来实现这一点。关键是call/cc的CPS变换是一个简单的函数。

这对程序员来说不实用。 CPS转换有两个用途:

  • 作为研究语言特征的理论思路,特别是控制操作员
  • 作为使用CPS作为中间语言的编译器的传递

您不希望在Lua代码上进行CPS转换,尤其不是手动转换。

答案 4 :(得分:0)

答案 5 :(得分:-2)

这是我的cps-convert in scheme,只需将它传递给你想要转换的每个函数。

(define (cps-convert function . functions)
  # Since "help" is called at 2 different places...
  (define (help) (error "syntax: (cps-convert f1 f2 ...)"))
  # Single function converter
  (define (convert func)
    # "name" contains the function's name prefixed with "cps-"
    (let ([name (string->symbol
                          (string-append "cps-" (symbol->string func)))])
      # Dirty hack to define "cps-*" in the global environment
     `(eval '(begin
                   # Necessary to prevent the function from being evaluated
                   (define ,name #f)
                                # Magic
                   (set! ,name (lambda (k . args) (k (func args)))))
                 # Global environment
                 (interaction-environment))))
  # Prerequisite... Call help if condition not met
  (if (symbol? function)
      # function is a symbol
      (cond
        # If there is only one function to convert
        [(null? functions) (convert function)]
        # Else ensure every other "functions" are symbols and convert each
        [(every symbol? functions) (apply convert function functions)]
        # Oops! Condition not met!
        [else (help)])
      # Else clause from previous "if"
      (help)))