AtCoder Beginners Selectionで
学ぶ、Common Lisp 入門

Common Lispの基本的な使い方を、競技プログラミングの基本的な問題 AtCoder Beginners Selection(ABS)の問題を解きながら、 ほぼ 0 から付け足していきます。
REPLだけでなく、標準入出力の使い方にも慣れます。

「実用的」なプログラムは、生成AIが書いてくれるようになったので、人間はLispで「プログラム遊び」を楽しむのがよいかな、と思います。

関連記事

1. 問題 0 — PracticeA: Welcome to AtCoder

1.1. 問題

整数 a が1行目に、整数 b と c がスペース区切りで2行目に、文字列 s が3行目に与えられます。
a + b + c の値と s をスペース区切りで1行に出力してください。

入力例

1
2 3
test

出力例

6 test

解答コードは、AtCoderに登録して提出すると、自動採点の結果を確認できます。

1.2. Common Lisp の基本構文

まずは、問題の前半部分を分解して考えます。
3つの数字を足す Common Lisp プログラムは、

(+ 1 2 3)Code language: Lisp (lisp)
1.2. Common Lisp の基本構文

Lisp のコードはすべて (関数名 引数 ...) という形のリストです。
数式 1 + 2(+ 1 2) と書き、(+ 1 2 3) のように引数を増やすこともできます1

1.3. 基本的な出力(format)

AtCoder の Common Lisp 処理系は SBCL 2.5.8 です2

対話モード(REPL)なら、式を入力するだけで評価された結果が表示されますが、単体のプログラムとして実行するなら標準出力に表示する処理が必要です。

; 1+2+3 を計算して、結果を表示する
(format t "~a~%" (+ 1 2 3))Code language: Lisp (lisp)

標準出力に値を出力するには、formatを使います。
~a は値を文字列化して埋め込むプレースホルダで、~% は改行を意味しています3
これを実行すると、6という結果が表示されることになります。

