フォーム

読者のみなさんは、これまで見てきたSchemeの例題プログラムも やはりS式であるとに気づくことになるでしょう。このことは 全てのSchemeプログラムに言えることです。プログラムはデータです。

すなわち、文字データ #\cはプログラムあるいは フォームです。プログラムというかわりに フォームというより一般的な言葉をつかいます。 こうすることにより、プログラムの断片もとりあげることが できます。

Schemeはフォーム#\cを評価して、値#\cとします。 これは、#\cは自己評価的であるからです。すべての S式が自己評価的であるわけではありません。たとえば、 シンボルのS式xyzは評価するのと変数xyzが 保持していた値になります。リストのS式(string->number "16")は 評価すると数値の16になります。

すべてのS式が正しいプログラムであるとはかぎりません。 ドット対のS式(1 . 2)をSchemeのリスナーから入力すると エラーになるでしょう。

Schemeはリストフォームを評価するとき、そのフォームの最初の要素、 先頭を検査します。先頭を評価して手続きでなれば、フォームの のこりの部分を評価して、その引数の値を得たうえで、手続きをその 引数に適用します。

もし、上のフォームの先頭がスペシャルフォームであったら、 評価はそのフォームに特有の方法で進みます。 いくつかのスペシャルフォームはすでに見てきました。 begindefineset!です。beginはその サブフォームを順に評価し、最後のサブフォームの結果の値を 全体の結果のとして返します。defineは変数を導入し、それを 初期化します。set!は変数の束縛を変更します。

3.1  手続き

すでに見てきた手続きは、consstring->listなどのような Schemeのプリミティブな手続きでした。ユーザはスペシャルフォーム lambdaをつかって、独自の手続きをつくりだすことができます。 たとえば、次のものは、引数に2を加える手続きを定義しています。

(lambda (x) (+ x 2))

最初のサブフォーム、(x)はパラメータのリストです。 のこりのサブフォームは手続きの本体を構成するものです。 この手続きは一つの引数に対して呼ぶことができ、プリミティブ 手続きと同じように振舞います。

((lambda (x) (+ x 2)) 5)
=>  7

もし、この同じ手続きを何度も呼びたければ、lambdaの複製を そのたびに呼んでもいいのですが、もっとよい方法があります。 この手続きの値を保持する変数を使うことができます。

(define add2
  (lambda (x) (+ x 2)))

こうすると、この変数add2を引数に2を足す必要があるたびに 呼びだすことができます。

(add2 4) =>  6
(add2 9) =>  11

3.1.1  手続きパラメータ

lambda手続きのパラメータは最初のサブフォーム(先頭、シンボル lambdaの直後)で指定します。add2は単一引数(単項)手続き なのでパラメータリストはシングルトンリスト(x)です。シンボルxは 手続き引数を保持する変数として振舞います。変数xは手続き本体について 局所的(ローカル)であるといいます。

2引数手続きについては2要素リストを使うことができます。 一般にn引数手続きについてはn要素リストを使えます。 次の例は長方形の面積を計算する2引数手続きです。 ふたつの引数は長方形のたてとよこです。

(define area
  (lambda (length breadth)
    (* length breadth)))

areaはその引数をかけあわせていますので、プリミティブ手続き *と同じです。単に

(define area *)

と書いてもよかったわけです。

3.1.2  可変長の引数

手続きのなかにはときに異った数の引数をとるものがあります。 このようにするためにはlambdaパラメータリストを単一の シンボルに置き換えます。このシンボルはその手続きが呼ばれる 引数のリストとして振舞います。

一般的には、このlambdaパラメータリストは(x ...)という フォームあるいは、単一のシンボルまたはドット対(x ... . z)という フォームです。ドット対の場合、ドットの前のすべての変数は 手続き呼び出しで、対応するすべての引数に束縛され、ドットの 後の一個の変数は、のこりのすべての引数をひとつのリストに 拾いあげます。

3.2  apply

Scheme手続きapplyは手続きを引数のリストに適用します。

(define x '(1 2 3))

(apply + x)
=>  6

一般には、applyは、ひとつの手続きと、別に可変長の引数をとります。 この引数の最後はリストでなければなりません。最後の引数の前に間の引数 を付けて引数リストをつくります。そうして、この引数リストに対して、 手続きを呼んで、その結果を返します。

(apply + 1 2 3 x)
=>  12

3.3  直列化

スペシャルフォームbeginを使って、順番に評価する必要のある一連の サブフォームをひとつにまとめます。

(define display3
  (lambda (arg1 arg2 arg3)
    (begin
      (display arg1)
      (display " ")
      (display arg2)
      (display " ")
      (display arg3)
      (newline))))

Schemeでは、lambda-本体は暗黙のbeginです。したがって、 display3の本体のbeginはなくてもかまいません。しかし、 あって困るものでもありません。display3はもっと単純になります。

(define display3
  (lambda (arg1 arg2 arg3)
    (display arg1)
    (display " ")
    (display arg2)
    (display " ")
    (display arg3)
    (newline)))