21

ウィキペディアの継続に関する記事には次のように書かれ
ます。 それが真実であり、その方法を知る必要があるか、それとも真実ではなく、そのステートメントを修正する必要があります. これが本当なら、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: OK 人々、ここで私を助けてください。Scheme をまったく知らないので、これには長い時間がかかりましたが、私たちを助ける何かを思いつきました。以下のコードを見てください。1 つ目は Scheme のプログラムで、2 つ目は同じプログラムですが 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



ルア版

-- 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 を使用しています。この 2 つは、簡単にダウンロードしてインストールできるツールで、これらを支援したいと考えている人なら誰でも使用できます。

4

6 に答える 6

18

ウィキペディアの引用に従って call/cc を手動で実装するには、2 つの前提条件があります。

  1. 言語はクロージャーをサポートする必要があります
  2. 継続渡しスタイル (CPS) でプログラムを作成する必要があります。

#2が気に入らないと思います。

継続渡しスタイルでプログラムを書くには:

  1. すべての関数は継続引数を取る必要があります
  2. 関数は、その継続を呼び出すことによって返さなければなりません

したがって、k継続引数の名前として使用すると、関数は次のようになります。

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

printトップレベルはその継続として使用する可能性があるためmultiplyadd、トップレベルでの呼び出しは次のようになります。

multiplyadd(print, 2, 4, 1)

その足場を使用して、call/cc を次のように定義できます。

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

上記multiplyaddは実際にはチートで*あり+、CPS には含まれていないことに注意してください。すべての演算子を CPS 形式で追加し、すべての Lua ライブラリ関数を CPS と同等のものに置き換え、すべてのコードを CPS に変換/生成するのは非常に面倒です。詳細はこちらをご覧ください。

于 2010-05-13T16:28:08.997 に答える
17

継続渡しスタイルでプログラムを書くことについての部分を忘れていたと思います。これを行うと、継続はすべての関数 (call/cc を含む) に対する明示的なパラメーターになるため、call/cc は (Lua または他の言語で) 自明です。

PS: クロージャーに加えて、継続渡しスタイルでプログラムするための適切な末尾呼び出しも必要です。

于 2010-05-13T16:04:12.657 に答える
8

Lua での call/cc の計画に関する質問への回答: Lua での call/cc の計画はありません。継続の取得はコストがかかりすぎるか、Lua コンパイラーが実行できる範囲をはるかに超えるコード分析が必要になります。また、Lua の継続に C の部分が含まれる可能性があるという問題もあります。

ただし、コルーチンを使用すると、既に call/cc1 を Lua で実装できます (ワンショット継続)。これは、継続の多くの使用には十分です。

于 2010-05-14T16:56:09.723 に答える
2

キーフレーズは

継続渡しスタイルでプログラムを実装することが可能です

(Emphasis mine。)これを行うには、通常の「直接スタイル」プログラムを使用し、 CPS変換と呼ばれるプログラム変換によって継続渡しスタイル(CPS)に変換します。重要なのは、のCPS変換がcall/cc単純な関数であるということです。

これはプログラマーにとっては実用的ではありません。 CPS変換には2つの用途があります。

  • 言語機能、特に制御演算子を研究するための理論的アイデアとして
  • 中間言語としてCPSを使用するコンパイラーのパスとして

特に手作業ではなく、LuaコードでCPS変換を実行することは避けたいと思います。

于 2010-05-13T21:27:49.330 に答える
-2

これがスキームの私の cps-convert です。変換するすべての関数を渡すだけです。

(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)))
于 2011-11-21T16:29:15.770 に答える