最終更新: 2012-07-04
Common Lispに関するメモ。
各項目の順番に意味はない。
- 整数の論理演算,ビット操作
- 実践CommonLispまとめ
- format関数の指示子
- 要素がリストのリストを,要素の長さでソートする
- ソートする (sort, stable-sort)
- リストから条件にあった要素を取り除く
- リストの中にある要素の位置を返す
- 後ろからn番目までの要素を取り出す
- n番目以降のcdrを取り出す
- prog1
- multiple-value-*
- maphash
- リスト構造を分解して変数に束縛する (destructuring-bind)
- キーワードパラメータ (&key)
- map関数
- 関数呼び出しのための関数 (apply, funcall)
- 局所関数(labels)
- ldb, byte
- do
- 値をずらす (shiftf)
- 場所と場所とで値を入れ替える (rotatef)
- setf
- slimeの再起動
- モジュール
- リストの後ろの要素を取り除いたリストを返す関数
- マクロ (defmacro)
- apply, funcall関数
- 配列を作る
- 配列の初期化
- 回数を指定した繰り返し (dotimes)
- 何も返さない関数
- 回数を決めずループ
- 文字列を数に変換する
- nilじゃなくて0を返す
- yes or no
- sbclスクリプト
整数の論理演算,ビット操作
| logand &rest integers |
ビットごとの論理積を返す |
| logior &rest integers |
ビットごとの論理和を返す |
| logxor &rest integers |
ビットごとの排他的論理和を返す |
| lognot integer |
ビットごとの論理的な否定を返す |
| logbitp index integer |
index 番目のビットが 1 ならば真を返す |
| logcount integer |
integer が正ならば 1 のビットの数を、負ならば 0 のビットの数を返す |
| ash integer count |
count が正ならば count ビットだけ左シフト、負ならば右シフトする |
format関数の指示子
使ったもの,使いそうなものだけ載せる。
全ての指示子はチルダ(~)から始まり,指示子を表す1つの文字で終わる。
~%は改行の指示子である。
~%は印字の整形に頻出するため,始めに説明した。
あるパラメータを指定するとき,
その前のパラメータを省略したいときには,指定しないパラメータに対してコンマ(,)が必要。
~$
小数点数の印字をする。デフォルトでは小数点以下2桁を印字する。
CL-USER> (format t "~$~%" pi)
3.14
NIL
小数点以下5桁を印字するには「~5$」と指示する.
CL-USER> (format t "~5$~%" pi)
3.14159
NIL
~f
桁数を制御するパラメータを2つめに取る。
あるパラメータを指定するとき,
その前のパラメータを省略したいときには,指定しないパラメータに対してコンマ(,)が必要。
CL-USER> (format t "~,5f~%" pi)
3.14159
NIL
~a, ~s
~aが最も汎用な指示子。
フォーマット引数をどんな種類であれ1つ消費し,
人に読める形で出力する。
~sは~aと似ているが,readで読み戻せるような出力を試みる。
文字列はダブルクォート(")で囲われ,
シンボルは必要に応じてパッケージ修飾子が付き,
read可能な表現を持たない指示子は,#<> というシンタックスで印字される。
~a, ~s共に,コロン修飾子をつけると,nilを()と印字する
CL-USER> (format t "~%a~%")
a
NIL
CL-USER> (format t "~&a~&")
a
NIL
~%, ~&
どちらも改行を出力するが,
~%は常に新しい行を出力し,~&は行頭でないときのみ改行する。
~d, ~x, ~o, ~b
~d: 10進
~x: 16進
~o: 8進
~b: 2進
要素がリストのリストを,要素の長さでソートする
sortを使うか,stable-sortを使うかは適宜変更する。
CL-USER> (stable-sort (copy-list '((0 2 1 5 7) (3) (4 6))) #'< :key #'length)
((3) (4 6) (0 2 1 5 7))
sort関数,stable-sort関数
sort関数は破壊的にリストをソートする。
stable-sort関数とsort関数の違いは,
等価と判断された要素の順序が入れ替わらない点。
(??シーケンスはリストじゃないとダメ?)
(sort シーケンス 述語 &key (:key 関数))
例
CL-USER> (sort (copy-list '(0 2 1 5 4 3)) #'<)
(0 1 2 3 4 5)
CL-USER> (sort (copy-list '(0 2 1 5 4 3)) #'>)
(5 4 3 2 1 0)
リストから条件にあった要素を取り除く
(remove-if 条件 リスト)
リストから要素(1 4)を取り除く。
CL-USER> *a*
((1 4) (4 2) (2 1))
CL-USER> (remove-if (lambda (a) (equal a '(1 4))) *a*)
((4 2) (2 1))
リストの中にある要素の位置を返す
(position ITEM LIST)
CL-USER> (position 1 '(0 0 0 0 0 1))
5
後ろからn番目までの要素を取り出す
(last list n)
n番目以降のcdrを取り出す
(nthcdr n list)
CL-USER> (nthcdr 3 '(0 1 2 3 4))
(3 4)
prog1
(prog1 式1 式2 ... 式n)
式1から式nまでを順に評価し,式1の値を返す。
multiple-value-*
multiple-value-setq
多値関数の返り値を変数に代入する。
CL-USER>
(let ((x nil) (y nil))
(multiple-value-setq (x y) (ceiling 7 3))
(list x y))
(3 -2)
値が足りなくなるとnilを代入する。
multiple-value-bind
letを一般化したもの。
CL-USER>
(multiple-value-bind (x y z) (ceiling 3 2)
(list x y z))
(2 -1 NIL)
multiple-value-list
多値関数の返り値(複数個の値)をリストにして返す。
CL-USER> (ceiling 7 3)
3
-2
CL-USER> (multiple-value-list (ceiling 7 3))
(3 -2)
multiple-value-prog1
(multiple-value-prog1 式1 式2 ... 式n)
式1から式nまで評価し,式1の値を返す。
prog1との違いは,式1が多値を返すなら,multiple-value-prog1も多値を返す。
maphash
(maphash 関数 ハッシュテーブル)
ハッシュテーブルのエントリ(キーと値のペア)ごとに,
そのキーと値を引数として関数を呼び出す。
maphashは値としてnilを返す。
CL-USER> (defvar *h* (make-hash-table))
*H*
CL-USER> (setf (gethash 'foo *h*) 1
(gethash 'bar *h*) 2)
2
CL-USER> (let ((x nil))
(maphash #'(lambda (key value)
(push (cons key value) x))
*h*)
x)
((BAR . 2) (FOO . 1))
リスト構造を分解して変数に束縛する(destructuring-bind)
基本的な構造
(destructuring-bind (parameter*) list
body-form*)
例
CL-USER> (destructuring-bind (x y z) (list 1 2 3)
(list :x x :y y :z z))
(:X 1 :Y 2 :Z 3)
CL-USER> (destructuring-bind (x y z) (list 1 (list 2 20) 3)
(list :x x :y y :z z))
(:X 1 :Y (2 20) :Z 3)
CL-USER> (destructuring-bind (x (y1 y2) z) (list 1 (list 2 20) 3)
(list :x x :y1 y1 :y2 y2 :z z))
(:X 1 :Y1 2 :Y2 20 :Z 3)
CL-USER> (destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2 20) 3)
(list :x x :y1 y1 :y2 y2 :z z))
(:X 1 :Y1 2 :Y2 20 :Z 3)
CL-USER> (destructuring-bind (x (y1 &optional y2) z) (list 1 (list 2) 3)
(list :x x :y1 y1 :y2 y2 :z z))
(:X 1 :Y1 2 :Y2 NIL :Z 3)
CL-USER> (destructuring-bind (&key x y z) (list :x 1 :y 2 :z 3)
(list :x x :y y :z z))
(:X 1 :Y 2 :Z 3)
CL-USER> (destructuring-bind (&key x y z) (list :z 1 :y 2 :x 3)
(list :x x :y y :z z))
(:X 3 :Y 2 :Z 1)
CL-USER> (destructuring-bind (&key x y z) (list :z 1 :y 2 :x 3)
(+ x y z))
6
キーワードパラメータ
CL-USER> (defun foo (&key a b c) (list a b c))
FOO
CL-USER> (foo :c 3 :a 1 :b 2)
(1 2 3)
CL-USER> (foo :a 1 :c 3)
(1 NIL 3)
CL-USER> (foo)
(NIL NIL NIL)
変数a, b, cの値は対応するキーワードに続く値に束縛される。
デフォルト値はデフォルトではnilだが,これではnilをパラメータに渡したのと区別がつかない。
これを区別するためにsupplied-pパラメータを使う。
(defun foo (&key a (b 20) (c 30 c-p)) (list a b c c-p))
上記ではbのデフォルト値は20,cのデフォルト値は30で,変数c-pがsupplied-pパラメータである。
cに値が渡されたら真,値が渡されなければ偽となる。
CL-USER> (foo :c 3 :a 1 :b 2)
(1 2 3 T)
CL-USER> (foo :a 1 :c 3)
(1 20 3 T)
CL-USER> (foo)
(NIL 20 30 NIL)
map関数
共通することは,繰り返し呼び出したい関数を第1引数で指定し,
呼び出しごとに受け渡す引数を第2引数以下でリストにより指定する。
mapcar
第2引数以下のリストの各要素を引数として,
指定された関数を繰り返し呼び出す。
第2引数以下のリストの長さが全て同じでないときは,
一番短いリストの長さ分だけ関数を繰り返す。
CL-USER> (mapcar 'cons '(a b c) '(1 2 3))
((A . 1) (B . 2) (C . 3))
CL-USER> (mapcar 'cons '(a b c) '(1 2 3 4 5))
((A . 1) (B . 2) (C . 3))
CL-USER> (mapcar #'(lambda (x) (* x x)) '(7 2 3 8))
(49 4 9 64)
maplist
mapcarと似ているが,各リストの要素ではなく,
cdrを次々と取ってそれを引数として関数を呼び出す。
CL-USER> (maplist 'cons '(a b c) '(1 2 3))
(((A B C) 1 2 3) ((B C) 2 3) ((C) 3))
CL-USER> (maplist #'(lambda (x) (length x)) '(7 2 3 8))
(4 3 2 1)
mapcar と maplistは,呼び出しごとの関数の値をリストにする。
副作用を起こすためにだけ関数を呼び出したい際には,
まったく使われないリストを作るため,無駄である。
このような場合に,mapcar の代わりに mapcを,
maplist の代わりに mapl を使う。
mapcan, mapcon
- mapcar::指定された関数の呼び出し方は mapcar と同じ。
- mapcon::指定された関数の呼び出し方は maplist と同じ。
関数の値をリストにするのではなく,
関数の値がすべてリストだと仮定して,
それらの要素すべてを nconc したリストを値として返す。
下記の例では,cdr が3回呼び出されている。
CL-USER> (mapcan 'cdr '((a b) (c) (1 2 3)))
(B 2 3)
引数はそれぞれ,(a b), (c), (1 2 3)。
cdrはそれぞれ,(b), nil, (2 3)。
CL-USER> (setq x '((a b) (c) (1 2 3)))
((A B) (C) (1 2 3))
CL-USER> (mapcan 'cdr x)
(B 2 3)
CL-USER> x
((A B 2 3) (C) (1 2 3))
関数呼び出しのための関数(apply,funcall)
funcall
呼び出す関数が取る引数の数が分かっているときに使う
CL-USER> (let ((f '+)) (funcall f 1 2 3 4))
10
CL-USER> (let ((f '*)) (funcall f 1 2 3 4))
24
apply
引数の最後はリスト。
第2引数から第n-1引数までの値,そして第n引数のリストの各要素が,
呼び出される関数へ渡される。
CL-USER> (let ((f '+)) (apply f '(1 2 3 4)))
10
CL-USER> (let ((f '*)) (apply f 1 2 '(3 4)))
24
CL-USER> (let ((f '*)) (apply f 1 2 3 4 nil))
24
局所関数(labels)
局所的に関数を定義する。
(labels ((関数名1 ラムダリスト 式 ... 式)
...
(関数名m ラムダリスト 式 ... 式))
式1 ... 式n)
関数1〜関数mはlabels内でのみ使える。
ldb, byte
byte
引数:ビット数(SIZE),ビット位置(POSITION)
POSITIONから,SIZE分のビットを指定する「バイト指定子(byte specifier)」を返す。
ldb
引数:バイト指定子,整数
入力された整数のビット表現から,バイト指定子で指定されたビット列を右詰めにしたものを整数として返す。
CL-USER> (ldb (byte 3 0) #b10111)
7
CL-USER> (ldb (byte 3 1) #b10111)
3
CL-USER> (ldb (byte 3 2) #b10111)
5
do
(do (variable-definition*)
(end-test-form result-form*)
statement*)
variable-definitionは,
(var init-form step-form)
end-test-formがnilの間はstatementが順番に評価される。
init-formがループ開始時に評価され,その結果がvarに束縛される。
次のループ処理の前にstep-formが評価され,その結果がvarに束縛される。
値をずらす (shiftf)
CL-USER> (let ((a 0)(b 1)(c 2)(d 3))
(shiftf a b c d)
(format t "~d ~d ~d ~d ~%" a b c d))
1 2 3 3
NIL
CL-USER> (let ((a 0)(b 1)(c 2)(d 3))
(shiftf a b c d 4)
(format t "~d ~d ~d ~d ~%" a b c d))
1 2 3 4
NIL
場所と場所とで値を入れ替える (rotatef)
(rotatef a b)
以下の式と等価
(let ((tmp a))
(setf a b b tmp) nil)
例
CL-USER> (let ((a 0)(b 1)(c 2)(d 3))
(rotatef a b c d)
(format t "~d ~d ~d ~d ~%" a b c d))
1 2 3 0
NIL
setf
(setf 場所 値)
| 単純な値 |
(setf x 10) |
| 配列 |
(setf (aref a 0) 10) |
| ハッシュテーブル |
(setf (gethash 'key hash) 10) |
| 'field'という名前のスロット(オブジェクト) |
(setf (field o) 10) |
slimeの再起動
emacsでslimeを起動させた状態で
M-x slime-restart-inferior-lisp
モジュール
モジュールとするプログラムファイルの先頭に
(provide 'モジュール名)
と書く。
モジュールを使いたいプログラムでは
(require 'モジュール名 "ファイル名")
とかく。
リストの後ろの要素を取り除いたリストを返す関数
非破壊的
CL-USER> (butlast '(1 2 3 4 5))
(1 2 3 4)
破壊的
CL-USER> (defvar *a* '(1 2 3 4 5))
CL-USER> (nbutlast *a*)
(1 2 3 4)
CL-USER> *a*
(1 2 3 4)
第2引数に整数を入れると,その個数の要素を取り除いたリストを返す。
マクロ(defmacro)
マクロは式を作る式。
マクロで作られた式をREPLが評価することで結果が返る。
言語のシンタックスを拡張する手段を提供する。
バッククォート記法
REPLがバッククォート(`)を読み込むと,
それをリスト構造を生成するためのコードに変換する。
コンマ(,)で部分的にアンクォートできる。
(コンマが付いた部分を評価する。)
アットマーク(@)なしの場合は,コンマに続く部分式は単純にその場に埋め込まれるが,
アットマークありの場合は,その値(リストでなくてはならない)が外側のリストに挿入される。
| バッククォートシンタックス |
等価なリスト構築コード |
結果 |
| `(a (+ 1 2) c) |
(list 'a '(+ 1 2) 'c) |
(a (+ 1 2) c) |
| `(a ,(+ 1 2) c) |
(list 'a (+ 1 2) 'c) |
(a 3 c) |
| `(a (list 1 2) c) |
(list 'a '(list 1 2) 'c) |
(a (list 1 2) c) |
| `(a ,(list 1 2) c) |
(list 'a (list 1 2) 'c) |
(a (1 2) c) |
| `(a ,@(list 1 2) c) |
(append 'a (list 1 2) 'c) |
(a 1 2 c) |
apply, funcall関数
| funcall |
第1引数に関数を与えて,その関数に残りの引数を与え評価する |
| apply |
funcallの第2引数がリストになったバージョン |
例
(funcall '+ 1 1 1) ;; => 3
(apply '+ '(1 1 1)) ;; => 3
配列を作る
1次元配列。要素数3
(make-array 3)
2次元配列。3*3
(make-array '(3 3))
3次元配列。2*3*4
(make-array '(2 3 4))
配列の初期化
すべての要素を同じ値で初期化
(make-array 3 :initial-element 0)
各要素を任意の値で初期化
(make-array 4 '(0 1 2 3))
回数を指定した(dotimes)
(dotimes (var limit result) S式 ...)
limitは整数。0〜limit-1までループ。
繰り返しごとにvarに0〜limit-1までバインドされる。
最後にresultを返す。
何も返さない関数
(values)
valuesは値をそのまま返す。
> (values 1)
1
回数を決めずループ
loopを使う。
returnが呼び出されるまで繰り返し実行する。
(loop
処理*
(if 条件 (return 値*)))
文字列を数に変換する
parse-integer
文字列から整数を読み取る。
C言語のatoiに相当。
| STRING |
対象の文字列 |
| :start |
読み取り開始位置 |
| :end |
読み取り終了位置 |
| :radix |
基数 |
| :junk-allowed |
non-nilなら前後の空白を無視します |
多値で二つの値を返します。
一つ目は読み込んだ整数値です。読み込めなかった場合はnilを返します。
二つ目は読んだ最後の文字の次の文字のインデックスです。文字列の最後まで読
んだ場合は文字列の長さになります。
例:
(parse-integer "16")
=>16
=>2
(parse-integer "016")
=>16
=>3
(parse-integer " 16 ")
=>不正な数値の形式です: " 16 "
(parse-integer " 16 " :junk-allowed t)
=>16
=>3
備考:
Common Lispの同名の関数と動作が異なります。
Common Lispのparse-integerは:junk-allowedがnilでも前後の空白を無視し、
non-nilのときは前後の空白のみならず数字以外の文字も無視します。
nilじゃなくて0を返す
(or nilを返す関数かなんか 0)
orは最初にnil以外になった値を返す。
すべての評価結果がnilの場合にはnilを返す。
yes or no
(y-or-n-p フォーマット指示子 ARGS)
標準入出力でy,nを打つダイアログの表示。
フォーマット指示子でフォーマットを指定できる。
指示子なくてもいい。
例
(y-or-no-p "Ripped [y/n]")
y: t
n: nil
他には
(yes-or-no-p)
(no-or-yes-p)
(yes-or-no-cancel-p)
Lispスクリプト
.sbclrcに以下を記入
;;; If the first user-processable command-line argument is a filename,
;;; disable the debugger, load the file handling shebang-line and quit.
(let ((script (and (second *posix-argv*)
(probe-file (second *posix-argv*)))))
(when script
;; Handle shebang-line
(set-dispatch-macro-character #\# #\!
(lambda (stream char arg)
(declare (ignore char arg))
(read-line stream)))
;; Disable debugger
(setf *invoke-debugger-hook*
(lambda (condition hook)
(declare (ignore hook))
;; Uncomment to get backtraces on errors
;; (sb-debug:backtrace 20)
(format *error-output* "Error: ~A~%" condition)
(quit)))
(load script)
(quit)))
Lispスクリプト(hello.lisp)は次のように作る
#!/usr/local/bin/sbcl --noinform
(write-line "Hello, World!")
実行
% scbl ./hello.lisp
Hello, World!
最終更新:2012年11月07日 19:30