出典(authority):フリー百科事典『ウィキペディア(Wikipedia)』「2013/04/02 15:01:38」(JST)
この項目では、プログラムの継続について記述しています。「継続」の語義については、ウィクショナリーの「継続」の項目をご覧ください。 |
計算機科学における継続(けいぞく、continuation)とは、プログラムの実行においてある時点において評価されていない残りのプログラム(the rest of the program)を意味するものであり、手続き(procedure)として表現されるものである。
目次
|
次のようなBASICプログラムについて考える。
10 GOTO 40 20 PRINT "WORLD" 30 STOP 40 PRINT "HELLO" 50 GOTO 20 60 END
このgoto文を用いたBASICプログラムは以下のように手続きを用いて書きなおすことができる。
100 SUB L1 110 PRINT "WORLD" 120 STOP 130 END SUB 140 SUB L2 150 PRINT "HELLO" 160 CALL L1 170 END SUB 180 CALL L2 190 END
継続の骨子は、このようにgoto文などの制御構文を手続きの呼び出しとして解釈することである。例えば、書きなおす前のBASICプログラムの50行を評価した時点における継続は具体的に表現する事は困難であるが、下のBASICプログラムであれば、単純に"HELLO"を表示した後に呼び出される手続き L1 と具体的に表現することができるようになる。
上記説明における制御構文を手続き(procedure)として表現するという手法を用いると、begin文に限定的な継続を導入することが容易に可能となる。
次に示すreset-beginマクロはbegin文と基本的挙動は同一であるが、shift-begin文を用いることによって、shift-begin行以下の継続を扱うことができる。
(define-syntax reset-begin (syntax-rules (shift-begin) ((_) #f) ((_ exp) exp) ((_ (shift-begin k body ...) exp2 ...) (let ((programpoint (lambda (res) (reset-begin exp2 ...)))) ((lambda (k) body ...) programpoint))) ((_ exp1 exp2 ...) (let ((res exp1)) (reset-begin exp2 ...)))))
上記マクロを用いた例
> (reset-begin (display "1\n") (display "2\n") (shift-begin cont (cont 0) (display "3 shifted\n")) (display "4\n") (shift-begin cont (display "5 exit\n")) (display "6\n") (display "END\n")) 1 2 4 5 exit 3 shifted
複数の段階から成るあらゆる計算において、「継続」は存在している。まず、簡単な例として、算術の計算を考えてみよう[1]。
以下のような式を評価して、値を得るという計算を考える。
(((((1 + 2) + 3) + 4) + 5) + 6)
ちょうど「3を足す」という計算が終わったところだとする。
(((6 + 4) + 5) + 6)
この時、「4を足す」というのが次にするべき計算で、さらにその継続は「5を足して、さらに6を足す」というものである。
このように、コンピュータプログラムに限らず、あらゆる計算に継続は存在している。
サブルーチンの呼び出しや割り込みの際には、プログラムの元の部分から実行を再開するための情報がスタックに積まれ、必要な処理が終わるとスタックからそれを戻して実行を再開するが、この際の「実行を再開するための情報」も継続の一種である。
C言語では、setjmp関数で継続を保存し、longjmp関数でその継続に飛ぶことができるが、関数呼び出しの入れ子が浅くなる側にしか飛ぶことができないという制限がある。
Schemeでは、call-with-current-continuation(en:call-with-current-continuation、しばしばcall/ccと略される)という関数により、「現在の継続」を明示的に取り出し、プログラムで扱うことができる(継続が第一級オブジェクトである、という)。継続を、関数のようにして呼び出すと、その引数を返戻値として、call-with-current-continuationから戻る。
RubyのC言語による実装(MRIとも呼ばれる)にも、同様のcallccというメソッドがある(バージョン1.9ではcontinuationライブラリをrequireする必要がある)。またMozillaによるJavaScript処理系の実装であるRhinoにも継続のサポートがある[2]。
たとえばC言語ではsetjmp/longjmpで実装するような、あるいはAda、C++、Javaなどの高水準プログラミング言語では言語機能として例外処理をサポートしているが、継続を扱うことができれば、同様の「今やっている計算を打ち切り、ネストの外側に値と処理を返す」、というふるまいをプログラムできる。
Webアプリケーションにおいて、継続の利用が開発効率を上げるとして、継続を利用するスタイルでのWebアプリケーションの開発がおこなえるフレームワークが開発されており、Kahuaなどの実装例がある。通常、Webサーバではユーザからの HTTP リクエストは完全に独立したものとして扱われており、したがってサーバ上で走っているプログラムは個々のリクエストを独立した計算過程として完了しなければならなかった。しかし、多くの Web アプリケーションでは『ログイン』や『買い物カゴへの追加』など、あたかもサーバ上で連続した状態を保持しているかのような機能をユーザに対して提供する必要がある。従来のWebプログラミングでは、これは一連のリクエストをいくつかの状態に分割してサーバ上に (あるいはクッキーなどで)保存しておき、各リクエストごとにそこから復帰するという手法が一般的だったが、このようなプログラミングは複雑になりがちで、バグも起こりやすかった。しかし継続をサーバ上に保持できれば、プログラマは状態の分割をなにも考えずにあたかもユーザと 1対1で通信しているかのようなコードを書くことができる。これにより複雑な Web アプリケーションがより簡単に(バグも少なく)書けるようになると、それらのフレームワークの開発者は主張している。
例外のようなスタックの巻き戻しのような処理ばかりではなく、コルーチンのように相互に呼び出し合うような機構も継続を使って実装できる(なお、コルーチンの実装には継続の持つ能力の全ては必要ではなく、また性能や機能の理由から、スレッドやファイバーで実装されるほうが多い)。
詳細は「継続渡しスタイル」を参照
現在主流である、LIFOの関数呼び出しではなく、全ての関数呼び出しに、その関数が終わった後実行されるべき継続を、明示的に渡す、というプログラムのスタイルがあり、プログラマが書くコードとしてだけではなく、プログラミング言語処理系の中間表現としても使われており研究されている。
Schemeでは前述のように、継続が第一級オブジェクトであり、また簡単に実行中のコンテキストから継続を取り出して使うことができる。そればかりではなく、Schemeは仕様においてプログラム意味論が与えられているが、そこでも継続を利用して定義がおこなわれている。
Scheme以前から、プログラムの理論として継続は意識されており[3]、SECDマシンの「D」は継続そのものである。また、手続き型(命令型)プログラミング言語の表示的意味論でも、gotoなどの意味を定めるために継続を使う。
計算の残り全体を扱う継続は、扱いづらいのみならず、一種の副作用のようなふるまいをする[4]。
これに対し、限定継続(en:delimited continuation・restricted ~、partial continuation 部分継続などとも)は、継続のうち、ある所までの計算を区切って取り出すことができる、というもので、近年研究や実装が進められている。限定継続に対し(ふつうの)継続をundelimited continuationなどと言う。
shiftとresetと呼ばれるプリミティブにより、限定された継続が作られる。Schemeでcall/ccを使った実装例が、論文 "Final shift for call/cc:: direct implementation of shift and reset" 中の§2のサーベイ中に示されている。
全文を閲覧するには購読必要です。 To read the full text you will need to subscribe.
リンク元 | 「100Cases 16」「連続」「持続」「continuity」「連続性」 |
拡張検索 | 「継続的」「無作為プラセボ非継続試験」「継続性」 |
[★] randomized discontinuation trial RDT
.