hash table操作めも

要素を引っ張り出すための関数&マクロのメモ。

;; 適当なサンプルデータ
CL-USER> (setq ht (make-hash-table))
#S(HASH-TABLE :TEST EXT:FASTHASH-EQL)

CL-USER> (dolist (x (coerce "my lisp !" 'list))
           (setf (gethash x ht) (char-code x)) )

loop マクロ

loop言語って複雑すぎてすぐ忘れちゃうなぁ。
毎回仕様書を読んでいる気がするでごあす。

CL-USER> (loop for key being each hash-key in ht
               using (hash-value value)
               collect (cons key value))
=> ((#\! . 33) (#\p . 112) (#\s . 115) (#\i . 105) (#\l . 108) (#\  . 32)
    (#\y . 121) (#\m . 109))

maphash

値は返してくれないようだ。(戻り値は常にnil

CL-USER> (let (acc)
           (maphash #'(lambda (k v)
                        (push (cons k v) acc))
                    ht)
           (nreverse acc))
=> ((#\! . 33) (#\p . 112) (#\s . 115) (#\i . 105) (#\l . 108) (#\  . 32)
    (#\y . 121) (#\m . 109))

hash-tableの順序なんて気にかける必要もないけど、一応↑のものと結果を合わせるためにnreverseしております。

with-hash-table-iterator マクロ

大体はmaphashでことは済むと思うけど、このようなマクロも用意されている。

CL-USER> (let (acc)
           (with-hash-table-iterator (iter ht)
             (loop
              (multiple-value-bind (has-next? k v) (iter)
                (unless has-next? (return))
                (push (cons k v) acc) )))
           (nreverse acc))
=> ((#\! . 33) (#\p . 112) (#\s . 115) (#\i . 105) (#\l . 108) (#\  . 32)
    (#\y . 121) (#\m . 109))

第一引数で名前を決めると、その変数に関数を代入してくれる・・・
と思っていたのだけど、そういえばiterをfuncallしなくても使えてるよな、ローカル関数でも定義してるのかな?と思ってマクロを展開してみました。

CL-USER> (macroexpand '(with-hash-table-iterator (iter ht) 'end))
;; (LET ((#:G4483 (SYSTEM::HASH-TABLE-ITERATOR HT)))
;;  (MACROLET ((ITER NIL '(SYSTEM::HASH-TABLE-ITERATE #:G4483))) 'END))
=> T

macrolet使ってるそうです。(結構どうでもいい)