プログラミング原人の進化ログ

プログラミング原人の進化論

オレ プログラミング ベンキョウ スル。マナンダ コト カク。

【Common Lisp】リストの基本中の基本

リストはLispにおいて非常に大事です。ということで、Common Lispのリストの基本的なアイデアをまとめました。

フォーム

Lispのコードはリストで構成されています。このリストは、フォームでなければなりません。フォームとは、先頭の要素がコマンドであるようなリストのこと。
コマンドの厳密な意味をまだ理解できていないのですが、多くの場合関数であるようです。

コードモードとデータモード

Common Lispのプログラムには次の2つのモードがあります。

  • コードモード:実行される。フォーム。
  • データモード:実行されない単なるデータ。

デフォルトではコードモードとなっています。データモードにするにはリストの前にシングルクオートをつけます(この操作を「クオートする」という)。

例:

CL-USER> (expt 2 3)
8
CL-USER> '(expt 2 3)
(EXPT 2 3)

上のコードモードは2の3乘を計算していますが、下のデータモードでは式をそのまま返しています。

準クオートと呼ばれる機能を使うと、データモードの中にコードモードの部分を埋め込むこともできます。
バッククオートでデータモードになり、カンマでコードモードになります。

CL-USER> `(1 + 1 = ,(+ 1 1))
(1 + 1 = 2)

リストの構造

コンスセル

Lispのリストは、コンスセルと呼ばれる部品からなっています。コンスセルは、2つのセルを並べたような構造をしています。それぞれのセルは何らかの別のものをさし示しています。
例えば、'(1 2 3)は次のようになっています。

f:id:programgenjin:20190228092230j:plain

一つ目のセルはデータを、2つ目のセルが次のコンスセルを指しています。nilはリストの終端を表しています。
連結リストに似ていますね。

リストを操作する

cons関数

cons関数は2つのデータをくっつけたリストを作ります。

例:

CL-USER> (cons 'chicken 'cat)
(CHICKEN . CAT)
CL-USER> (cons 'chicken 'nil)
(CHICKEN)
CL-USER> (cons 'chicken ())
(CHICKEN)
CL-USER> (cons 'pork '(beef 'chicken))
(PORK BEEF 'CHICKEN)

リストの真ん中のドットは、それがコンスセルであることを表現しています。nilは空リストと等価であり、上のように省略されます。
次の表現は全て等価です。

nil, 'nil, (), '()

上の図の'(1 2 3)は次のようになります。

CL-USER> (cons 1 (cons 2 (cons 3 ())))
(1 2 3)

コンスセルが連なっているのが、リストとして表示されています。

car、cdr

car関数はリストの最初のコンスセルの1番目のセルの指すものを取り出します。
cdr関数はリストの最初のコンスセルの2番目のセルが指すものを取り出します。つまり、リストで言えば、2番目以降の要素が取り出されることになります。

CL-USER> (car '(pork beef chicken))
PORK
CL-USER> (cdr '(pork beef chicken))
(BEEF CHICKEN)

carとcdrを組み合わせたコマンドに、cdarやcadrなどがある。それぞれ、carを適用してからcdrを適用したもの、cdrを適用してからcarを適用したもの。

list

list関数を使うと、cons関数を重ねて用いなくても、長いリストを簡単に作ることができます。

CL-USER> (list 'pork 'beef 'chicken)
(PORK BEEF CHICKEN)

まとめると、リストの作り方には、list関数を使う、consしてコンスセルをつなげる、クオートして作る、という3つの方法があります。

append

append関数はいくつかのリストをまとめて1つのリストにします。

CL-USER> (append '(mary had) '(a) '(little lamb))
(MARY HAD A LITTLE LAMB)

参考

コンラッドバルスキ(2013)「Land of Lisp」 川合史郎訳 オライリー・ジャパン