実践Common lispを読み始めた-第12章 リスト処理:やつらがLISPと呼ぶ理由(2)

絵を書きたい

はてなでは簡単に絵を描く方法はないのかな。手書きはうまくかけないし、最近Macに変えたのどんなドローイングソフトを使うのがよいのかよくわからないし、書いたものをpngファイルとかに保存して貼るのも面倒。

点対(ドットペア)

コンスセルの2つ目の値がNIL、他のコンスセルへの参照でもないとドットペアになる。理解はできるのだけど、あえて点対を作る意味はあるのだろうか?

CL-USER> (cdr '(1 . 2))
2
CL-USER> (cdr '(1 2))
(2)

点対のcdrは値そのものに対してリストへのcdrはリストになる。この違いでうれしいことがあるのかな?
LIST関数の他にLIST*なんて関数があって、最後が点対を作るものが用意されているところをみると使い道があるのだろう。

CL-USER> (list 1 2 3 4 5)
(1 2 3 4 5)
CL-USER> (list* 1 2 3 4 5)
(1 2 3 4 . 5)

破壊的操作とリサイクル操作

リスト操作の多くが関数的に書かれているので関数呼び出しによってリストを参照している他の人たちが困ってしまうことはないが、そうすると、リストの構造を変えないと行けない操作では、データコピーが発生してしまうので、必要最小限のコピーで済むように色々工夫している。
一方で、できるだけデータコピーを減らすように引数のコンスセルを使い回すような関数も提供されている。この関数のことをsetfみたいな意識的に副作用を起こす関数と区別してリサイクルな関数と呼んでいる。
リサイクルな関数の副作用については明確に規定されていないので、一部の例外を特定の副作用にあわせた実装はできない。
lispでコンスセルのメモリ上のどこにあるかを調べるにはどうすればいいのだろうか?

リサイクルな関数の使い方

リサイクルな関数を使うときのイディオム

  • pushとnreverseをペアで使って自分で作ったリストに対してリサイクル操作をすれば、他に参照している人はいないから安全。
  • リサイクルな関数の戻り値をリサイクルされた値が含まれ得る場所に即座に代入し直す。でもこっちのイディオムは他の変数がリサイクルされた値を参照していると影響を受ける。

リスト操作関数

  • first(car), rest(cdr)ですべての要素にアクセスできる。でも組み合わせが面倒くさいからcaarとかcadadrとか合成関数が用意されている。
  • first, second, ... , tenthまでのn個目の要素にアクセスする関数がある。むむ、点対に使うとどうなるのかな?
CL-USER> (second '(1 2))
2
CL-USER> (second '(1 . 2))
; Evaluation aborted

リストでないとだめなのね。

マッピング

  • mapcar mapと似ているが戻り値の型指定がいらない。
  • maplist 関数に渡されるのが要素ではなくてコンスセル。だから渡ってきたコンスセルが参照しているコンスセルにもアクセスできる。
CL-USER> (maplist #'(lambda (x) (reduce #'+ x)) '(1 2 3))
(6 5 3)

上の例では引数で与えられた要素以降の和を要素に持つリストができる。