1.1.1 式
プログラミングを始める簡単な方法のひとつとして、LispのScheme方言のインタプリタと対話をしてみて、その結果を調べるというやり方があります。コンピュータターミナルの前に座っているところを想像してみてください。あなたが式(expression)を入力すると、インタプリタはその式の評価(evaluation)の結果を表示することによって応答します。
タイプできる基本的な式のひとつとして、数値があります(より正確に言うと、あなたがタイプする式は、10進数の数値を表す数値からなります)。Lispに次の数値を入力すると、
486
インタプリタは以下の内容を表示することで応答します。5
486
数値を表す式は、基本的な手続きを表す式(例えば や )と組み合わせることで複合式を作り、数値に対し手続きを適用することを表します。例えば:
(+ 137 349)
486
(- 1000 334)
666
(* 5 99)
495
(/ 10 5)
2
(+ 2.7 10)
12.7
このような式は、括弧の中の式のリストを区切ることにより作られ、手続きの適用を意味するもので、組み合わせ(combination)と呼ばれます。リストの左端の要素は演算子(operator)と呼ばれ、ほかの要素は被演算子(operand)と呼ばれます。組み合わせの値は、演算子によって指定された手続きを、被演算子の値である引数(argument)に適用することによって得られます。
演算子を被演算子の左に置くというやり方は、前置記法(prefix notation)として知られているものですが、数学で慣例となっているやり方とは大幅に違うので、最初は混乱するかもしれません。しかし、前置記法にはいくつかの利点があります。その中のひとつは、任意の数の引数を取る手続きにも対応できるということです。例を以下に示します。
(+ 21 35 12 7)
75
(* 25 4 12)
1200
演算子はいつでも左端の要素で、組み合わせ全体は括弧で区切られているので、曖昧さの入り込む余地はありません。
前置記法の二つ目の利点は、これを単純に拡張して、組み合わせをネストできる、つまり、組み合わせの要素がそれ自身組み合わせであるようなものが作れるということです。
(+ (* 3 5) (- 10 6))
19
このようなネストや、Lispインタプリタが評価できる式の全体としての複雑性には(原則的には)制限がありません。次のような、まだ比較的単純な式に出会った場合、混乱してしまうのは私たち人間のほうです。
(+ (* 3 (+ (* 2 4) (+ 3 5))) (+ (- 10 7) 6))
インタプリタは、迷うことなく57だと評価するでしょう。このような式は、次のような形で書くことで、私たち自身にわかりやすいようにできます。
(+ (* 3
(+ (* 2 4)
(+ 3 5)))
(+ (- 10 7)
6))
プリティプリント(pretty-printing)として知られるフォーマットの慣習は、被演算子が垂直に揃うようにそれぞれの長い組み合わせを書くというものなのですが、上の式はそれに従って書いたものです。結果として、字下げが明確に式の構造を示すことになります。6
複雑な式でも、インタプリタはいつでも同じ基本的なサイクルで動作します。式を端末から読み、式を評価し、結果を表示するというものです。この操作モードはよく、インタプリタのREPL(read-eval-print loop)モードと呼ばれます。特に、結果を表示することをインタプリタに明示的に指示しなくてもいいというところに注意してください。7
5. この本全体を通して、ユーザーの入力とインタプリタが表示した応答との違いを強調したい場合、後者を斜体で表します。 ↩
6. Lispシステムは、普通はユーザーが式を整形しやすいようにする機構を持っています。その中でも特に役に立つものが二つあります。ひとつは、改行のタイミングで自動的に正しいプリティプリントの位置にインデントするというもので、もうひとつは右括弧が入力されるたびにマッチする左括弧をハイライトするというものです。 ↩
7. Lispは、すべての式は値を持つという慣習に従っています。この慣習と、Lispは非効率的な言語であるという昔からの評判を組み合わせて、Alan PerlisはOscar Wildeのセリフをもじった次のような皮肉を言っています。“Lispプログラマはすべての値を知っているが、コストについては何も知らない” ↩