連載
» 2009年02月20日 00時00分 UPDATE

Gaucheでメタプログラミング(5):継続を使ったコントローラを作る (1/3)

Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう(編集部)

[吉田裕美,有限会社イーワイオフィス]

 第4回「Gaucheでテンプレートエンジンを作る」では、MVCのビューの部分を作成しました。今回は、継続を使ったコントローラ部分の作成を行い、Webアプリケーションを完成させます。

 Webアプリケーションのコントローラの解説に入る前に、プログラムの処理の流れについて少し考えてみます。

 例えば、2つの数を入力して足した結果を表示するコンソールアプリケーションであれば、以下のように変数を2つ用意し、2つの数値を入力した後で足した結果を表示するというシンプルなプログラムになります。

(define (console-add)
  (let ((a 0) (b 0))
    (display "Aを入力して下さい: ") (flush)
    (set! a (read))
    (display "Bを入力して下さい: ") (flush)
    (set! b (read))
    (display "A + B は ")
    (print (+ a b))))

 しかし、皆さんが書いているようなWebアプリケーションではどうでしょうか。1つのページにAとB、2つの入力欄があれば簡単ですが、1ページ目でAを入力し、2ページ目でBを入力し、3ページ目に表示するようなアプリケーションの場合は、1ページで入力したAの値を2ページ目の入力の間も保持するために、セッションなどの特別な場所に格納するか、2ページ目にhiddenタグを使って埋め込んでおく必要があります。

 これは、リクエストごとに起動されるCGIはもちろんですが、アプリケーションサーバ上に常駐して動くアプリケーションの場合も同じです。なぜ、上のコンソールアプリケーションのようにシンプルに書けないのでしょうか。

クロージャ

 ここでまた、Schemeの勉強をしましょう。次のようなadd-nという関数を定義します。

(define (add-n n)
  (lambda(x) (+ x n)))

 この関数を使ってadd3を定義します。

(define add3 (add-n 3))

 add3を実行してみましょう。

gosh> (add3 5)
8 

 add3は引数に3を足す関数になっています。同じようにadd4を定義して使ってみると、add4は引数に4を足す関数になっています。

gosh> (define add4 (add-n 4))
add4
gosh> (add4 5)
9 

 ところで、最初に定義したadd-nは何でしょうか。定義を見ると、lambda式を戻す関数です。しかも、引数nが足し算の片方に使われています。

 従って、add3は(lambda(x) (+ x 3))となり、引数xを受け取り、3を足します。しかも、add3の定義で使った3が、ずっとその後でもどこかに記憶されていて、いつでも3を足す関数として使えます。

 このような機構は、クロージャ(Closure)と呼ばれています。Schemeでは、lambda式は実行するプログラム(上の例では(+ x n))だけではなく、定義した際の環境(ここではnの値)も一緒になっているのです。

 従って、add3には(lambda(x) (+ x n))とn = 3の環境が一体化して記憶されているのです。

継続ベースのアプリケーション

 さて、ここで最初のA+Bのプログラムに戻りますが、クロージャを使って以下のように書き換えてみました。

(define cont #f)
 
(define (continuation-add)
  (let ((a 0) (b 0))
    (cond (cont (cont) (set! cont #f))
    (else
     (display "Aを入力して下さい: ") (flush)
     (set! a (read))
     (set! cont
           (lambda()
             (display "Bを入力して下さい: ") (flush)
             (set! b (read))
             (display "A + B は ")
             (print (+ a b))))))))

 これを実行すると、

gosh> (continuation-add)
Aを入力して下さい: 2
#<closure (continuation-add continuation-add)>
gosh> (continuation-add)
Bを入力して下さい: 3
A + B は 5

となります。このプログラムは、Aを入力したところで終了してしまいますが、再度実行するとBの入力と計算結果の表示が行われます。Webアプリケーションに似ていると思いませんか。

 プログラムを説明すると、contという広域変数に値がある場合はそれを関数として実行し、cont変数をfalseに戻します。contがfalseの場合は、Aを入力し、B入力以降の処理をクロージャとしてcont変数に代入しています。

 ここで特筆すべきことは、aの値は明示的に保存してないにもかかわらず保存されていて、2回目の実行で使えることです。また、(set! cont (lambda()などの余分なコードが入っていますが、処理の大部分は最初に説明したシンプルなプログラムそのままです。

 このようなスタイルのプログラムは、継続ベースのアプリケーションと呼ばれています。schemeの継続という言葉は、厳密にはここに書いたものとは異なる側面もありますが、ここではこれ以上は追求しないことにします。

 このプログラムでは、継続すべき処理を1つの広域変数にしまっているので現実的なWebアプリケーションには使えません。そこで、次項ではもう少し実用に近い継続ベースのWebアプリケーションを作ってみます。

       1|2|3 次のページへ

Copyright© 2017 ITmedia, Inc. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。