TIL

Today I Learned. 知ったこと、学んだことを書いていく

連想配列(alist) - Lisp

Lispには連想リストというものがある

連想リスト(association list)、また、alistと呼ばれる。carがキー、cdrが値となる。

carがキー、cdrが値って考え方スッキリしてて好き

> (defparameter *alist* '((hoge (1 2 3))
                          (fuga (4 5 6))
                          (piyo (7 8 9))))
*ALIST*

> *alist*
((HOGE (1 2 3)) (FUGA (6 7 8)) (PIYO (9 1 2)))

要素の追加にはconsを使う

> *alist*
((HOGE (1 2 3)) (FUGA (4 5 6)) (PIYO (7 8 9)))

> (cons (cons 'hogera '(10 11 12)) *alist*)
((HOGERA 10 11 12) (HOGE (1 2 3)) (FUGA (4 5 6)) (PIYO (7 8 9)))

先頭に追加のときはpushを使うのが楽かも

> *alist*
((HOGE (1 2 3)) (FUGA (4 5 6)) (PIYO (7 8 9)))

> (push '(foo (a b c)) *alist*)
((FOO (A B C)) (HOGE (1 2 3)) (FUGA (4 5 6)) (PIYO (7 8 9)))

キーを指定して値を取得

assoc関数を使うことで指定したキーに一致するリストが取得できる

> (assoc 'hoge *alist*)
(HOGE (1 2 3))

値のみ取得したい場合

> (cadr (assoc 'hoge *alist*))
(1 2 3)

assocとpushで値を更新したように見せかける

assocは見つかった最初の要素を返すため、pushすると(最初の要素に追加)、値を更新したことと同じになる。最初、何言ってるのかわかんなくて全然理解できなかった。

> (defparameter *fruits* '((apple (100 red))
                           (banana (150 yellow))
                           (peach (300 pink))))
*FRUITS*

> *fruits*
((APPLE (100 RED)) (BANANA (150 YELLOW)) (PEACH (300 PINK)))

> (assoc 'peach *fruits*)
(PEACH (300 PINK))

;;; 最初の要素に追加
> (push '(peach (500 pink)) *fruits*)
((PEACH (500 PINK)) (APPLE (100 RED)) (BANANA (150 YELLOW)) (PEACH (300 PINK)))

;;; あたかも更新されてるみたいになる!
> (assoc 'peach *fruits*)
(PEACH (500 PINK))

更新されてるみたいになる。理解できた気がする!!

Land of Lispの説明文を引用

assocコマンドは常に、見つかった最初のエントリを返す。したがってpushコマンドを使うと、assocにとってはそのオブジェクトに対する場所が更新されたのと同じ効果を持つわけだ。pushとassocを使って、以前の値を残したまま、alistの値が変更されたかのように見せることができる。

参考文献

Land of Lisp

Land of Lisp

四則演算 - Lisp

整数と浮動小数点数がある。小数点をつけると浮動小数点数になる。

加算は+

> (+ 1 2)
3
> (+ 1 2.0)
3.0

整数と浮動小数点数を混ぜると浮動小数点数になる

減算は-

> (- 3 4)
-1

乗算は*

> (* 2 3)
6

除算は/

> (/ 3 4)
3/4

どちらも整数だと有理数を返す。すごい数学的だなと思った。有理数は分数で表せる数?

少数で返してほしいときは、引数のいずれかに小数点をつければいい。

> (/ 3 4.0)
0.75
> (/ 3.0 4)
0.75

あまりはmod

> (mod 8 3)
2

参考文献

コンスセルとconsとcarとcdr

Lispはすべてリストでできている。また、リストとリストを繋ぐ役目にコンスセルというものがある。これはすごい面白い考え方だと思った

コンスセルは2つの部屋(セル)でできている。1つ目のセル(CARと呼ばれる)にはデータなどを指し、2つ目のセル(CDRと呼ばれる)には別のコンスセルやnilというリストの終わりを示す特別なシンボルを指すことができる。
また、データそのものが入るわけではなく、データの参照を指し示すようになっている。ポインタみたいな?

Lispではコンスセルの連なりとリストは全く同じものとして扱われる。

コンスセルについてはAbout Cons Cellsがわかりやすかった

Lispのプログラムからコンスセルを扱うにはcons関数を使う。ほかにもcarcdrがある。

cons関数

> (cons 1 2)
(1 . 2)

これは、1と2をコンスセルで繋いでいますよっていうこと。リストと区別するために、.が入っている

> (cons 1 'nil)
(1)

第2引数にリストの終りを示す'nilを渡すと、通常のリストのような表示になる。(1 . 'nil)と同じ意味。Lispはできるだけ、コンスセルではなくリストとして見せてくれている!

> (cons 1 ())
(1)

Common Lispでは、空のリスト()とシンボル'nilは同じ意味となっている。そのためこのようにも書ける。
「空のリストに1を追加したら、1だけのリストになる」と考えればしっくりきた。

どんなリストもコンスセルで表せる!

> '(1 2 3)
(1 2 3)
> (cons 1 (cons 2 (cons 3 'nil)))
(1 2 3)
> (cons 1 (cons 2 (cons 3 ())))
(1 2 3)

car関数とcdr関数

コンスセルを扱うための関数であるcar(カー)とcdr(クダー)。それぞれ、コンスセルの部屋の構造と一致している。

carで1つ目のコンスセルのCARセル。cdrで1つ目のコンスセルに紐づくデータが取得できる。

> (car '(dog cat chicken))
DOG
> (cdr '(dog cat chicken))
(CAT CHICKEN)
> (cdr (cons 'b 'nil))
NIL

組み合わせることで取得したい場所を取得できる。ちょっと複雑になるけど。。。

例)2番目の要素を取得する

> (car (cdr '(dog cat chicken)))
CAT

cdrで2番目以降の要素を取得し、それに対してcarで1番目の要素(2番目)を取得する

一度に取得できる関数も用意されている

> (cadr '(dog cat chicken))
CAT
> (cadr '(dog cat chicken))
CAT
> (cddr '(dog cat chicken))
(CHICKEN)
> (caddr '(dog cat chicken))
CHICKEN
> (cdddr '(dog cat chicken))
NIL

各順番のとおりにadを書けばいいのかも。(car (cdr '(...)))だから、(cadr '(...))みたいに。

最後のコンスセルにはnilが入っていることを忘れずに!!!

cdadadrをやろうとしたら、そんなの無いよ!!!っていわれた。最大4つまでらしい。

> (cdadadr '((peas carrots tomatoes) (pork beef chicken)))

*** - EVAL: undefined function CDADADR

list関数

list関数を使うと簡単にリストが作れる。

以下の3つは同じ意味!!

> (list 'dog 'cat 'chicken)
(DOG CAT CHICKEN)
> '(dog cat chicken)
(DOG CAT CHICKEN)
> (cons 'dog (cons 'cat (cons 'chicken ())))
(DOG CAT CHICKEN)

nilを含まないデータでcdrを使うと

なんかおもしろいと思った。

> (cons 'a (cons 'b 'c))
(A B . C)
> (cdr (cons 'a (cons 'b 'c)))
(B . C)
> (cddr (cons 'a (cons 'b 'c)))
C

.は要素なのかな!?って思ってみたけど、そんなことはなかった。。。

listとクオートの快適さを実感した

複雑なリストを作るときにちょっと手こずった。

> (cons (cons 'peas (cons 'carrots (cons 'tomatoes ())))
        (cons 'pork (cons 'beef (cons 'chicken ()))))
((PEAS CARROTS TOMATOES) PORK BEEF CHICKEN)

((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN)) ってなってほしい!!

> (cons (cons 'peas (cons 'carrots (cons 'tomatoes ())))
        (cons (cons 'pork (cons 'beef (cons 'chicken ()))) ()))
((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))

できたけど、ややこしいよ!!!!!!

これをやったあとにlistやクオートを使うとなんと楽なことか

> (list (list 'peas 'carrots 'tomatoes) (list 'pork 'beef 'chicken))
((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))
> '((peas carrots tomatoes) (pork beef chicken))
((PEAS CARROTS TOMATOES) (PORK BEEF CHICKEN))

あぁ、楽すぎる...まじでやばい

Lispを始める

Lispになぜか興味を持ってしまい、本まで買ってしまった。

Lispの魅力は

  • Lisp自身をLispで書くのが容易
  • 問題に対応して書きやすい形に変化させて書ける
  • 拡張されることを前提にして作られている

たぶん、もっとあった気がする

Lispの種類

ANSI Common LispSchemeというのが有名らしい。最近見たのはClojureっていう言語も有名かも?

clispをインストールする

clispというCommon Lispで書かれたコンパイラを使って、書いていく

CLISP - an ANSI Common Lisp Implementation

brew install clisp
clisp --version

さぁ!Lispをはじめるぞ!!

roswellっていうのもあるらしいけど、とりあえず、最初はclispでやってみる。基礎がわかってきたらroswellでやってみよう。


インストール完了時に出てきた文字がちょっと気になった

==> clisp
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/clisp

Emacs!?なんですと!?emacsとコマンドを打ってみたら、Emacsが起動した。終了の仕方わからなくておどおどしてた。C-x C-cで終了できた

参考文献