1. はじめる前に
コードを書いて、動かして、書き換える。
それだけで、だんだんわかってきます。
VS Code と SBCL と Alive 拡張を使って、まずは動かしてみましょう。
main.lisp というファイルを開いて、Alive の REPL を起動しておきましょう。
2. 計算のやり方を決める(defun)
2.1. やってみよう
main.lisp にこのコードを書いてください。
(defun sum3 (a b c)
(+ a b c))Code language: Lisp (lisp)
sum(サム):合計
書いたら Alt + Shift + L を押します。
これで REPL にコードが読み込まれます。
次に REPL にこれを打ち込んでみましょう。
(sum3 1 2 3)Code language: Lisp (lisp)
6 が返ってきましたか?
続けて試してみましょう。
(sum3 10 20 30)
(sum3 100 200 300)Code language: Lisp (lisp)
2.2. 書き換えてみよう
次は、+ を * に変えて、名前もmultiply3に変えてみます。
(defun multiply3 (a b c)
(* a b c))Code language: Lisp (lisp)
multiply(マルチプライ):掛ける、掛け算する
書き換えたら Alt + Shift + L でもう一度読み込んで、REPL で試します。
(multiply3 2 3 4)
(multiply3 5 5 5)Code language: Lisp (lisp)
受け取る数をもっと増やしてみましょう。
(defun multiply9 (a b c d e f g h i)
(* a b c d e f g h i))Code language: Lisp (lisp)
(multiply9 1 2 3 4 5 6 7 8 9)Code language: Lisp (lisp)
何が返ってきましたか?
2.3. ここで覚えたこと
defun(デファン)は、「計算のやり方を決める」命令です。
define function(ディファイン・ファンクション)の略です。
- define(ディファイン):決める
- function(ファンクション):数値や文字を受け取って、結果を返す仕組み
(a b c) の部分は受け取る値の名前で、引数(ひきすう、パラメータ)といいます。
受け取った値を使って、(+ a b c) のように、計算します。
関数の名前は自由につけていい
キーワードである defun と違って、sum3 や multiply3 の部分、あるいは a b c などの名前は自分で決められます。
たとえば、mittsu-tasu でも keisan でも動きます。
ただ、短い英単語を使うとかっこよく見えます。
辞書で「合計」を調べると sum(サム)、「面積」なら area(エリア)など、気に入った名前を見つけて使ってみましょう。
この章で出てきた英単語
| コード | 読み方 | 意味 |
|---|---|---|
defun | デファン | 計算のやり方を決める |
+ | プラス | 足し算(plus) |
* | アスタリスク | 掛け算(multiply) |
3. 引き算と割り算
3.1. やってみよう
(defun diff (a b)
(- a b))Code language: Lisp (lisp)
diff(ディフ):差(difference)の略
(diff 10 3)
(diff 100 45)Code language: Lisp (lisp)
3.2. 書き換えてみよう——切り捨て割り算
(defun half (n)
(floor n 2))Code language: Lisp (lisp)
half(ハーフ):半分
(half 7)
(half 10)
(half 99)Code language: Lisp (lisp)
2 の部分を 3 や 4 に変えるとどうなりますか?
3.3. 大きい方・小さい方
(defun biggest (a b c)
(max a b c))Code language: Lisp (lisp)
biggest(ビゲスト):いちばん大きい
(biggest 5 12 8)
(biggest 100 3 77)Code language: Lisp (lisp)
max を min に変えてみましょう。
(defun smallest (a b c)
(min a b c))Code language: Lisp (lisp)
smallest(スモーレスト):いちばん小さい
(smallest 5 12 8)Code language: Lisp (lisp)
3.4. ここで覚えたこと
floor は「床(ゆか)」という意味で、小数点以下を切り捨てます。/ を使うと分数が返ることがあるので、整数のまま割るときは floor を使います。
コード中の ; より後ろはコメントです。
実行されないので、メモを書く場所として使えます。
| コード | 読み方 | 意味 |
|---|---|---|
- | マイナス | 引き算(minus) |
floor a b | フロア | a を b で割って小数点以下を切り捨て |
max | マックス | 最大値(maximum) |
min | ミン | 最小値(minimum) |
; | セミコロン | コメント(メモ) |
4. 途中の結果に名前をつける(let*)
4.1. やってみよう
(defun two-step (n)
(let* ((step1 (* n 2))
(step2 (+ step1 10)))
step2))Code language: Lisp (lisp)
step(ステップ):段階、手順
(two-step 5)
(two-step 100)Code language: Lisp (lisp)
let は、「縦かける横を『答え(kotae)』とする」という意味です。step1 に n * 2 の結果を入れて、次の行の step2 でそれを使っています。step2 の計算式を変えてみましょう。
4.2. ここで覚えたこと
let*(レット・スター)は途中の計算結果に名前をつける書き方です。
上から順番に計算され、前の名前を次の行で使えます。
(let* ((名前1 式1)
(名前2 式2))
結果として返す式)Code language: Lisp (lisp)
| コード | 読み方 | 意味 |
|---|---|---|
let* | レット・スター | 途中の結果に名前をつける |
5. 入力と出力(read princ)
5.1. やってみよう
main.lisp にこのまま書いてください。
(defun sum3 (a b c)
(+ a b c))
(defun main ()
(let* ((a (read))
(b (read))
(c (read)))
(princ (sum3 a b c))
(terpri)))Code language: Lisp (lisp)
これをREPLで実行します。
(main)
入力待ちになるので、3 4 5 と入力して Enter を押すと 12 が返ってきます。
5.2. 文字列を読む
(defun echo ()
(let* ((s (read-line)))
(princ s)
(terpri)))
(echo)Code language: Lisp (lisp)
echo(エコー):やまびこ。入力をそのまま返すことを「エコーする」という
何か文字を入力してみましょう。
そのまま返ってきましたか?
5.3. ここで覚えたこと
read(リード)はキーボードから数値を1つ受け取ります。
スペースで区切れば複数回呼んで順番に読めます。read-line(リード・ライン)は、1行まるごと文字列として受け取ります。princ(プリンク)は値を画面に表示します。terpri(タープリ)は改行します。
| コード | 読み方 | 意味 |
|---|---|---|
read | リード | 数値を1つ読み込む(read) |
read-line | リード・ライン | 1行を文字列として読む |
princ | プリンク | 値を出力する(print) |
terpri | タープリ | 改行する |
プログラムの問題(AtCoderなど)では、コードはこの形で書いて提出することが多いです。
(defun solve (...) ; 計算のやり方
...)
(defun main () ; 読み込みと出力
(let* (...)
(princ (solve ...))
(terpri)))
(main)Code language: Lisp (lisp)
main(メイン):主な、中心の。プログラムの入口として使う名前solve(ソルブ):解く、解決する
6. 条件で分ける(if)
6.1. やってみよう
(defun pass-or-fail (score)
(if (>= score 60)
"合格"
"不合格"))Code language: Lisp (lisp)
pass(パス):合格する、通過するfail(フェイル):不合格、失敗するscore(スコア):点数
(pass-or-fail 80)
(pass-or-fail 45)
(pass-or-fail 60)Code language: Lisp (lisp)
6.2. 書き換えてみよう
合格ラインを 60 から 80 に変えてみましょう。
文字も変えてみましょう。
6.3. 余りで分ける
(defun odd-or-even (n)
(if (evenp n)
"偶数"
"奇数"))Code language: Lisp (lisp)
odd(オッド):奇数の、変なeven(イーブン):偶数の、平らな
(odd-or-even 4)
(odd-or-even 7)
(odd-or-even 100)Code language: Lisp (lisp)
6.4. 2つの条件を組み合わせる
(defun both-pass (a b)
(if (and (>= a 60) (>= b 60))
"両方合格"
"どちらかが不合格"))Code language: Lisp (lisp)
both(ボース):両方
(both-pass 80 70)
(both-pass 80 40)Code language: Lisp (lisp)
and を or に変えるとどうなりますか?
6.5. ここで覚えたこと
if(イフ)は「もし〜なら」という分岐です。
条件が正しいとき(真)は1つ目の式が、まちがいのとき(偽)は2つ目の式が返ります。
(if 条件
正しいとき
まちがいのとき)Code language: Lisp (lisp)
| コード | 読み方 | 意味 |
|---|---|---|
if | イフ | もし〜なら(if) |
= < <= > >= | イコール、より小さい、以下、より大きい、以上 | 数値を比べる |
and | アンド | 両方が正しい(and) |
or | オア | どちらかが正しい(or) |
evenp | イーブン・ピー | 偶数かどうか(even) |
oddp | オッド・ピー | 奇数かどうか(odd) |
zerop | ゼロ・ピー | 0かどうか(zero) |
mod a b | モッド | a を b で割った余り(modulo) |
7. 3つ以上に分ける(cond)
7.1. やってみよう
(defun grade (score)
(cond ((>= score 90) "A")
((>= score 70) "B")
((>= score 50) "C")
(t "D")))Code language: Lisp (lisp)
grade(グレード):等級、成績score(スコア):点数
(grade 95)
(grade 75)
(grade 55)
(grade 30)Code language: Lisp (lisp)
7.2. 書き換えてみよう
境界の数値や返す文字を変えてみましょう。
条件をもう1つ増やしてみましょう。
7.3. 文字列で分ける
(defun next-weather (s)
(cond ((string= s "はれ") "くもり")
((string= s "くもり") "あめ")
(t "はれ")))Code language: Lisp (lisp)
next(ネクスト):次のweather(ウェザー):天気
(next-weather "はれ")
(next-weather "くもり")
(next-weather "あめ")Code language: Lisp (lisp)
7.4. ここで覚えたこと
cond(コンド)は3つ以上に分けるときに使います。
condition(コンディション、条件)の略です。
上から順番に確認して、最初に正しかった条件の値が返ります。t は、ほかの条件に当てはまらないときになります。
文字列を比べるときは string= を使います。
数値の = とは別物です。
| コード | 読み方 | 意味 |
|---|---|---|
cond | コンド | 3つ以上の条件で分ける(condition) |
t | ティー | どれにも当てはまらないとき |
string= | ストリング・イコール | 文字列が等しいか調べる |
string<= | ストリング・以下 | 文字列を辞書順で比べる |
8. 文字を扱う
8.1. やってみよう
(defun count-a (s)
(count #\a s))Code language: Lisp (lisp)
count(カウント):数える
(count-a "banana")
(count-a "apple")
(count-a "lemon")Code language: Lisp (lisp)
#\a の a を別の文字に変えてみましょう。
8.2. 文字列から1文字取り出す
(defun first-char (s)
(char s 0))Code language: Lisp (lisp)
first(ファースト):最初のchar(チャー):character(文字)の略
(first-char "hello")
(first-char "world")Code language: Lisp (lisp)
0 を 1 や 2 に変えるとどうなりますか?
8.3. 次のアルファベット
(defun next-char (c)
(code-char (+ (char-code c) 1)))Code language: Lisp (lisp)
next(ネクスト):次の
(next-char #\a)
(next-char #\z)
(next-char #\A)Code language: Lisp (lisp)
8.4. 小文字にする
(defun to-lower (c)
(char-downcase c))Code language: Lisp (lisp)
lower(ロワー):小文字の、低い
(to-lower #\A)
(to-lower #\B)Code language: Lisp (lisp)
8.5. ここで覚えたこと
"hello" は文字列、#\h は文字1文字です。
書き方が違います。
文字には内側で数値(文字コード)が割り当てられていて、a は 97、b は 98、c は 99……という順番になっています。
だから +1 すると次のアルファベットになります。
| コード | 読み方 | 意味 |
|---|---|---|
#\文字 | ハッシュ・バックスラッシュ | 文字1つ |
char s i | チャー | 文字列 s の i 番目の文字(0から数える) |
char= c1 c2 | チャー・イコール | 文字が等しいか |
count #\文字 s | カウント | 文字列の中にその文字が何個あるか |
char-code c | チャー・コード | 文字を数値に変える |
code-char n | コード・チャー | 数値を文字に変える |
char-downcase c | チャー・ダウンケース | 文字を小文字にする |
9. リストを使う
9.1. やってみよう
REPL に直接打ち込んでみましょう。
(list 1 2 3)
(list 10 20 30 40)Code language: Lisp (lisp)
9.2. リストから値を取り出す
(first (list 5 8 3))
(second (list 5 8 3))
(third (list 5 8 3))Code language: Lisp (lisp)
9.3. 並べ替え
(sort (list 5 2 8 1) #'<)Code language: Lisp (lisp)
sort(ソート):並べ替え
#'< を #'> に変えると大きい順になります。
9.4. リスト全体に計算する
(apply #'+ (list 1 2 3 4))
(apply #'max (list 5 2 8 1))
(apply #'min (list 5 2 8 1))Code language: Lisp (lisp)
apply(アプライ):適用する、使う
9.5. 重複を取り除く
(remove-duplicates (list 1 2 2 3 3 3))Code language: Lisp (lisp)
9.6. 組み合わせて使う
(defun range3 (a b c)
(let* ((lst (sort (list a b c) #'<)))
(- (third lst) (first lst))))Code language: Lisp (lisp)
range(レンジ):幅、範囲。最大値と最小値の差のことlst(リスト):list の略として変数名によく使われる
(range3 3 7 1)
(range3 10 2 5)Code language: Lisp (lisp)
何をしている関数か、わかりますか?
9.7. ここで覚えたこと
リストは複数の値をひとまとめにしたもので、list で作ります。
| コード | 読み方 | 意味 |
|---|---|---|
list a b c | リスト | 値をリストにまとめる |
first second third | ファースト・セカンド・サード | リストの要素を取り出す |
sort リスト #'< | ソート | 昇順に並べる |
length | レングス | リストの要素数 |
apply #'関数 リスト | アプライ | リスト全体に関数を使う |
remove-duplicates | リムーブ・デュプリケーツ | 重複を取り除く |
10. 文字列を変換する
10.1. やってみよう
(coerce "abc" 'list)Code language: Lisp (lisp)
(coerce (list #\a #\b #\c) 'string)Code language: Lisp (lisp)
10.2. 文字列を1文字ずつ変換する
(defun swap-1-9-char (c)
(if (char= c #\1) #\9 #\1))
(defun swap-1-9 (s)
(coerce (loop for c across s collect (swap-1-9-char c))
'string))Code language: Lisp (lisp)
swap(スワップ):入れ替えるcollect(コレクト):集める
(swap-1-9 "119")
(swap-1-9 "191")
(swap-1-9 "911")Code language: Lisp (lisp)
swap-1-9-char を変えて、a を b に変換する関数を作ってみましょう。
10.3. 文字列の1文字だけ変える
(defun change-first (s)
(let* ((result (copy-seq s)))
(setf (char result 0) #\X)
result))Code language: Lisp (lisp)
change(チェンジ):変えるresult(リザルト):結果copy(コピー):複製する、コピーする
(change-first "hello")
(change-first "world")Code language: Lisp (lisp)
0 を 1 に変えると、どこが変わりますか?
10.4. ここで覚えたこと
loop for c across s は文字列 s を1文字ずつ取り出す書き方です。collect で集めたものを coerce で文字列に戻します。
| コード | 読み方 | 意味 |
|---|---|---|
coerce s 'list | コアース | 文字列をリストに変える |
coerce リスト 'string | コアース | リストを文字列に変える |
loop for c across s collect ... | ループ | 文字列を1文字ずつ処理する |
copy-seq s | コピー・シーク | 文字列をコピーする |
setf | セットエフ | 値を書き換える |
11. AtCoder に挑戦しよう
ここまで覚えた機能を組み合わせると、AtCoder のA問題が解けます。
11.1. 解答の基本形
(defun solve (a b)
(+ a b))
(defun main ()
(let* ((a (read))
(b (read)))
(princ (solve a b))
(terpri)))
(main)Code language: Lisp (lisp)
REPL で (solve 3 4) と試してから main でつなぐ——この流れで進めましょう。
次は「ABC A問題で学ぶ Common Lisp 入門」に進んで、実際の問題を解いてみましょう。
11.2. これまでに覚えたすべての機能
| コード | 読み方 | 意味 |
|---|---|---|
defun | デファン | 計算のやり方を決める |
let* | レット・スター | 途中の結果に名前をつける |
read | リード | 数値を1つ読み込む |
read-line | リード・ライン | 1行を文字列として読む |
princ | プリンク | 値を出力する |
terpri | ターポリ | 改行する |
+ - * | プラス・マイナス・アスタリスク | 足し算・引き算・掛け算 |
floor a b | フロア | 切り捨て除算 |
ceiling a b | シーリング | 切り上げ除算 |
mod a b | モッド | 余り |
max min | マックス ミン | 最大値・最小値 |
if | イフ | 2通りに分ける |
cond | コンド | 3通り以上に分ける |
= < <= > >= | イコール、より小、以下、より大、以上 | 数値を比べる |
and or | アンド・オア | 条件を組み合わせる |
evenp oddp zerop | イーブンピー オッドピー ゼロピー | 偶数・奇数・0の判定 |
string= string<= | ストリング・イコール | 文字列を比べる |
char s i | チャー | 文字列の i 番目の文字 |
char= | チャー・イコール | 文字を比べる |
count #\文字 s | カウント | 文字列の中の文字の個数 |
char-code code-char | チャー・コード、 コード・チャー | 文字と数値を変換する |
char-downcase | チャー・ダウンケース | 文字を小文字にする |
list | リスト | 値をリストにまとめる |
first second third | ファースト・ セカンド・ サード | リストの要素を取り出す |
sort リスト #'< | ソート | 昇順に並べる |
length | レングス | 要素数 |
apply #'関数 リスト | アプライ | リスト全体に関数を使う |
remove-duplicates | リムーブ・デュプリケーツ | 重複を取り除く |
coerce | コアース | 文字列とリストを変換する |
loop for c across s collect | ループ | 文字列を1文字ずつ処理する |
copy-seq | コピー・シーク | 文字列をコピーする |
setf | セットエフ | 値を書き換える |