プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(49)

11月はめちゃくちゃ風邪を引きます。例年。
多少体調が良くなったのでやります、「実践Common Lisp」。
シーケンスについての話が続きます。

シーケンス述語

シーケンス全体に述語を適用して評価値を得る便利な関数が4つあり、
それはevery, some, notany, noteveryです。名前からして大体働きが想像できますね。
第一引数は述語で、残りの引数であるシーケンスと同じ数の引数を取るものとなります。

シーケンスマッピング関数

最後のシーケンス関数は総称的なマッピング関数だそう。
mapはシーケンス述語関数に似ていて、N個の引数を取る関数とN個のシーケンスを取ります。
戻り値になるのはシーケンスの各要素に関数を適用した新しいシーケンスです。
種類は指定しなくてはなりません。これstringとか指定することあるのでしょうか。

CL-USER> (map 'vector #'(lambda (x) (* x x)) '(1 2 3 4 5))
#(1 4 9 16 25)
CL-USER>

map-intoは、第一引数として渡したシーケンスに値を設定します。それ以外はmapに似ていると。

CL-USER> (defparameter *a* '(1 2 3 4 5))
*A*
CL-USER> (defparameter *b* '(10 20 30 40 50))
*B*
CL-USER> (defparameter *c* '(100 200 300 400 500))
*C*
CL-USER> (map-into *a* #'+ *a* *b* *c*)
(111 222 333 444 555)
CL-USER> *a*
(111 222 333 444 555)
CL-USER>

各シーケンスの長さが違う場合、最も短いシーケンスの要素と同じ数にだけ影響するそう。

最後のシーケンス関数はちょっと変わっているreduce。
第一引数が関数、第二引数がシーケンスで、シーケンスの第一要素と第二要素に関数を適用し、
その結果と残った要素を一つずつ関数に適用していきます。

CL-USER> (reduce #'+ '(1 2 3 4 5 6 7 8 9 10))
55
CL-USER>

reduceは非常に役に立つ関数で、特にシーケンスから1つの値を引き出したいときに有用だと。

CL-USER> (reduce #'max '(1 2 3 4 5 6 71 8 9 10))
71
CL-USER>

基本的なキーワード引数は全部取ることができ、また:initial-valueという
キーワード引数で、最初に関数に渡す値を指定することができます。

ハッシュテーブル

Common Lispに用意されているシーケンス以外の汎用目的のコレクションとしては
ハッシュテーブルがあるそうです。ベクタは整数でインデックス付けされたデータ構造で、
ハッシュテーブルは任意のオブジェクトをキーとして使うことができます。

引数なしのmake-hash-tableはeqlによって同じオブジェクトだった場合に2つのキーが等価だと
判定するハッシュテーブルを悪性します。
つまり、デフォルトではeqlで判定できない、例えば文字列などはキーに使えません
そういう時はお馴染、:testにequalを渡してmake-hash-tableを作成します。

ハッシュテーブルの要素にアクセスするには、gethashを使います。
キーとハッシュテーブルを引数として、キーが合致する値を返し、なければnilを返します。

CL-USER> (defparameter *h* (make-hash-table))
*H*
CL-USER> (gethash 'foo *h*)
NIL
NIL
CL-USER> (setf (gethash 'foo *h*) 'bar)
BAR
CL-USER> (gethash 'foo *h*)
BAR
T
CL-USER>

見ての通り、gethashは多値を返します。
nilが返ってきたときに値が見つかってnilなのか、見つからずnilなのかが分かるように真偽値があります。
多値の使い方は前どっかでやった気がするので割愛。

ハッシュテーブルからエントリを削除するには、remhashを使います。
引数はgethashと同じです。clrhashを使えばハッシュテーブルの全てのキーと値のペアを削除します。

ハッシュテーブル上の反復

ハッシュテーブルのエントリを反復して操作する手段が一通りあり、
一番シンプルなのがmaphashを使うことだそう。
とりあえず使い方の例がこんな感じのようです。

CL-USER> (maphash #'(lambda (k v) (format t "~a => ~a~%" k v)) *h*)
FOO => BAR
FIZZ => BAZZ
NIL
CL-USER>

反復処理の最中にハッシュテーブルの要素を追加したり削除したりしたらどうなるかわからないそうです。
とにかくそういう意地悪はやめたほうがよさそうであるとのこと。
ただし例外として、現在のエントリの値を変更するにはsetfがgethashとの組み合わせで使え、
現在のエントリを削除するにはremhashが使えるそうです。

とりあえずこんなもんでこの章は終わりです。
次の章ではいよいよリストの話。おやすみなさい。