継続
出典: フリー百科事典『ウィキペディア(Wikipedia)』
プログラミングにおいて継続(けいぞく、continuation、コンティニュエーション)とは、ある計算過程のある瞬間における、その過程の未来全体(デフォルト)を表すもの、あるいは計算過程の実行スナップショットと説明される。
[編集] 概念
継続は、実行中のどんな計算機プログラムにも存在する。もっとも単純な形の継続は、アセンブラレベルのプログラムで考えるとわかりやすい。この場合、継続とは「ある時点でのPC(プログラム・カウンタ)の値と、その後実行されるであろう命令の順列」と定義することができる。C言語においては、関数setjmpで補足されるデータjmp_bufが継続の概念に近いと考えることができるが、実際には継続はCプログラム中のあらゆる場所に現れる。たとえば
a = b + (c * f(x));
という代入文を考える。この場合、各項の値を計算する箇所 (および、代入演算子 = ) における継続を考えることができる。 この式で最初に値が計算されるのは c および f(x) の 2項であるが、ここでは関数 f(x) を呼び出す部分で、すでにその返り値を待っている「継続」が存在している。また、c と f(x) の積を求める過程でも、その値を受けとるべき「継続」が存在していることになる。最終的にこの値は変数 b に加えられ a に代入されるわけだが、ここでも「値を a に代入する」という、その瞬間の継続が存在する。
しかし継続の概念は、ほとんどの (Scheme や Ruby を除く) プログラミング言語では明示的に扱われていない。
多くの標準的プログラミング言語では、関数(メソッド、サブルーチン)からの「戻り」「リターン」という概念を持つ。これはサブルーチン呼び出しが、呼びだし元のコンテキストを常に保存する局所的なスタック処理だからである。
しかし、あるサブルーチン呼び出しが呼びだし元(親関数)の終端文脈で行われた場合(つまり呼びだし元はサブルーチンから戻り次第自らもリターンするような場合)、サブルーチンは親関数を迂回せず、親関数の親関数(それが存在するとして)へと直接リターンしても計算結果自体は同じになるはずである。このような場合親関数と戻り先が異なるので「戻る」という概念は不適当で、たった今の計算結果を元に次の計算を「継続する」と考えるのが自然だろう。
実際には終端文脈に関わらず任意の計算過程で上のような「継続」を考えることができる。以上の内容を抽象化したものがすなわち継続である。
継続概念をもつプログラミング言語では機能の呼びだし元と戻り先が異なっていても構わないため、構造化プログラミングの通常のサブルーチン呼び出しより強く、しかし事前に継続先を取り出した所へしか進めないという制約がある分、無条件goto文よりも安全な制御が可能となる。したがって継続は洗練された大域ジャンプ命令とも捉えられる。
[編集] 継続の応用
継続応用の主な例は構造化例外処理の実装である。Ada、C++、Javaなど、例外処理を言語組み込みレベルでサポートするケースは時折見受けられるが、継続を持つプログラミング言語ではそこから例外を導出できるため組み込みのサポートが不要となる。
他には深い条件判定からの脱出(いわゆるCでのエスケープgoto文)などがある。
最近では Web アプリケーションにおける継続の明示的な利用がプログラミング効率を上げるものとして注目を集めている。通常、Webサーバではユーザからの HTTP リクエストは完全に独立したものとして扱われており、したがってサーバ上で走っているプログラムは個々のリクエストを独立した計算過程として完了しなければならなかった。しかし、多くの Web アプリケーションでは『ログイン』や『買い物カゴへの追加』など、あたかもサーバ上で連続した状態を保持しているかのような機能をユーザに対して提供する必要がある。従来のWebプログラミングでは、これは一連のリクエストをいくつかの状態に分割してサーバ上に (あるいはクッキーなどで)保存しておき、各リクエストごとにそこから復帰するという手法が一般的だったが、このようなプログラミングは複雑になりがちで、バグも起こりやすかった。しかし継続をサーバ上に保持できれば、プログラマは状態の分割をなにも考えずにあたかもユーザと 1対1で通信しているかのようなコードを書くことができる。これにより複雑な Web アプリケーションがより簡単に(バグも少なく)書けるようになると期待されている。
[編集] Schemeにおける継続
プログラミング言語Schemeは継続をファーストクラスとして取り入れた代表的な言語で、call-with-current-continuation (call/cc) によりプログラム中の任意の位置における継続を取り出し、それをオブジェクトとして扱うことが可能である。したがって「ある時点での状態」を変数に束縛して、後からそこへ戻るといったことも可能になる。Scheme の継続は関数の形をとっており、これは「その関数を呼ぶことにより、そこに渡された引数を call/cc で囲まれている式全体の値として継続に“注入”できる」と定義されている。