はじめに
Commonlispの小ネタ集です。
マイナー関数や小技、ハマりポイント等を紹介します。適時書き足します。
list
値を書き換え
sublis関数はlistの値を書き換えます。listの内部にあっても書き換えられます。
*(sublis '((1 . 100)) '(100 (1) 1))
;(100 (100) 100)
cdrはcdddrまで、firstはtenthまで
文字通りです。(個人的には3要素超えたらnthやeltなどでアクセスすべきだと思います)
popはnilでもできる
nilをpopするとnilが返ります。
string
連結
concatenate関数
concatenate関数は型を混在できます。
* (concatenate 'string "abc" "def")
;"abcdef"
;concatenateはsequenceを混在できます。
* (concatenate 'string '(#\a #\b #\c) "def")
;"abcdef"
array
eltは汎用的に使える
arrayのアクセスはarefやsvrefで行うと思いますがeltでもアクセスできます。
数学処理
最大公倍数、最大公約数
lcm
最小公倍数
gcd
最大公約数
引数は任意の個数です。
* (lcm 2 5)
;10
* (gcd 20 30)
;10
-関数の特性
-関数は1引数の場合1引数の負を返します。
* (- 10)
;-10
* (- 10 0)
;10
少数を分数にする
rationalize
* (rationalize 1.1)
;11/10
分数から少数に変換した時の桁を増やす
floatは10^-8までしか表現できないため桁数が不足する場合があります。coerseでdouble-floatやlong-floatに変換しましょう。
* (coerce 1/3 'double-float)
;0.3333333333333333d0
素数判定
(sbclのみ)
sbclの組み込み関数でsb-int:positive-primepは自然数が素数であるかを判定します。引数以下の数字で割れるかを確かめているため計算効率は悪いです。
破壊的代入
shiftfマクロ
右側に詰めるように値を入れます。
rotatefマクロ
値を回転させるように入れ替えます。シンボル同士で値を入れ替えたりする場面で使います。
(let* ((a 1)
(b 2)
(c 3))
(shiftf a b c)
(format t "a:~A b:~A c:~A~%" a b c) ;a:2 b:3 c:3
(shiftf a b c)
(format t "a:~A b:~A c:~A~%" a b c)) ;a:3 b:3 c:3
(let* ((a 1)
(b 2)
(c 3))
(rotatef a b c)
(format t "a:~A b:~A c:~A~%" a b c) ;a:2 b:3 c:1
(rotatef a b c)
(format t "a:~A b:~A c:~A~%" a b c)) ;a:3 b:1 c:2
map
map関数のand/orを取る
every
some
everyはand、someはorをとります。
(every #'evenp '(2 4 6));T
(some #'evenp '(1 2 3));T
入出力
###yesかnoの入力を待ちたい
yes-or-no-p y-or-n-p
入力がyesかnoか(yかnか)を判定します。
(yes-or-no-p "yes?no?")
;yes?no? (yes or no)
;a
;Please type "yes" for yes or "no" for no.
;yes?no? (yes or no)
;n
;Please type "yes" for yes or "no" for no.
;yes?no? (yes or no)
;no
;nil
その他
型を調べる
type-of
詳細に調べたいときはdescribe
(type-of t)
;BOOLEAN
*(describe t)
;COMMON-LISP:T
; [symbol]
;
;T names a constant variable:
; Value: T
;
;T names the system-class #<SB-PCL:SYSTEM-CLASS COMMON-LISP:T>:
; Class precedence-list: T
; Direct subclasses: ARRAY, SIMD-PACK, NUMBER, SB-KERNEL::RANDOM-CLASS,
; SB-KERNEL:FDEFN, SB-KERNEL:CODE-COMPONENT,
; WEAK-POINTER, SYSTEM-AREA-POINTER, SYMBOL,
; CHARACTER, SB-PCL::SLOT-OBJECT, SEQUENCE, STREAM,
; FUNCTION
; No direct slots.
再帰関数の展開を見たい
trace
*(defun f (x)
(if (= x 1) 1 (* x (f (1- x)))))
*(trace f)
*(f 5)
;
; 0: (F 5)
; 1: (F 4)
; 2: (F 3)
; 3: (F 2)
; 4: (F 1)
; 4: F returned 1
; 3: F returned 2
; 2: F returned 6
; 1: F returned 24
; 0: F returned 120
*(untrace f)
*(f 5)
;120
常に何かを返す関数が欲しい
constantly関数で作成します。
マクロを関数化したい
macro-functionマクロ
任意の添字付きでループを回したい
(loop :for j :from 1
:for k :in (concatenate 'list "test")
:do(print (cons j k)))
;(1 . #\t)
;(2 . #\e)
;(3 . #\s)
;(4 . #\t)
loopマクロでforが複数ある場合は最短のforが優先されるのでkが終了したら止まります
ハマりポイント
listは要素のアクセスにO(n)かかる
listはconsセルをたどる実装なのでO(n)かかります。後ろからアクセスしたい場合はreverseしたものを置いておくかarrayを使いましょう。またmapcarやreduce関数はcdrで遷移するためO(n^2)ではなくO(n)で済みます。
tに値は束縛できない
tはcommonlispではtrueを示すためシンボルとして用いることはできません。
他の言語では一時変数として使われることも多いためそのまま書くとたまにハマります。
loopマクロのacross節、map関数はvectorをとる
vectorは1次元のarrayです。つまり2次元以上のarrayをそのまま処理することはできません。
findは探査する要素を返す
find関数は探査する要素を返すのでnilを探査するとnilを返します。このためcount=0かpositionで探しましょう。