逆引きxyzzy lisp(リスト) - circleratio/xyzzy GitHub Wiki
- リストの作成
- リストを調べる
- リストの変更
- リストの一部を取り出す
- リスト同士の操作
- リストの並び替え
- 連想リスト
- 属性リスト
- リストの外部出力
- リストをコピーする
- 数字を要素とするリストを、数値に変換する
(list 1 2 3)
=> (1 2 3)
'(1 2 3)
=> (1 2 3)
(make-list 5)
=> (nil nil nil nil nil)
(make-list 5 :initial-element 7)
=> (7 7 7 7 7)
(cons 'a nil)
=> (a)
(defun seq (n)
(labels((seq-reverse (n)
(if (= n 0) nil
(cons n (seq-reverse (- n 1))))))
(reverse (seq-reverse n))))
(seq 10)
=> (1 2 3 4 5 6 7 8 9 10)
(length '(1 2 3 4 5))
=> 5
(defun singlep (l)
(and (consp l) (null (cdr l))))
(singlep '(1 2))
=> nil
(singlep '(1))
=> t
length を使うのは非効率. 最初の要素だけを見ればよい.
(find 1 '(1 2 3 4 5))
=> 1
位置を調べたい場合は position を使うこと.
(position 4 '(5 4 3 2 1))
=> 1
文字列の場合は、デフォルトの比較関数 eq が使えないので注意。
(find "apple" '("grape" "orange" "apple"))
nil
(find "apple" '("grape" "orange" "apple") :test 'equal)
=> "apple"
各要素(リストになっている)の先頭の要素を取り出して、条件と比較する例。
(member 'c '((a b) (c d) (e f)) :key #'car)
=> ((c d) (e f))
(defun check-duplicate (lst)
(if (eql lst nil)
nil
(let ((var (car lst))
(l (cdr lst)))
(if (find var l)
t
(check-duplicate l)))))
(check-duplicate '(1 2 3 4 5))
=> nil
(check-duplicate '(1 1 2 3))
=> t
(defun longerp (la lb)
(cond ((null la) nil)
((null lb) t)
(t (longerp (cdr la) (cdr lb)))))
(longerp '(1 2 3) '(1 2 3 4 5))
=> nil
(longerp '(1 2 3 4 5 6) '(1 2 3 4 5))
=> t
(longerp '(1 2 3) '(1 2 3))
=> nil
(count 'x '(x y z x y z))
=> 2
(position 5 '(1 2 3 4 5 6))
=> 4
(apply #'max '(1 2 3 4 5))
=> 5
(apply #'min '(1 2 3 4 5))
=> 1
(apply #'+ '(1 2 3 4 5))
=> 15
(defun adjacent (x y lst)
(cond ((null lst) nil)
((and (eql x (car lst)) (eql y (cadr lst)) t))
((and (eql y (car lst)) (eql x (cadr lst)) t))
(t (adjacent x y (cdr lst)))))
(adjacent 'x 'y '(x y z z y))
=> t
(adjacent 'x 'y '(x p y z z y))
=> nil
(defun before (x y lst)
(let ((p1 (position x lst))
(p2 (position y lst)))
(cond ((null p1) nil)
((null p2) nil)
((< p1 p2) t)
(t nil))))
(before 'x 'y '(a b c x y z))
=> t
(before 'x 'y '(a b c x - z))
=> nil
(before 'x 'y '(a b c y x z))
=> nil
(notany #'evenp '(1 3 5))
=> t
(notany #'evenp '(1 2 3 4 5))
=> nil
(notevery #'evenp '(1 2 3 4 5))
=> t
(notevery #'evenp '(2 4))
=> nil
(some #'evenp '(1 2 3 4 5))
=> t
(some #'evenp '(1 3 5))
=> nil
(cons 4 '(1 2 3))
=> (4 1 2 3)
(fill '(a b c d e) 'x)
=> (x x x x x)
意味はないと思うが…。
(setq x '(1 2 3))
=> (1 2 3)
(setq x nil)
=> nil
(setq lst '(1 2 3 4 5))
=> (1 2 3 4 5)
(setf (first lst) 5)
=> 5
lst
=> (5 2 3 4 5)
(setf (nth 4 lst) 9)
=> 9
lst
=> (5 2 3 4 9)
(subst 1 2 '(0 1 2 (1 2 (1 2)) 3 4 5))
=> (0 1 1 (1 1 (1 1)) 3 4 5)
(substitute 1 2 '(0 1 2 (1 2 (1 2)) 3 4 5))
=> (0 1 1 (1 2 (1 2)) 3 4 5)
subst と substitute の違いは、前者がリスト構造を再帰的にたどること。
subst, substitute には -if, -if-not もある。
(substitute-if 0 #'(lambda (x) (and (integerp x) (evenp x))) '(0 1 2 (1 2 (1 2)) 3 4 5))
=> (0 1 0 (1 2 (1 2)) 3 0 5)
(subst-if 0 #'(lambda (x) (and (integerp x) (evenp x))) '(0 1 2 (1 2 (1 2)) 3 4 5))
=> (0 1 0 (1 0 (1 0)) 3 0 5)
テスト条件には、リストの末端要素だけでなく部分リストも与えられるので、型チェックを忘れないこと。
(defun swap (n m ls)
(let ((new-ls (copy-list ls)))
(setf (nth n new-ls) (nth m ls))
(setf (nth m new-ls) (nth n ls))
new-ls))
(swap 1 5 '(0 1 2 3 4 5))
=> (0 5 2 3 4 1)
(remove nil '(nil 1 2 3 nil nil))
=> (1 2 3)
(defun take (n lst)
(if (= n 0) nil
(cons (car lst) (take (1- n) (cdr lst)))))
(defun remove-nth (n lst)
(append (take (1- n) lst) (nthcdr n lst)))
(remove-nth 5 '(1 2 3 4 5 6 7 8))
=> (1 2 3 4 6 7 8)
(butlast '(1 2 3 4 5))
=> (1 2 3 4)
(remove-duplicates '(1 1 2 2 1 1 3 3))
=> (2 1 3)
(defun sorted-list-insert (val lst f)
(cond ((null lst) (cons val nil))
((funcall f val (car lst)) (cons val lst))
(t (cons (car lst) (sorted-list-insert val (cdr lst) f)))))
=> sorted-list-insert
(sorted-list-insert 5 '(1 2 3 7 8 9) #'<)
=> (1 2 3 5 7 8 9)
(sorted-list-insert 4 '(1 2) #'<)
=> (1 2 4)
(sorted-list-insert 'j '(a b c x y z) #'string<)
=> (a b c j x y z)
(defun take (n lst)
(if (= n 0) nil
(cons (car lst) (take (1- n) (cdr lst)))))
(defun group (n lst)
(if (null lst) nil
(cons (take n lst) (group n (nthcdr n lst)))))
(group 3 '(1 2 3 4 5 6 7 8 9))
=> ((1 2 3) (4 5 6) (7 8 9))
(group 3 '(1 2 3 4 5 6 7 8 9 10))
=> ((1 2 3) (4 5 6) (7 8 9) (10 nil nil))
(defun take (n lst)
(if (= n 0) nil
(cons (car lst) (take (1- n) (cdr lst)))))
(defun list-cons (lst cars)
(mapcar #'(lambda (l c) (cons c l))
lst
cars))
(defun divide (n lst)
(labels ((divide-sub (m l result)
(if (null l) result
(divide-sub m
(nthcdr m l)
(list-cons result (take m l))))))
(mapcar #'reverse (divide-sub n lst (make-sequence 'list n)))))
(divide 2 '(a 1 b 2 c 3 d 4 e 5))
=> ((a b c d e) (1 2 3 4 5))
(setq l '(a b c d e))
=> (a b c d e)
(rplaca l 'x)
=> (x b c d e)
(rplaca (cddr l) 'y)
=> (y d e)
l
=> (x b y d e)
(setq l '(a b c d e))
=> (a b c d e)
(rplacd l '(x y z))
=> (a x y z)
l
=> (a x y z)
1~10番目までは関数が用意されている.
(setq lst '(1 2 3 4 5 6 7 8 9 10))
(first lst)
=> 1
(second lst)
=> 2
(tenth lst)
=> 10
それ以降については,nth を使う. 最初の要素は「0番目」なので注意.
(nth 5 lst)
=> 6
(last '(1 2 3 4 5))
=> (5)
(car '(1 2 3 4 5))
=> 1
(car (last '(1 2 3 4 5)))
=> 5
(defun random-element (ls)
(nth (random (length ls)) ls))
(random-element '(1 2 3 4 5 6 7 8 9 10))
=> 3
より一般的な考え方に基づく別解。
長さがわからない列において、今まで出てきた要素がそれぞれ等確率で残るようにする手順は、「n番目の要素に出会った際、確率1/nで新しい要素と置き換える」である。
- 最初の要素: 1/1 の確率で残る。
- 2番目の要素: 1/2 の確率で入れ替え→最初の要素が残る確率は 1 - 1/2 = 1/2 となる。
- 3番目の要素: 1/3 の確率で入れ替え→1, 2番目の要素が残る(=入れ替えられない)確率は2/3。それぞれは1/2の確率で残るので、全体としては (2/3)*(1/2) = 1/3 ずつの確率で残る
- n番目の要素: n/1 の確率で入れ替え→1..(n-1)番目の要素が残る(=入れ替えられない)確率は(n-1)/n。それぞれは1/(n-1)の確率で残るので、全体としては {(n-1)/n}*{1/(n-1)} = 1/n ずつの確率で残る。
(defun random-element (ls)
(let ((i 2)
(result (car ls)))
(dolist (e (cdr ls) result)
(if (= 0 (random i)) (setq result e))
(incf i))))
(random-element '(1 2 3 4 5 6 7 8 9 10))
=> 2
ただし、xyzzy lisp 上で時間測定したところ、nth を使った実装のほうが15倍早かった。
(defun take (n lst)
(if (= n 0) nil
(cons (car lst) (take (1- n) (cdr lst)))))
(take 3 '(1 2 3 4 5))
=>(1 2 3)
(nthcdr 3 '(1 2 3 4 5))
=>(4 5)
(member 3 '(1 2 3 4 5))
=> (3 4 5)
(member 3 '(1 2 3 4 5 1 2 3 4 5))
=> (3 4 5 1 2 3 4 5)
(subseq '(1 2 3 4 5 6 7) 3 5)
=> (4 5)
(subseq '(1 2 3 4 5 6 7) 3)
=> (4 5 6 7)
素直に書くなら再帰。
(defun filter (val test lst)
(if (null lst) nil
(if (funcall test (car lst) val)
(cons (car lst) (filter val test (cdr lst)))
(filter val test (cdr lst)))))
(filter 5 #'> '(2 4 6 8 10))
=> (6 8 10)
reduce を使う別解。 こちらのほうが一般解で美しい。
(defun filter (func lst)
(reduce (lambda (x y)
(if (funcall func x)
(cons x y)
y))
lst
:initial-value nil
:from-end t))
(filter #'(lambda (x) (> x 5)) '(2 4 6 8 10))
=> (6 8 10)
(append '(1 2 3) '(4 5 6))
=> (1 2 3 4 5 6)
(concatenate 'list '(1 2 3) '(4 5 6))
=> (1 2 3 4 5 6)
(setq x '(1 2 3))
=> (1 2 3)
(setq y '(4 5 6))
=> (4 5 6)
(nconc x y)
=> (1 2 3 4 5 6)
x
=> (1 2 3 4 5 6)
y
=> (4 5 6)
(setq a1 '(1 2 3 4 5)
a2 '(1 2 3 4 5))
(mapcar #'+ a1 a2)
=> (2 4 6 8 10)
(mapcar #'- a1 a2)
=> (0 0 0 0 0)
(union '(1 2 3) '(2 3 4))
=> (1 2 3 4)
(intersection '(1 2 3) '(2 3 4))
=> (3 2)
(set-difference '(1 2 3 4 5) '(2 3 4 9))
=> (5 1)
(set-exclusive-or '(1 2 3 4 5) '(2 3 4 9))
=> (5 1 9)
mismatch は異なる要素が最初に現れる位置を返す。
(mismatch '(a b c d e) '(a c d e f))
=> 1
(mismatch '(a b c d e) '(a b c d e))
=> nil
(reverse '(1 2 3 4 5))
=> (5 4 3 2 1)
(sort '(3 4 5 6 9 1) #'<)
=> (1 3 4 5 6 9)
(sort '("apple" "grape" "orange" "banana") #'string<)
=> ("apple" "banana" "grape" "orange")
(defun take (n lst)
(if (= n 0) nil
(cons (car lst) (take (1- n) (cdr lst)))))
(defun remove-nth (n lst)
(append (take (1- n) lst) (nthcdr n lst)))
(defun shuffle (lst)
(if (null lst)
nil
(let* ((n (1+ (random (length lst))))
(v (nth (1- n) lst))
(l (remove-nth n lst)))
(cons v (shuffle l)))))
(shuffle '(1 2 3 4 5 6 7 8 9 10))
=> (5 8 3 10 1 7 9 2 4 6)
(setq alist '((a . b) (c . d) (e . f) (g . h)))
=> ((a . b) (c . d) (e . f) (g . h))
(pairlis '(a c e g) '(b d f h))
=> ((g . h) (e . f) (c . d) (a . b))
(setq alist '((a . b) (c . d) (e . f) (g . h)))
=> ((a . b) (c . d) (e . f) (g . h))
(assoc 'a alist)
=> (a . b)
(assoc 'b alist)
=> nil
(rassoc 'd alist)
=> (c . d)
(rassoc 'c alist)
=> nil
文字列をキーにする場合は注意すること.:test を指定しないと期待通りの動きをしない.
(setq alist '(("a" . ".-.")
("b" . "---")
("c" . "...")))
=> (("a" . ".-.") ("b" . "---") ("c" . "..."))
(assoc "a" alist :test #'equal)
=> ("a" . ".-.")
(setq alist '((a . b)))
=> ((a . b))
(acons 'c 'd alist)
=> ((c . d) (a . b))
cdr 部で降順,cdr 部が同じ場合は car 部で降順にソートする例.
(setq alist '(("abc" . 10) ("xcd" . 20) ("cde" . 30) ("dea" . 30) ("eab" . 20)))
=> (("abc" . 10) ("xcd" . 20) ("cde" . 30) ("dea" . 30) ("eab" . 20))
(defun 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))))
(defun sort-alist (alist cmp-car cmp-cdr)
(sort alist (lambda (e1 e2)
(compare-alist e1 e2 cmp-car cmp-cdr))))
(sort-alist alist #'string> #'>)
=> (("dea" . 30) ("cde" . 30) ("xcd" . 20) ("eab" . 20) ("abc" . 10))
属性リストは、キーと値のペアを保存するためのデータ構造。
(key-1 value-1 key-2 value-2 ... key-n value-n)
(setf (get 'taro 'height) 170)
=> 170
(setf (get 'taro 'weight) 60)
=> 60
(setf (get 'taro 'sex) 'male)
=> male
(symbol-plist 'taro)
=> (sex male weight 60 height 170)
(get 'taro 'height)
=> 170
(remprop 'taro 'height)
=> t
(get 'taro 'height)
=> nil
(symbol-plist 'taro)
=> (sex male weight 60)
(defun join (str delimiter vlist)
(let ((v (car vlist)))
(cond
((equal v nil) str)
((equal str "") (join (format nil "~D" v) delimiter (cdr vlist)))
(t (join (concat str delimiter (format nil "~D" v)) delimiter (cdr vlist))))))
(join "" "," '(1 2 3 4 5))
=> "1,2,3,4,5"
copy-list, copy-tree がある。 両者の違いは、 *copy-list はトップレベルのセルだけをコピーする *copy-tree はすべてのセルをコピーする
(copy-list '(1 2 3 4 5))
=> (1 2 3 4 5)
(copy-list '((1 2) (3 4) (5 6))
=> ((1 2) (3 4) (5 6))
(copy-tree '(1 2 3 4 5))
=> (1 2 3 4 5)
(copy-tree '((1 2) (3 4) (5 6))
=> ((1 2) (3 4) (5 6))
(defun numerize (lst)
(labels
((numerize-recur (lst)
(if (null lst) 0
(+ (car lst) (* 10 (numerize-recur (cdr lst)))))))
(numerize-recur (reverse lst))))
numerize
(numerize '(1 2 3))
=> 123