1.4. 使う機能(let*, read, read-line

let は変数を束縛します。
let* は上から順に評価されるので、前の変数を後で参照できます4

(let* ((a 1)
       (b (+ a 1)))  ; a を使える
  (format t "~a~%" b))  ; => 2Code language: Lisp (lisp)

入力を受け取るには、readread-lineがあります。

(read)                 ; 標準入力からトークン(数値・シンボル)を1つ読む
(read-line)            ; 標準入力から1行を文字列として読むCode language: Lisp (lisp)

read は、標準入力からトークンを読み込みます。
数値などもそのまま評価されます。
スペースと改行を区切り文字として扱うため、同一行の数値も複数回の read で読めます。

read-lineは、標準入力から1行を文字列として読みます。
数字の文字列でも、そのままでは数値演算はできません。
トークンとして読み直すには、read-from-string関数を使います。

read はトークンを終端させた空白文字(スペース・改行を含む)も一緒に消費します5
そのため数値の後に文字列が続く場合、read-line を1回呼ぶだけで次の行を読めます。

1.5. 解答例

(let* ((a (read)) (b (read)) (c (read))
       (s (read-line)))
 (format t "~a ~a~%" (+ a b c) s))Code language: Lisp (lisp)

変数 a, b, c に標準入力をトークンとして受け取り 、s には一行分の文字列を入れています。
それを計算し、formatで空白を間に入れて出力しています。

ちなみに、変数で束縛せず、返り値をそのまま使って書くこともできます。
Lispは、カッコが多い点でとっつきにくい印象もありますが、意外と「3つ読み込んで合計し、一行読み込んで表示する」というのが直感的に書けることがわかりますね。

(format t "~a ~a"
   (+ (read) (read) (read))
   (read-line))Code language: Lisp (lisp)

2. 問題 1 — ABC086A: Product

2.1. 問題

正整数 a と b が1行にスペース区切りで与えられます。
積 a×b が偶数なら Even、奇数なら Odd を出力してください。

入力例

3 4

出力例

Even

2.2. 使う機能(if, evenp)

if(if 条件 真の式 偽の式) の形で書きます。
式なので値を返すことができ、format の引数にそのまま渡せます。

(format t "~a~%" 
   (if (> 3 2) "yes" "no"))  ; => yesCode language: Lisp (lisp)

evenp は偶数なら T を返す述語です。
Lisp の述語名は慣習的に末尾が p になります6

2.3. 解答例

(let* ((a (read)) (b (read)))
  (format t "~a~%" (if (evenp (* a b)) "Even" "Odd")))Code language: Lisp (lisp)

if が文字列を返し、format~a がそれを出力します。
format の呼び出しが1回にまとまります。

私は、evenp が思いつかなかったので、2の剰余で素直に判定しました。

  (print (if (= (mod (* (read) (read)) 2) 0)
	     "Even"
	     "Odd")) Code language: Lisp (lisp)

3. 問題 2 — ABC081A: Placing Marbles

3.1. 問題

01 からなる3文字の文字列 s が与えられます。
1 の個数を出力してください。

入力例

101

出力例

2

3.2. 使う機能(count)

count は、文字列やリストなどのシーケンスの中で特定の要素が何個あるかを数えます7
文字リテラルは #\文字 と書きます8

(count #\1 "101")     ; => 2
(count #\a "banana")  ; => 3Code language: Lisp (lisp)

3.3. 解答例

(format t "~a~%" (count #\1 (read-line)))Code language: Lisp (lisp)

変数に束縛せず、read-line の戻り値を count に直接渡すだけで解けます。

私の解答は、数値として評価して 9 で割った余りを求めることで、1の数を数えました。3が9未満だったから可能な解法ですね。

(format t "~a" (mod (read) 9))Code language: JavaScript (javascript)

4. 問題 3 — ABC081B: Shift Only

4.1. 問題

N 個の正整数 A が与えられます。
全要素が偶数のとき全要素を2で割る操作を繰り返し、何回できるか答えてください。

入力例

3
8 12 40

出力例

2

4.2. 使う機能(loop, collect, every, mapcar)

loop は汎用ループ構文です。
collect と組み合わせると要素をリストに集めます9

(loop repeat 3 collect (read))  ; 3つ読んでリストにする => (a b c)Code language: Lisp (lisp)

loopcount キーワードは条件式が真になった回数を数え、ループの戻り値として返します。

(loop repeat 5 count t)                        ; => 5
(loop for x in '(1 2 3 4) count (evenp x))    ; => 2Code language: Lisp (lisp)

every はリスト全要素が条件を満たすとき T を返します10

(every #'evenp '(2 4 6))  ; => T
(every #'evenp '(2 3 6))  ; => NILCode language: Lisp (lisp)

mapcar はリストの各要素に関数を適用して新しいリストを作ります11

lambda は無名関数で、(lambda (x) (* x x)) と書くと「x を受け取り x² を返す関数」になります12
#' は関数を値として渡すときの略記で、function の短縮形です13

4.3. 解答例

(let* ((n (read))
       (a (loop repeat n collect (read))))
  (format t "~a~%"
          (loop while (every #'evenp a)
                count t
                do (setf a (mapcar (lambda (x) (ash x -1)) a)))))Code language: Lisp (lisp)

count t は毎反復で t(常に真)をカウントするので、ループの実行回数を返します。
別途カウンタ変数を用意する必要がありません。
(ash x -1) は算術右シフトで、非負整数に対して (floor x 2) と同じ結果です14setf は変数への代入です15

私の解答は、2で何回割れるかをリストに対して計算して、合成しました。

(defun div2times (a)
	   (if (= (mod a 2) 0)
	       (+ 1 (div2times (/ a 2)))
	       0))
(defun read-list-line ()
	   (read-from-string
	    (concatenate 'string "(" (read-line) ")")))

(format t "~a "
	  (if (= (read) 0)
	      0
	      (apply #'min
		     (mapcar #'div2times (read-list-line)))))Code language: Lisp (lisp)

5. 問題 4 — ABC087B: Coins

5.1. 問題

500円玉 A 枚、100円玉 B 枚、50円玉 C 枚から、合計がちょうど X 円になる選び方は何通りか答えてください。

入力例

2
2
2
100

出力例

2

5.2. 使う機能

loop for i to n は 0 から n まで両端を含んで繰り返します。
dotimes1+ が必要だった上限の扱いがすっきりします16

loopsum は各反復の値を足し合わせて返します。
count は条件が真の回数を返します。
ネストした loop はそれ自体が値を返す式なので、外側の sum に渡せます。

5.3. 解答例

(let* ((a (read)) (b (read)) (c (read)) (x (read)))
  (format t "~a~%"
          (loop for i to a sum
            (loop for j to b sum
              (loop for k to c count
                (= x (+ (* 500 i) (* 100 j) (* 50 k))))))))Code language: Lisp (lisp)

ループ変数 i, j, k がそのまま枚数を表します。
最内側の count は条件が成立した回数(0 か 1)を返し、中間の sum がそれを積み上げ、外側の sum がさらに集計します。
カウンタ変数も incf も不要です。

私の解答は、以下のように三つ組のリストの要素をおのおの判定して、真の数を数えました。

(defun totalp (a b c x)
  (= (+ (* 500 a) (* 100 b) (* 50 c)) x))

(defun triples (a b c)
  (loop for i from 0 to a
	append (loop for j from 0 to b
		     append (loop for k from 0 to c
				  collect (list i j k)))))

(defun main (a b c x)
    (print (count t (mapcar
	   (lambda (s) (apply #'totalp (append s (list x))))
	   (triples a b c)))))


(main (read) (read) (read) (read))
Code language: Lisp (lisp)

6. 問題 5 — ABC083B: Some Sums

6.1. 問題

1 以上 N 以下の整数のうち、10進各桁の和が A 以上 B 以下であるものの総和を出力してください。

入力例

20 2 5

出力例

84

6.2. 使う機能

defun で名前付き関数を定義します17

(defun 関数名 (引数...)
  本体)Code language: Lisp (lisp)

mod は剰余を、floor は切り捨て除算の結果を返します18
plusp(> n 0) の慣用表現です。

6.3. 解答例

(defun digit-sum (n)
  (loop while (plusp n)
        sum (mod n 10)
        do (setf n (floor n 10))))

(let* ((n (read)) (lo (read)) (hi (read)))
  (format t "~a~%"
          (loop for i from 1 to n
                when (<= lo (digit-sum i) hi)
                sum i)))Code language: Lisp (lisp)

digit-sum 内の loopsum キーワードで桁の和を直接返すので、初期化変数 s が不要になります。
メイン部分の when … sum は、条件を満たす i だけを足し合わせます19
入力変数を lo/hi にすることで問題中の A/B と区別しています。

私の解答は、数列から条件を満たす数だけを集めて、総和を求めました。

(defun sum-digits (n)
  (if (> n 0)
      (+ (mod n 10) (sum-digits (floor n 10)))
      0))

(defun digit-sum-filter (n a b)
  (if (and (>= (sum-digits n) a) (<= (sum-digits n) b))
      n
      0))
        
(defun integers-up-to(n)
  (loop for i from 1 to n collect i))


(defun abc083b (n a b)
  (print (apply #'+ (mapcar (lambda (x)
	    (digit-sum-filter x a b)) (integers-up-to n)))))

(abc083b (read) (read) (read))Code language: Lisp (lisp)

7. 問題 6 — ABC088B: Card Game for Two

7.1. 問題

N 枚のカードに数が書かれています。
アリスとボブが交互に最大値のカードを取り、アリスが先手です。
最終的な「アリスの合計 − ボブの合計」を出力してください。

入力例

3
2 7 4

出力例

5

7.2. 使う機能

sort はリストをソートします。
第2引数に比較関数を渡します。
sort は元のリストを破壊的に変更する点に注意が必要です20

(sort '(3 1 4 1 5) #'<)  ; => (1 1 3 4 5)
(sort '(3 1 4 1 5) #'>)  ; => (5 4 3 1 1)Code language: Lisp (lisp)

loop for x in リスト はリストを先頭から順に取り出します。複数の for 節を並べると変数が同時に進みます21

7.3. 解答例

(let* ((n (read))
       (cards (sort (loop repeat n collect (read)) #'>)))
  (format t "~a~%"
          (loop for x in cards
                for i from 0
                sum (if (evenp i) x (- x)))))Code language: Lisp (lisp)

i が偶数(アリスの番)なら x を加算し、奇数(ボブの番)なら -x を加算します。
sum がその合計を返すため、アリスの合計とボブの合計を別々に管理せず、差を1回の loop で直接計算できます。

私の回答では、読み込みと変換とリストを定義してから、計算しました。

(defun flip-by-two (cards)
  (loop for x in (sort (copy-seq cards) #'>)
      for i from 0
	collect (if (oddp i) (- x) x)))

(defun read-times(n)
  (loop for i from 1 to n collect 
			  (read)))

(defvar cards (read-times (read)))

(print (apply #'+ (flip-by-two cards)))
Code language: Lisp (lisp)

8. 問題 7 — ABC085B: Kagami Mochi

8.1. 問題

N 個の餅の直径が与えられます。
異なる直径の種類数を出力してください。

入力例

4
10
8
8
6

出力例

3

8.2. 使う機能

remove-duplicates はリストの重複要素を除いた新しいリストを返します22

(remove-duplicates '(1 2 2 3 3 3))  ; => (1 2 3)Code language: Lisp (lisp)

length はリストや文字列の要素数を返します23

8.3. 解答例

(format t "~a~%"
        (length (remove-duplicates
                 (loop repeat (read) collect (read)))))Code language: Lisp (lisp)

N を変数に束縛せず (read)repeat に直接渡します。
3つの式をネストするだけで解が完結します。

私は、大きい順に並べ替えてからカウントしていましたが、よく考えたら順序は関係ないですね💦

(defun read-times (n)
  (loop for i from 1 to n collect 
			  (read)))
(defun mochi-sort (s)
  (remove-duplicates (sort (copy-seq s) #'>)))

(let*
    ((n (read))
     (mochi-list (read-times n)))
  (print (length (mochi-sort mochi-list))))
Code language: Lisp (lisp)

9. 問題 8 — ABC085C: Otoshidama

9.1. 問題

N 枚の封筒に合計 Y 円が入っています。
各封筒には 1000円、5000円、10000円のどれかが入っています。
1000円が a 枚、5000円が b 枚、10000円が c 枚となる組み合わせを1つ出力してください。
なければ -1 -1 -1 を出力します。

入力例

9 45000

出力例

0 9 0

9.2. 使う機能

loopthereis キーワードは、各反復の式が最初に真になった時点でその値を返してループを終了します。
全反復で偽なら NIL を返します。

(loop for x in '(1 3 4 7) thereis (evenp x))  ; => T(4 で終了)
(loop for x in '(1 3 5 7) thereis (evenp x))  ; => NILCode language: Lisp (lisp)

or は最初の非 NIL 値を返します。
(or nil 式) では が評価されるので、副作用(出力)を持つフォールバック処理として使えます。

9.3. 解答例

(let* ((n (read)) (y (read)))
  (or (loop for c to n
            thereis (loop for b to (- n c)
                          for a = (- n b c)
                          when (= y (+ (* 10000 c) (* 5000 b) (* 1000 a)))
                          do (format t "~a ~a ~a~%" a b c)
                          and return t))
      (format t "-1 -1 -1~%")))Code language: Lisp (lisp)

内側の loopwhen 条件成立時に出力して t を返します。
thereis がその t を受け取り外側のループも即終了します。
見つからなければ thereisNIL を返し、or がフォールバックの出力を実行します。block/found フラグが不要になります24

私の解法は、3つ組をすべてチェックして、うまくいくケースを集めて来る方法にしました。

(defun total-money (x y z)
  (+ (* 10000 x) (* 5000 y) (* 1000 z)))

(defun possible-triplets (n)
  (loop for i from 0 to n
	append  (loop for j from 0 to (- n i)
		      collect (list i j (- n i j)))))

(defun exactp (y s)
  (= y (apply #'total-money s) ))

(defun exact-triplets (n y)
  (loop for s in (possible-triplets n) 
	when (exactp y s) collect s))

(defun print-triplet (s)
  (if s
      (format t "~a ~a ~a" (car s) (cadr s) (caddr s))
      (format t "-1 -1 -1"))) 

(let*
    ( (n (read))
      (y (read)) )
  (print-triplet (car (exact-triplets n y))))Code language: Lisp (lisp)

10. 問題 9 — ABC049C: Daydream

10.1. 問題

文字列 S が "dream""dreamer""erase""eraser" だけを並べて作れるか判定し、YESNO を出力してください。

入力例

erasedream

出力例

YES

10.2. 使う機能

string= は文字列の同一性を比較します。:start1:end1 で部分文字列を指定できます25

(string= "abcde" "cd" :start1 2 :end1 4)  ; => TCode language: Lisp (lisp)

loopalways キーワードは全反復で式が真なら T を返し、最初に偽になった時点で即 NIL を返してループを終了します。

(loop for x in '(2 4 6) always (evenp x))  ; => T
(loop for x in '(2 3 6) always (evenp x))  ; => NILCode language: Lisp (lisp)

10.3. 解答例

文字列を末尾から照合します。
S を反転し、反転した単語リストと先頭から照合すると実装が簡単です。

(let* ((rev   (reverse (read-line)))
       (words '("maerd" "remaerd" "esare" "resare"))
       (len   (length rev)))
  (format t "~a~%"
          (if (loop with pos = 0
                    while (< pos len)
                    always (loop for w in words
                                 for wlen = (length w)
                                 when (and (<= (+ pos wlen) len)
                                           (string= rev w :start1 pos
                                                          :end1 (+ pos wlen)))
                                 do (incf pos wlen)
                                 and return t))
              "YES" "NO")))Code language: Lisp (lisp)

外側の loopalways を使い、内側で一致する単語が見つからなかった(NIL が返った)時点で即 NIL を返します。
matched フラグ変数が不要になります。with pos = 0loop 専用のローカル変数です26
"dreamer" の反転 "remaerd""maerd" より先にリストに置くことで、dreamer の誤認識を防いでいます27

この問題は、メモリ制限超過と実行時間超過に悩まされました。
最終的に完成した答えでは、真面目に先頭から一致点の候補を拾いながらチェックを進めています。

(defvar tests '("dream" "dreamer" "erase" "eraser"))

(defun next-match (test str n)
  (cond
    ((> (+ (length test) n) (length str)) nil)
    ((string= test str
	      :start2 n
	      :end2 (+ n (length test)))
     (+ n (length test)))
    (t nil) ))

(defun next-match-list (tests str n)
  (loop for test in tests
	for pos = (next-match test str n)
	when pos
	collect pos))

(defun valid-p (tests s)
  (labels ((check-step (tests str pendings)
	     (cond
	       ((null pendings) nil)
	       ((= (length str) (car pendings)) t)
	       (t (check-step tests
			str
			(append
			 (next-match-list tests str (car pendings))
			 (cdr pendings)))))))
    (check-step tests s (list 0))))
  
(format t (if (valid-p tests (read-line))
	      "YES"
	      "NO"))
Code language: Lisp (lisp)

11. 問題 10 — ABC086C: Traveling

11.1. 問題

時刻 0 に座標 (0, 0) を出発します。
クエリが N 個あり、時刻 t_i に (x_i, y_i) にいなければなりません。
すべてのクエリを満たせるなら Yes、無理なら No を出力してください。
移動は各時刻に上下左右1マスずつで、同じ場所に留まることもできます。

入力例

2
3 1 2
6 1 1

出力例

Yes

11.2. 使う機能

判定に必要な条件は2つです。前の地点からのマンハッタン距離が経過時間以内であること28、そして経過時間とマンハッタン距離の差が偶数であることです。
余った時間は往復で消費できるため、差が奇数のときは到達できません。

abs は絶対値を返します29
loopwith 変数 = 初期値 and 変数 = 初期値 で複数のループ変数をまとめて宣言できます。

11.3. 解答例

(format t "~a~%"
        (if (loop repeat (read)
                  with prev-t = 0 and prev-x = 0 and prev-y = 0
                  for ti = (read) for xi = (read) for yi = (read)
                  for dt   = (- ti prev-t)
                  for dist = (+ (abs (- xi prev-x)) (abs (- yi prev-y)))
                  always (and (>= dt dist) (evenp (- dt dist)))
                  do (setf prev-t ti prev-x xi prev-y yi))
            "Yes" "No"))Code language: Lisp (lisp)

with … and … で前回座標を loop 変数として管理し、for ti = (read) で毎反復読み取ります。
always が全クエリの条件を一括検査し、1つでも失敗すれば即 NIL を返します。ok フラグ変数が不要になり、すべてが1つの loop 式に収まります30

私の解答では、いったんクエリをリストに読み込んで、loop alwaysで確認しました。
リストのアクセスに時間がかかったので、for – on で進んで、前後の点を car と cadr で取得するようにしました。

(defun distance (pos1 pos2)
  (let ((x1 (first pos1))
	(y1 (second pos1))
	(x2 (first pos2))
	(y2 (second pos2)))
    (+ (abs (- x1 x2)) (abs (- y1 y2))) ))

(defun movable-p (pos1 pos2 dt)
  (let ((dist (distance pos1 pos2)))
    (and (>= dt dist)
	 (zerop (mod (- dt dist) 2)))))

(let* ((n (read))
       (time-points (append
		     '((0 0 0))
		     (loop for i from 1 to n
			   collect (list (read) (read) (read))))))
  (format t (if
	     (loop for rest on time-points
		   while (cdr rest)
		   always (let ((prev (car rest))
				(now (cadr rest)))
			    (movable-p
			     (cdr prev)
			     (cdr now)
			     (- (car now) (car prev)))))
	     "Yes"
	     "No")))
Code language: Lisp (lisp)

12. 登場した機能一覧

機能説明
read標準入力からトークンを読む
read-line標準入力から1行を文字列で読む
format t "~a~%" 値標準出力に値を出力して改行
let*順番に変数を束縛する
ifwhenunless条件分岐
loop汎用ループ
loop … count条件が真の回数を返す
loop … sum各反復の値を合計して返す
loop … thereis最初の真値を返す(短絡)
loop … always全反復が真なら T を返す(短絡)
loop with … andループ専用ローカル変数の宣言
loop for i to n0 から n まで両端含むループ
dolistリスト要素のループ
defun関数定義
setfincf変数への代入と加算
lambda無名関数
mapcarリスト全要素に関数を適用
every全要素が条件を満たすか確認
sortリストをソート
count要素の個数を数える
remove-duplicates重複を除く
length要素数を返す
evenpoddpplusp偶数・奇数・正数の判定
modfloor剰余と切り捨て除算
ashビットシフト
string=文字列比較(部分一致対応)
abs絶対値
  1. Lisp のコードはすべて S 式(Symbolic Expression)として表現されます。演算子と引数をリストとして書く前置記法が特徴で、(+ 1 2) は「+ という関数に 12 を渡す」という意味です。 – とほほのLISP入門 — S式
  2. Steel Bank Common Lisp の略。Carnegie Mellon University Common Lisp から派生したフリーの処理系で、ANSI Common Lisp 規格に準拠しネイティブコードにコンパイルします。2024 年の Common Lisp Community Survey では回答者の約 88% が使用していると報告されています。 – Steel Bank Common Lisp — Wikipedia
  3. format の第 1 引数 t は標準出力ストリームを指します。~aprinc 相当の人間向け出力、~sprin1 相当の Lisp 読み取り可能な形式での出力です。整数を 10 進数で出力する ~d も競技プログラミングでよく使います。 – とほほのLISP入門 — format
  4. let は複数の変数を並列に束縛するため、同じ let 内で別の変数を参照できません。let* は上から順番に束縛するので前の変数を次の初期値に使えます。 – お気楽 Common Lisp プログラミング入門 — 関数定義
  5. SBCL でのテストで確認できます。printf '1\n2 3\ntest\n' | sbcl --script(read)(read)(read) の後に peek-char すると、次の文字は \n ではなく ttest の先頭)です。read3 を読んだ時点で改行も消費されています。 – 競技プログラミングでCommon Lispを使っている人とこれから使うかもしれない人のために
  6. 述語(predicate)は真偽値を返す関数の総称です。Common Lisp では nulllistpnumberpstringp など末尾が p の関数名が述語を示します。ただし =< など比較演算子はこの慣習に従っていません。 – お気楽 Common Lisp プログラミング入門 — 述語
  7. Common Lisp では文字列・リスト・ベクトルなどをまとめてシーケンスと呼びます。count のほか findpositionremove なども同じシーケンス型に対して使えます。 – とほほのLISP入門
  8. #\ は読み取りマクロの一種で 1 文字を表すリテラルです。#\Space(スペース)、#\Newline(改行)、#\Tab(タブ)のように特殊文字も表現できます。 – とほほのLISP入門 — 文字
  9. loop マクロは ANSI Common Lisp で標準化された強力なイテレーション構文です。collectsumcountmaximize など集積のためのキーワードや、whileuntilfor などの制御キーワードを組み合わせられます。 – とほほのLISP入門 — loop
  10. 類似の関数として some(いずれかが条件を満たせば T)、notevery(全要素が満たすわけではないとき T)、notany(どれも満たさないとき T)もあります。 – お気楽 Common Lisp プログラミング入門 — 高階関数とラムダ式
  11. mapcar には複数のリストを渡すこともできます。(mapcar #'+ '(1 2 3) '(10 20 30)) は各位置の要素を足し合わせて (11 22 33) を返します。 – お気楽 Common Lisp プログラミング入門 — 高階関数とラムダ式
  12. lambda はアロンゾ・チャーチのラムダ計算に由来する表記です。クロージャを形成するため、外側のスコープの変数を捕捉できます。 – お気楽 Common Lisp プログラミング入門 — 高階関数とラムダ式
  13. #' は特殊オペレータ function の略記で、#'evenp(function evenp) と同じです。Common Lisp では変数と関数が別の名前空間にある(Lisp-2)ため、関数を値として渡すには #'function が必要です。 – とほほのLISP入門 — 関数参照
  14. ash(Arithmetic Shift)は整数のビットシフト演算です。(ash x n) は n が正なら左シフト(×2^n)、負なら右シフト(÷2^|n|)です。整数除算の慣用表現としてよく使われます。 – とほほのLISP入門 — 算術演算子
  15. setf はゼネラライズドリファレンス(generalized reference)に対応しており、変数だけでなくリストの要素((setf (car list) val))や配列要素なども書き換えられます。 – とほほのLISP入門 — 代入
  16. to は両端を含む範囲で、(loop for i to 3 collect i)(0 1 2 3) を返します。上限を含まない場合は below を使います。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  17. defun はトップレベルで評価される特殊フォームで、関数名をグローバルな関数名前空間に登録します。定義した関数はファイル内のどこからでも呼び出せます。 – お気楽 Common Lisp プログラミング入門 — 関数定義
  18. floor は商と余りを多値(multiple values)として返します。(floor 17 5)32 を返します。1 値だけ使いたい場合は (floor n 10) と書けば最初の値(商)だけ取り出せます。切り上げは ceiling、ゼロ方向への切り捨ては truncate です。 – とほほのLISP入門 — 算術演算子
  19. loopwhen 節は条件付きで後続のキーワード(sumcollect など)を実行します。unless 節も同様に使えます。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  20. 安全に使うには (sort (copy-list cards) #'>) のようにコピーしてからソートする方法もあります。順序が同じ要素の並びを保証したい場合は stable-sort を使います。 – とほほのLISP入門
  21. loop では複数の for 節を並べると変数が同時に進みます。一方、and で繋ぐと独立した繰り返しになります。同時進行か独立かで挙動が変わるため、複数 for を書く際は注意が必要です。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  22. デフォルトの比較関数は eql です。文字列の重複を除くには :test #'string= を明示します。(remove-duplicates list :test #'string=) のように書きます。 – とほほのLISP入門
  23. 文字列に対して使った場合、length はバイト数ではなく文字数を返します。そのため日本語など 1 文字が複数バイトになる場合でも正しく文字数を数えられます。 – とほほのLISP入門 — length
  24. loopwhen … do … and return 値 という組み合わせは、条件成立時に副作用を実行してから指定した値でループを抜けます。return のみでは NIL を返します。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  25. string= には :start1:end1:start2:end2 の 4 つのキーワード引数があり、両文字列の任意の部分同士を比較できます。省略した場合は文字列全体が対象です。 – とほほのLISP入門
  26. loop with 変数 = 初期値loop 内でのみ有効なローカル変数を宣言します。with a = 0 and b = 0 のように and で複数まとめて書けます。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  27. return(return-from nil 値) の省略形です。loop マクロは暗黙的に nil という名前のブロックを形成しているため、return で直近の loop から脱出できます。 – お気楽 Common Lisp プログラミング入門 — 繰り返し
  28. マンハッタン距離は格子状の移動で使われる距離の概念で、2点 (x1, y1) と (x2, y2) の間では |x1-x2| + |y1-y2| で計算します。チェスのルークが移動できる最小手数に相当します。 – 競技プログラミングでCommon Lispを使っている人とこれから使うかもしれない人のために
  29. abs は整数・浮動小数点数・複素数のいずれにも対応します。複素数に対して使うと複素絶対値(modulus)を返します。 – とほほのLISP入門
  30. for 変数 = 式 は毎反復ごとに式を再評価します。for a = (read) と書けば反復のたびに標準入力から値を読み取れます。 – お気楽 Common Lisp プログラミング入門 — 繰り返し