car と cdr ¶リストのCARとは、端的に言えばリストの先頭要素のことです。したがって、リスト(rose violet daisy buttercup)のCARはroseとなります。
GNU EmacsのInfoでこれを読んでいる場合は、以下を実行することで確認できます。
(car '(rose violet daisy buttercup))
式を評価した後、echo領域にroseが表示されます。
car
はリストから最初の項目を削除するわけではなく、単にそれが何であるかを報告するだけです。car
をリストに適用した後も、リストは元のままです。専門用語で言えば、car
は「非破壊的」です。この特徴は重要であることがわかります。
リストのCDRはリストの残りの部分、すなわちcdr関数はリストの最初の要素に続く部分を返します。したがって、リスト'(rose violet daisy buttercup)のCARがroseであるのに対し、リストの残りの部分であるcdr関数が返す値は(violet daisy
buttercup)となります。
これは、以下を通常の方法で評価することで確認できます:
(cdr '(rose violet daisy buttercup))
これを評価すると、エコー領域に (violet daisy buttercup) が表示されます。
carと同様に、cdrはリストから要素を削除するわけではなく、単に2番目以降の要素が何であるかというレポートを返すだけです。
ちなみに、例では花のリストをクォートしています。 そうしないと、Lispインタプリタは rose
を関数として呼び出してリストを評価しようとします。 この例では、そうしたことはしたくありません。
リストを操作する場合、carやcdrという名称よりも、firstやrestという名称の方が理にかなっています。実際、firstやrestをcarやcdrのエイリアスとして定義し、コード内でfirstやrestと記述するプログラマーもいます。
しかし、Lispのリストは「コンスセル」と呼ばれる低レベルな構造を用いて構築されており(リストの実装方法を参照)、そこでは「先頭」や「末尾」といった概念はなく、CARとCDRは対称的です。Lispはコンスセルの存在を隠そうとはしませんし、プログラムではリスト以外の用途でもこれらが利用されます。そのため、リスト内では非対称な使われ方をしているにもかかわらず、carとcdrが実際には対称的であることをプログラマに思い出させる上で、これらの名称は有用です。
car や cdr を、リスト (pine fir oak maple) のようなシンボルからなるリストに適用すると、関数 car が返すリストの要素は、括弧で囲まれていないシンボル pine となります。pine
はリストの最初の要素です。ただし、以下の式を通常の方法で評価すればわかるように、このリストの CDR
はそれ自体がリスト (fir oak maple) となっています。
(car '(pine fir oak maple)) (cdr '(pine fir oak maple))
一方、リストのリストでは、最初の要素自体がリストとなります。car
はこの最初の要素をリストとして返します。例えば、以下のリストには、肉食動物のリスト、草食動物のリスト、海棲哺乳類のリストという3つのサブリストが含まれています:
(car '((lion tiger cheetah)
(gazelle antelope zebra)
(whale dolphin seal)))
この例では、リストの最初の要素またはCARが肉食動物のリスト(lion tiger cheetah)であり、リストの残りの部分は((gazelle antelope zebra) (whale dolphin seal))となっています。
(cdr '((lion tiger cheetah)
(gazelle antelope zebra)
(whale dolphin seal)))
改めて述べておく価値がありますが、car と cdr
は非破壊的です。つまり、適用先のリストを変更したり改変したりすることはありません。これは、これらの使い方において非常に重要な点です。
また、第1章のアトムに関する解説において、Lispでは配列などの特定の種類のアトムを分割できると述べましたが、その仕組みはリストを分割する仕組みとは異なります。Lispの観点からは、リストのアトムは分割不可能です。(Lispのアトムを参照してください。)carおよびcdr関数はリストの分割に用いられ、Lispの基本機能とされています。これらの関数では配列を分割したりその各要素にアクセスしたりすることができないため、配列はアトムとみなされます。逆に、もう一つの基本関数であるconsは、リストを結合・構築することはできますが、配列は構築できません。(配列は配列専用の関数で扱われます。The GNU Emacs Lisp Reference Manualの配列を参照してください。)