逆引きxyzzy lisp(配列、ハッシュ、キュー) - circleratio/xyzzy GitHub Wiki

目次

配列

配列の新規作成

(make-array '(3 2))
=> #2A((nil nil) (nil nil) (nil nil))

初期値を設定するには,initial-element(すべての要素に同じ値を設定)または initial-contents (要素ごとに異なる値を設定) オプションを使う.

(make-array '(3 2) :initial-element 0)
=> #2A((0 0) (0 0) (0 0))

(make-array '(2 3) :initial-contents '((a b c) (d e f)))
=> #2A((a b c) (d e f))

直接数値を書くのであれば、次のようにすればよい。

#(1 2 3)
=> #(1 2 3)

配列に値を設定する

この操作は破壊的なので注意.

(setq mat (make-array '(3 2)))
=> #2A((nil nil) (nil nil) (nil nil))

(setf (aref mat 2 1) 1)
=> 1

mat
=> #2A((nil nil) (nil nil) (nil 1))

配列の値を参照する

(setq mat (make-array '(3 2)))
=> #2A((nil nil) (nil nil) (nil nil))

(setf (aref mat 1 0) 10)
=> 10

mat
=> #2A((nil nil) (10 nil) (nil nil))

(aref mat 1 0)
=> 10

配列の大きさを調べる

次元ごとに調べることもできるし(array-dimension)、全部まとめてリストで求めることもできる。

(setq mat (make-array '(2 3) :initial-contents '((a b c) (d e f))))
=> #2A((a b c) (d e f))

(array-dimension mat 0)
=> 2

(array-dimension mat 1)
=> 3

(array-dimensions mat)
=> (2 3)

要素数の合計を調べるには array-total-size を使う。

(array-total-size mat)
=> 6

ハッシュ

ハッシュを作成,削除する

作成は make-hash-table, 削除は clrhash を使う.

(setq hash (make-hash-table :test #'equal))
=> #<hashtable 46666404>

(clrhash hash)
=> #<hashtable 46666404>

ハッシュに値を追加,取得,削除する

値を追加するには setf, 取得するには gethash, 削除するには remhash を使う.

(setf (gethash "abc" hash) 100)
=> 100

(gethash "abc" hash)
=> 100

(remhash "abc" hash)
=> t

(gethash "abc" hash)
=> nil

ハッシュ内にキーが存在するかどうか調べる

gethash が nil を返すかどうかで判定する.

(defun hash-key-exist-p (key hash)
  (if (gethash key hash) t nil))

使用例は以下.

(setq hash (make-hash-table :test #'equal))
=> #<hashtable 46666404>

(setf (gethash "abc" hash) 100)
=> 100

(hash-key-exist-p "abc" hash)
=> t

(hash-key-exist-p "def" hash)
=> nil

ハッシュ全体に対して,ある操作を行う

maphash を使う.

(setq hash (make-hash-table :test #'equal))
=> #<hashtable 46666380>

(setf (gethash "a" hash) 100)
(setf (gethash "b" hash) 200)
(setf (gethash "c" hash) 300)

(maphash #'(lambda (key val) (format t "key = ~A, val = ~A~%" key val)) hash)
=> key = a, val = 100
   key = b, val = 200
   key = c, val = 300

ハッシュのファイルへの保存・復元

xyzzy lisp はハッシュは read/write してくれない(CLでは実装依存)ので,自分で書く.

(defun dump-hash (hash file)
  (with-open-file (stream file :direction :output :if-exists :supersede)
    (write (let ((alist nil))
             (maphash #'(lambda (key val)
                          (pushnew (cons key val) alist))
                      hash)
             alist) :stream stream)))

(defun restore-hash (file)
  (let ((alist (with-open-file
                   (stream file :direction :input)
                 (read stream)))
        (hash (make-hash-table :test #'equal)))
    (list-to-hash hash alist)))

(defun list-to-hash (hash alist)
  (let* ((p (car alist)) (key (car p)) (val (cdr p)))
    (if (equal p nil)
        hash
      (progn
        (setf (gethash key hash) val)
        (list-to-hash hash (cdr alist))))))

使い方は下記.

(setq hash (make-hash-table :test #'equal))

(setf (gethash "a" hash) 100)
(setf (gethash "b" hash) 200)
(setf (gethash "c" hash) 300)

(dump-hash hash "c:/temp/hash.dmp")
=> (("c" . 300) ("b" . 200) ("a" . 100))

(setq hash (restore-hash "c:/temp/hash.dmp"))
=> #<hashtable 46666284>

(gethash "b" hash)
=> 200

ハッシュの大きさを調べる

要素数を数えるには hash-table-count を使う。

(hash-table-count hash)
=> 1

ハッシュテーブルの大きさを調べるには hash-table-size を使う。

(hash-table-size hash)
=> 17

ハッシュをリストに変換する

(defun hash-keys (hash)
  (let ((keys))
    (maphash #'(lambda (key val)
		 (setq keys (cons key keys))) hash)
    keys))

(defun hash-values (hash)
  (let ((vals))
    (maphash #'(lambda (key val)
		 (setq vals (cons val vals))) hash)
    vals))

(defun hash-to-alist (hash)
  (let ((alist))
    (maphash #'(lambda (key val)
		 (setq alist (acons key val alist))) hash)
    alist))

使用例:

(setq hash (make-hash-table :test #'equal))
=> #<hashtable 46666428>

(setf (gethash "a" hash) 100)
(setf (gethash "b" hash) 200)
(setf (gethash "c" hash) 300)

(hash-keys hash)
=> ("c" "b" "a")

(hash-values hash)
=> (300 200 100)

(setq al (hash-to-alist hash))
=> (("c" . 300) ("b" . 200) ("a" . 100))

(assoc "a" al :test #'equal)
=> ("a" . 100)

ハッシュを値で降順、値が等しい場合キーで昇順にソートする

逆引きxyzzy lisp(リスト)の項で定義した sort-alist 関数を活用する.

(defun sort-alist (alist cmp-car cmp-cdr)
  (labels ((compare-alist (e1 e2 cmp-car cmp-cdr)
             (let ((car1 (car e1))
                   (car2 (car e2))
                   (cdr1 (cdr e1))
                   (cdr2 (cdr e2)))
               (if (equalp cdr1 cdr2)
                   (funcall cmp-car car1 car2)
                 (funcall cmp-cdr cdr1 cdr2)))))
    (sort alist (lambda (e1 e2)
                  (compare-alist e1 e2 cmp-car cmp-cdr)))))

(defun hash-to-alist (hash)
  (let ((alist))
    (maphash #'(lambda (key val)
     (setq alist (acons key val alist))) hash)
    alist))

(defun sort-hash (hash cmp-car cmp-cdr)
  (sort-alist (hash-to-alist hash) cmp-car cmp-cdr))

使用例:

(setq hash (make-hash-table :test #'equal))

(setf (gethash "abc" hash) 100)
(setf (gethash "bca" hash) 200)
(setf (gethash "cab" hash) 300)
(setf (gethash "acb" hash) 200)
(setf (gethash "bac" hash) 100)
(setf (gethash "cba" hash) 500)

(sort-hash hash #'string< #'>)
=> (("cba" . 500) ("cab" . 300) ("acb" . 200) ("bca" . 200) ("abc" . 100) ("bac" . 100))

ハッシュの要素をランダムに抽出する

「ハッシュをリストに変換する」の項で定義した hash-keys 関数を使う.

(setq hash (make-hash-table :test #'equal))

(setf (gethash "abc" hash) 100)
(setf (gethash "bca" hash) 200)
(setf (gethash "cab" hash) 300)
(setf (gethash "acb" hash) 200)
(setf (gethash "bac" hash) 100)
(setf (gethash "cba" hash) 500)

(defun hash-keys (hash)
  (let ((keys))
    (maphash #'(lambda (key val)
                 (setq keys (cons key keys))) hash)
    keys))
=> hash-keys

(hash-keys hash)
=> ("cab" "abc" "bca" "bac" "acb" "cba")

(let ((keys (hash-keys hash)))
  (nth (random (length keys)) keys))
=> "acb"

(gethash
 (let ((keys (hash-keys hash)))
   (nth (random (length keys)) keys))
 hash)
=> 200
t

連想リストからハッシュを作る

(defun alist-to-hash (alist)
  (let ((hash (make-hash-table :test #'equal)))
    (map 'list (lambda (x) (setf (gethash (car x) hash) (cdr x))) alist)
    hash))

使用例:

(setq alist '(("cba" . 500) ("cab" . 300) ("acb" . 200) ("bca" . 200) ("abc" . 100) ("bac" . 100)))
=> (("cba" . 500) ("cab" . 300) ("acb" . 200) ("bca" . 200) ("abc" . 100) ("bac" . 100))

(setq h (alist-to-hash '(("cba" . 500) ("cab" . 300) ("acb" . 200) ("bca" . 200) ("abc" . 100) ("bac" . 100))))
=> #<hashtable 68489820>

(gethash "acb" h)
=> 200

複数のハッシュをマージする

「ハッシュを連想リストに変換⇒連想リストをマージ⇒連想リストをハッシュに変換」の手順で行う.

(defun hash-to-alist (hash)
  (let ((alist))
    (maphash #'(lambda (key val)
                 (setq alist (acons key val alist))) hash)
    alist))
=> hash-to-alist

(defun alist-to-hash (alist)
  (let ((hash (make-hash-table :test #'equal)))
    (map 'list (lambda (x) (setf (gethash (car x) hash) (cdr x))) alist)
    hash))
=> alist-to-hash

(defun merge-hash (hash1 hash2)
  (alist-to-hash
   (append (hash-to-alist hash1) (hash-to-alist hash2))))
=> merge-hash

使用例:

(setq hash1 (make-hash-table :test #'equal))
(setq hash2 (make-hash-table :test #'equal))

(setf (gethash "abc" hash1) 100)
(setf (gethash "bca" hash1) 200)
(setf (gethash "cab" hash1) 300)
(setf (gethash "acb" hash2) 200)
(setf (gethash "bac" hash2) 100)
(setf (gethash "cba" hash2) 500)

(hash-to-alist hash1)
=> (("cab" . 300) ("abc" . 100) ("bca" . 200))

(hash-to-alist hash2)
=> (("bac" . 100) ("cba" . 500) ("acb" . 200))

(setq h (merge-hash hash1 hash2))
=> #<hashtable 67048076>

(gethash "cab" h)
=> 300
   t

(gethash "bac" h)
=> 100
   t

キュー

(defstruct Queue
  (front nil)
  (rear nil))
=> #<structure-definition: Queue>

(defun enqueue (queue item)
  (let ((cell (list item)))
    (if (Queue-front queue)
	(setf (cdr (Queue-rear queue)) cell)
      (setf (Queue-front queue) cell))
    (setf (Queue-rear queue) cell)))

(defun dequeue (queue)
  (if (Queue-front queue)
      (prog1 (pop (Queue-front queue))
	(unless (Queue-front queue)
	  (setf (Queue-rear queue) nil)))))

(setq *queue* (make-Queue))
=> #S(Queue front nil rear nil)

(dotimes (x 5) (enqueue *queue* x))
=> nil

*queue*
=> #S(Queue front (0 1 2 3 . #1=(4)) rear #1#)

(dequeue *queue*)
=> 0

(dequeue *queue*)
=> 1

*queue*
=> #S(Queue front (2 3 . #1=(4)) rear #1#)
⚠️ **GitHub.com Fallback** ⚠️