LISP のコンセプト
ここでは、LISP プログラムの構造や法則といったものについて説明していきます。具体的なプログラムを書く前に、この基本的な構造、LISP の法則といった LISP のコンセプトを理解しておくことはとても重要なことで、LISP を使いこななせるかどうかの分水嶺(ぶんすいれい)となるものです。そして、それはとても単純なものです。こんな単純な LISP のコンセプトで他のプログラミング言語に劣らない表現力豊かなプログラムが書けることが、LISP とのつきあいが長くなるにつれて実感していくでしょう。
ここで説明する LISP のコンセプトというものは他のプログラミング言語の文法には無いもので、初めてふれる方には新鮮であり奇異に感じるかもしれません。これこそが LISP の文法と言ってもいいでしょう。そして、他のプログラミング言語に比べて圧倒的に押さえておくべき点が少ないのが特徴です。
さて、 LISP のコンセプトとは具体的にどういったものでしょうか?それは、アトム - リストといった構成と「評価」という仕組みです。
シンボル
LISP において、変数名は「シンボル」と呼ばれています。シンボルはその名前で区別できる、つまり他と重複は許されない名前で、値を代入できます。シンボルには、整数や実数、文字列といった AutoLISP で扱えるあらゆるデータタイプを代入することができます。シンボルは、特別な宣言を行う必要がなく、使いたいときに使い始めることができます。
下の例は、a というシンボルに、整数 10 や実数 0.025、文字列 "AutoCAD" を代入しています。「setq」は代入を行う関数です。
_$ (setq a 10) ⏎
10
_$ (setq a 0.025) ⏎
0.025
_$ (setq a "AutoCAD") ⏎
"AutoCAD"
そして、変数名と同じく関数名もシンボルです。関数が定義されるということは、関数名を表すシンボルに関数の内容を表す値が代入される、関数というオブジェクトが代入されるということです。別の言い方をすると、整数や文字列と同じように、関数をデータとしてシンボルに代入して扱えます。
以下の例では expt という組み込み関数のシンボルの内容を取り出して、シンボル a に代入しています。すると新たに a を関数名として expt と同じことが行えます。
_$ (setq a expt) ⏎
#<SUBR @000000002e63aa68 EXPT>
_$ (a 3 2) ⏎ ;(expt 3 2)と同じ
9
#<SUBR @000000002e63aa68 EXPT> と表示されるのが関数の内容を表す値で、SUBR はシステム内臓の関数(コンパイル済みの関数)であることを示しています。
関数名のシンボルについては、関数の説明で再度取り上げます。
アトムとリスト
LISP のプログラムにおいて、これ以上分解できない要素をアトムと言います。対して、分解できるものはリストと呼ばれています。
具体的にアトムは、数値、文字列などといったもの、そして関数型言語の特徴である関数自身、及び、変数名や関数名といったシンボルです。分解できるリストは、その中にさらにリストを含むこともありますが、要素を分解していくと、最終的にはアトムで構成されます。
リストは一対のカッコ「(」・「)」で囲まれ、半角スペースで区切られた要素の羅列です。
評価 - アルゴリズムとデータ
リストを具体的に説明する前に、LISP プログラム内におけるアルゴリズムとデータについて見てみます。
プログラムの構成要素は、アルゴリズムとデータに分けられるのは御存じのとおりです。AutoLISP においては、アルゴリズム部分を「式」またはカタカナで「フォーム」と呼びます。LISP 一般では「S 式(symbolic expression)」と呼ぶ場合もあります。式は、とりあえず関数の呼び出しのような事を意味します。そして、式とデータの記述の仕方が LISP の特徴です。比較すると次のようになります。
式 | データ | |
---|---|---|
整数 | 1 | '1 または (quote 1) |
実数 | 10.0 | '10.0 または (quote 10.0) |
文字列 | "AutoCAD" | '"AutoCAD" または (quote "AutoCAD") |
シンボル | counter | 'counter または (quote counter) |
関数の呼び出し・リスト | (expt 3 2) | '(expt 3 2) または (quote (expt 3 2)) |
式に対して、データは「'」(クォーテーションマーク)が付くか同じ意味の quote 関数で囲まれています。アルゴリズムとして実行されるのが式ですが、LISP では実行することを「評価する」と言います。それに対して、「'」や quote 関数を付けることは「評価をしない」という指示を行うこととなり、その結果「評価されない」ものがデータとして区別されるのです。
この点がこれから説明しようとしている LISP のコンセプトの核心になります。この仕組みがどのようにプログラミング言語として機能するようになるのか、一歩一歩見ていきたいと思います。
アトムと評価
整数・実数・文字列・シンボルといったアトムの場合の「評価/評価しない」の関係を確認します。
AutoLISP関数 |
---|
(quote expr) |
expr:式 |
評価せずに式を返します。 |
戻り値:式 |
quote 関数が行うことはいたってシンプルです。まずは数値の場合を見ていきます。
_$ (quote 1)⏎
1
ここでは、整数の1 を「評価しないという評価」を行う意味になります。(quote a)の簡略表現として、'a といった具合に頭にシングルクォーテーションを付加したものが使用できます。これは quote 関数を呼び出したのと同じと解釈されます。
_$ '1⏎
1
文字列の場合も同様です。
_$ '"AutoCAD"⏎
"AutoCAD"
これらは、次の例のように式として「評価したもの」として扱ったものと、タイプの内容がほとんど同じですし、結果も同じです。しかし、意味としては異なります。
_$ 1⏎
1
_$ "AutoCAD"⏎
"AutoCAD"
quote 関数を用いなかった上の例の場合は、整数 1 というアトムの評価を行っています。アトムを評価するとアトムが示すそのものになります。また、アトムである文字列を評価すると、その文字列そのものになります。回りくどい言い方になったのでもっと簡単に言うと、アトムを評価しても変わらない、ということです。
次は、変数であるシンボルに対する quote 関数の実行例を示します。値を代入されたシンボルは、式として評価されれば変数の内容つまり値を返し、評価されずにデータとして扱われればシンボル名を返します。
_$ (setq a 3) ⏎ ;変数 a に 3 を代入
3
_$ a⏎ ;変数 a を評価
3
_$ 'a⏎ ;変数 a を評価しない
A
なお、アトムも評価される時は、文法の概念的に式の一種です。
関数の呼出しやリストと評価
関数の呼び出しやリストと呼ばれるものの「評価/評価しない」の関係を確認します。
関数呼び出しとしての式は、カッコの中の第 1 要素を関数名として、のこりを関数への引数として関数を実行、すなわち評価するのが LISP の仕組み、文法になります。下の例は 3 と 2 を引数として expt 関数を評価( 3の2乗を計算 )し、シンボル a に代入します。
_$ (setq a (expt 3 2)) ⏎
9
次の、データとしてのリストの場合は quote 関数を使って、(expt 3 2)を評価することなく変数 a に代入しています。
_$ (setq a (quote (expt 3 2))) ⏎
(expt 3 2)
シングルクォーテーションを使うならば、以下のように簡潔に書けます。
_$ (setq a '(expt 3 2)) ⏎
(expt 3 2)
LISP においては、関数呼び出しの式もデータとしてのリストも「広い意味でのリスト」です。どちらも同じように、LISP においてリストと呼ばれる、一対のカッコ「(」・「)」で囲まれ、半角スペースで区切られた要素の羅列です。「広い意味でのリスト」が式として評価されると、関数を実行した結果の値が得られます。対して、quote 関数やその簡略表現の「'」(シングルクォーテーション)によりデータとして扱われた「狭い意味でのリスト」は、関数呼び出しなどがおこることもなく、そのままデータとして扱われます。式とデータは「評価する/しない」の区別を付けられただけで、同じリストといった構造で記述されることが LISP の大きな特徴です。
式
LISP のプログラムをご覧になると、カッコが多く連なって異様に見えるかもしれません。アトムである整数や文字列も式の一つですが、一般的な式のイメージとしては、一対のカッコ「(」・「)」で囲まれた部分が式と呼ばれます。そして式の集まりがプログラムとして機能します。
LISP は、式が与えられるとカッコ内の最初の要素を関数名と解釈し、以降の内容を関数に渡す引数と解釈して実行します。
「式の評価」は関数の実行と同じ意味です。ある種の関数は計算した結果を戻り値で返します。「戻り値を得る」ことも「式の評価」と同じ意味です。なお、ある種の関数には戻り値には大した意味は無く関数の実行に伴う副作用が目的の場合もあります。入出力を伴う関数がその代表例ですが、代入を行う setq という関数も変数に値を代入するという副作用が目的であって、戻り値を受け取って利用するケースは半分ぐらいではないでしょうか。これらも同様に「式の評価」と言われます。LISPは、すべてのプログラムを「式の評価」で記述するということを指向した言語で、他の手続き型言語とはものの見方が異なるところです。
シンプルな式の例は以下のとおりです。expt が関数名を、3 と2 は関数へ渡す引数を表しています。常にカッコ内の先頭は関数名と解釈されます。意味は 3 の2 乗を返す式です。
他の言語で、計算結果を変数 a に代入するには、一般に以下のような感じになるでしょうか。
LISP では代入を次のように書きます。「=」は用いないで、代入を行うための setq 関数をわざわざ使います。
LISP には「=,+,-,*,/」といった演算子やオペレータはありません。あるのは同じ記号であらわされた関数です。
他の言語で、次のような代入式があったとします。
上の計算式をLISP では次のように記述します。「*」や「+」はオペレータではなく関数名です。
カッコが簡単に増えていきますが、これは慣れるしかありません。
これは、ポーランド記法や前置記法と言われるものに似ていますが、+ 関数が 2 個以上の引数を受け付けるなど、一般的なイメージと異なる部分があります。
大文字小文字の区別
LISP では、プログラムの関数名や変数名について大文字小文字は区別されません。ただし、自分でプログラムを書くときは自分のルールを決めて書いた方が読みやすくなります。値を出力させてみると大文字で表示されるのが普通です。もちろん、データとしての文字列では大文字小文字が区別されます。