変数


データの保存

数値を一時的に保存するには変数を使います。 通常、変数に対して 10 や 512 という数値を定数と呼びます。 変数はプログラムの実行時に値が決定されますが、定数はプログラムコードを記述した時点でその値が決定しています。

変数は、コンピュータのメモリ領域を表す識別子(symbol)であると考えられます。 コンピュータは、その内部ではデータの保存領域を数値を用いた番地(アドレス)で表現しています。 しかし、人間にとってデータの保存場所を数値で表されても直観的には理解できないので、その代わりに文字を使ったものが識別子なのです。

データを変数に保存するには setq を用います。

setq {var form}*

{var form}* には、変数の識別子と変数に保存する式を指定します。 何らかの式の結果を変数に保存することを代入すると呼びます。 例えば、変数に 10 を代入するには次のように記述します。

(setq 変数名 10)

変数名には、アルファベット、数値、記号で構成される識別子を指定します。 Common Lisp の識別子は、アルファベットの大文字小文字を区別しません。 次の記号を使うこともできます。

+ - * / @ $ % ^ & _ = < > ~ .

ただし +1 や -20 という表記は整数として認識できるため識別子にはなりません。 +$ や 1+ は識別子として認識することができます。

> (setq x 10)
10
> (setq $$$ 100)
100
> (+ x $$$)
110

この一連のコマンドでは、変数 x と $$$ に対して 10 と 100 を代入しています。 これらの変数は他の関数の引数などに利用することができます。 何らかの情報を一時的に保存しておけば、必要なときに変数にアクセスすることで情報を得ることができます。 最後の + 関数では x 変数と $$$ 変数の加算結果が適切に出力されていることがわかります。

setq は、次のように同時に複数の変数に対して代入することも可能です。

(setq a 10 b 100 c 1000)

この場合、a 変数に 10、b 変数に 100、c 変数に 1000 が代入されます。 代入する値には式を指定することもできます。

(setq a (+ b c))

この場合、a 変数には b 変数と c 変数の値を加算した結果が格納されます。

> (setq x 10)
> (setq y (+ x 100) z (- x 5))
> x
10
> y
110
> z
5

このコマンド例では、y と z 変数に対して同時に setq で式を代入しています。 このとき、式は単純な値ではなく x 変数も使った + 関数式であることに注目してください。 y や z には x 変数の値 10 に対して加減算したものであることが結果から確認できます。


特別式

setq は関数のように振舞いますが、言語仕様の分類上では関数式ではありません。 実は、setq は特別式(Special Form) と呼ばれる式に分類されています。 つまり setq 関数ではなく、setq 特別式と呼ぶのが正しい表現です。

通常の関数式に x や y といった変数名を指定した場合、Lisp は指定した変数名を評価して変数に格納されている値を参照します。 つまり x に 10 が代入されている場合 (+ x 5) は Lisp エンジンに評価されて (+ 10 5) として扱われます。 しかし (setq x 5) は (setq x 5) は (setq 10 5) とは扱われません。 x のような変数名が評価されて値が参照されるのではなく、x はあくまで x という名前の識別子であると認識されます。 この点で、特別式は関数式と異なるのです。

C 言語のようなコンパイル言語では、変数名のような識別子はコンパイル時にコンパイラが識別するためのテキスト上のシンボルに過ぎませんでした。 機械語に変換された時点で、変数はメモリアドレスで管理されているため識別子という考えはすでに消滅しています。

しかし、Lisp において識別子は特別な意味を持ってます。 Lisp 言語では、識別子はテキスト上で識別するためのシンボルではなく独立した型として認識されています。 究極的には変数に変数名(識別子)を保存することもできます。 これは、C 言語におけるポインタのような役割を果たします。

Lisp 上で管理できる識別子というデータは、quote 特別式が返します。

quote 識別子

