数値や文字など、LISP で扱うことができる何らかのデータをまとめてリストとして管理することができます。 リストは、一般的なプログラミング言語における配列のようなものだと思って良いですが、LISP 言語には明確に配列という機能も存在するため、リストと配列は異なります。 LISP のリストは、近年のオブジェクト指向型フレームワークにおける動的配列やイテレータのような、順次アクセス型の動的なデータ並びに近い存在です。
リストも LISP が扱う一つのデータ型で、組み込み関数 list で定義することができます。
list &rest args
この関数の引数には、リストのデータ並びに指定する任意のデータを指定します。 このとき、リストに配置される各々のデータを要素と呼びます。 例えば (list 10 20 30) とあった場合、数値 10、20、30 がリストの要素となるのです。
> (list 1 2 3 4 5) (1 2 3 4 5)
このように、list 関数でリストを定義すると、システムは括弧 ( ) で括ってリストを表示します。 リストも一つのデータなので、リストの要素にリストを指定することも可能です。
> (list (list 1 2 3) (list 10 20 30) 100 200 300) ((1 2 3) (10 20 30) 100 200 300)
このリストの第一要素と第二要素は、同じく list 関数で作成されたリスト型のデータになっています。 このように、様々なデータ型をリストとして並べることができるので、リストは柔軟にデータを扱うための重要な存在です。
list 関数に値の列を送ることでリストを得ることができましたが、quote 特別式にリスト表記を指定することで式を評価しないリストを得ることもできます。
quote object
この式は、変数を使うために記号型の値を作成するときに使いました。 実は、quote は引数に渡されたデータを評価せずに値として返すという性質を持つ特別式なのです。 そのため、x や sakura という識別子を渡すと、そのまま x や sakura という記号を評価せずに返したのです。
LISP のデータ構造に慣れていない手続き型思考のプログラマには、この概念は難しく感じるかもしれません。 例えば (1 2 3) という表記はリスト表記ですが、これをシステムが実行すると 1 という関数に引数 2 と 3 を渡すものだと評価されてしまうのです。 同様に、変数 x が 10 という値を保存している場合 (set x 20) は変数 x に 20 を代入するのではなく、(set 10 20) と評価されてしまうのです。
これを逆に考え、quote は引数に渡したデータを評価しないという特別式であるということを認識すると、quote の本質的な意味が理解できます。 (quote x) は x を評価するのではなく、システムが認識できる直接の記号 x を返していたのです。 システムはリストを括弧で括ったデータ列として認識するため quote に ( ) 表記のデータを渡せば、それを評価せずにリストとして返すということになります。
> (quote (10 20 30)) (10 20 30) > '(10 20 30) (10 20 30)
これは、リスト表記のデータを quote 特別式に指定することでリストを作成した例です。 quote は ' で省略表記できたので、(quote (10 20 30)) は '(10 20 30) でも同じです。
くどく言いますが、list 関数と quote 特別式の違いは、式を評価するかどうかです。 list 関数は引数に指定された任意の個数のデータを受け取り、それをリストに変換して返します。 このとき、list 関数を実行した時点で引数は評価されています。 一方 quote は評価されません。
> (setq x 10 y 20 z 30) 30 > (list x y z) (10 20 30) > (quote (x y z)) (X Y Z)
これは、setq で初期化した変数 x、y、z をリストの各要素に指定した場合の例です。 list 関数の場合の結果は明白で、関数実行時に x、y、z 記号が評価され、変数に格納されている値がリストの要素となります。 一方の quote は (x y z) が評価されずに、記号がそのままリストの要素となります。
リストは一種のスタックのような性質があり、動的に要素を追加したり、要素を取得することができます。 近年で一般的な動的配列とは異なり、どちらも先頭要素を対象にして行われるという特徴があるので注意してください。
リストの先頭要素を取得するには car 関数、先頭要素以外を得るには cdr 関数を使います。 この二つの関数による処理を繰り返すことができれば、先頭要素から順にリストのデータを検出することができるでしょう。
car list
cdr list
これらの関数の引数には、リスト型のデータを指定します。
> (setq x '(10 20 30)) (10 20 30) > (car x) 10 > (setq x (cdr x)) (20 30) > (car x) 20 > (setq x (cdr x)) (30) > (car x) 30
この例では、変数 x にリスト (10 20 30) を保存し、このリストを先頭から順に取り出すというものです。 先頭要素のデータを得るには car 関数を用いて、先頭要素を取り除いたリストを得るには cdr 関数を使っています。 (setq x (cdr x)) では、変数 x が保存しているリストの先頭要素を除いたリストを改めて x に保存しています。
逆に、リストにデータを追加したい場合は cons 関数を使います。 cons 関数は指定したリストの先頭に、新しい要素としてデータを追加します。
cons addValue list
addValue には追加するデータを、list には対象のリストを指定します。 この関数によって、list の先頭に addValue を追加した新しいリストが返されます。
> (setq x '(30)) (30) > (setq x (cons 20 x)) (20 30) > (setq x (cons 10 x)) (10 20 30)
この例では、最初は 30 が要素を一つだけのリストを x に保存し、その後 (setq x (cons 20 x)) というような形で、リストを保存している変数 x に対して新しい要素を追加しています。
cdr 関数で要素が全て失われたリストや、最初から要素が存在しないリストは空のリストと呼ばれます。 空のリストは表記上 ( ) と記述されますが、システムでは定数 nil と同義です。 そのため、リストの評価結果が nil である場合、リストは要素を持たないと解釈することができます。
> (list) NIL > (quote ()) NIL
この例から確認できるように list 関数に引数を渡さなかった場合や ( ) は nil であることがわかります。 nil は特別な定数で、nil という記号を表します。
リストが空であるかどうかは、length 関数から判断することも可能です。 この関数は引数に指定されたリストの要素の数を返します。
length sequence
sequence にリストを指定することで、この関数は指定されたリストの要素数を返します。 要素が空である場合や nil が指定された場合は 0 を返すでしょう。
> (length '(10 20)) 2 > (length (cdr '(10))) 0 > (length nil) 0