quote 特別式は引数に指定された識別子をデータとして返します。 この、変数名などを表すデータ型を記号型または単純に記号と呼びます。 再帰的でややこしい話に感じますが、記号型は単純な文字リテラルなどではなく、独立したひとつの型なのです。

> (quote moemoe)
MOEMOE

この Lisp コマンド quote は、moemoe という識別子を記号型の値 MOEMOE に変換しています。

記号型の値を使えば、関数から変数を制御することができます。 特別式と関数式の違いを理解するには set 関数 と setq 特別式の違いを認識する必要があります。

set symbol value

set 関数は setq 特別式と同様に変数に値を代入する役割を持ちます。 しかし、次のコマンドは適切ではありません。

> (setq x 10)
10
> (set x 100)
** - Continuable Error

setq 特別式の表記であれば、正常に変数 x に対して値を代入できています。 しかし set 関数式の場合 (set x 100) とありますが、変数 x は関数が実行される前に評価され setq 特別式で代入された値 10 が参照されます。 その結果 (set 10 100) となってしまい、定数 10 に 100 を代入することはできないというエラーになるのです。

set 関数の場合、最初の引数には記号型の値を渡さなければならないのです。 記号型の値は quote 特別式が返すことができました。

> (set (quote x) 100)
100

quote 特別式によって x という記号値が返ります。 これによって、set 関数は正しく変数を認識することができ、記号値 x が参照する変数に対して 100 を代入することができました。

勘違いしてはならないのが、set 関数の x が変数だったから代入できなかったわけではなく、x 変数が数値型の値を保存していたから代入できなかったということです。 センスあるプログラマは、この時点で変数 x が記号型を保存している場合 (set x 100) が正常に動作することを理解できるでしょう。

> (set (quote a) 100)
> (set (quote x) (quote a))
> (set x 1000)

> x
A
> a
1000

上記のコマンドの結果、変数 x は記号型の値 A を保持していて、変数 a には最終的に set 関数から 1000 という値が間接的に代入されているということを理解する必要があります。

まず、最初の (set (quote a) 100) は (setq a 100) と同義です。 変数 a に対して数値型の値 100 を代入しています。

問題は次です。 (set (quote x) (quote a)) は、変数 x に対して記号型の値 A を代入しています。 この結果、関数式から変数 x を参照するという行為は記号型の値 A を得るということと同じになります。 (set x 1000) では、x 変数を参照して記号型の値 A を得ることができるため、すなわち変数 a に対して 1000 を代入するということを意味しているのです。 (set x 1000) が変数 x に 1000 を代入しているわけではありません。

因みに、記号型の値を得るために quote 特別式を使うのは面倒なので引用符 ' で識別子から記号型の値を得ることができます。

'識別子

特別な事情がない限り、quote ではなく ' を使ったほうがスマートに記述できるでしょう。

> (set 'x 100)
>  x
100

このコマンドの (set 'x 100) は (set (quote x) 100) と同じです。


記号型の値から変数値を得る

記号型の値を評価しても得られる値は記号型が表す変数の識別子です。 これは至極当然のことなのですが、記号型の値から、記号型の値が表す変数が保存している値にアクセスできないものでしょうか。

> (setq a 100)
> (setq b 'a)
> b
A

このコマンドの変数 b は記号型の値 A を格納しています。 関数式で b を評価しても得られるのは変数 a であり、変数 a が保存している値 100 にはたどり着きません。 記号型の値 A から変数 a の値を得るには symbol-value 関数 を使います。

symbol-value 記号

この関数の引数に指定する記号は、常に記号が他の値です。 関数は指定した記号から、記号が表している変数の値を返してくれます。

> (setq a 100)
> (setq b 'a)
> (symbol-value b)
100

このコマンドの symbol-value 関数では、記号型の値を保存する変数 b を指定してます。 変数 b は関数を評価するときに参照され、記号型の値 A を symbol-value に渡しています。 その結果、symbol-value 関数は記号型の値 A から、記号型が表す変数 a を参照して 100 を返しています。



前のページへ戻る次のページへ