プログラミングを頑張る日記

プログラミングを勉強して、ハッカーになろう

Common Lispを頑張る(29)

こんばんは。今日も「Land of Lisp」です。
先のページの方見ていたら面白そうなゲームを作っているので早くそこまで行きたいです。

今日から第Ⅲ部「LISPはハックなり」です。カッコいい。

Lispの影の部分

ここまで見てきたのは、簡潔でエレガントなLispのコアの機能だったそうです。
しかし、そうでないところにも、美しさを犠牲にして力を得た機能があるとのこと。
この章では、そういった拡張機能の巨頭、LOOPとFORMATについて見ていくそうです。

LOOP

まずはloopコマンドです。簡単な例が一つ。

CL-USER> (loop for i
              below 5
              sum i)
10
CL-USER>

これまで何度か使いましたが、間抜けにもこのコマンドの特異さに気づけませんでした。
括弧が少ないです。一つの括弧の中に7つも要素が置かれています。
さらにLispらしからぬ、forやbelowやsumといった意味を持つようなトークンがいます。
(最初に見た時「わーわかりやすい、こんな風にも書けるんだ!」と感動してた気がします…)

これまで見てきたLispでは、括弧の一番最初の要素がコードの基本的な振る舞いを決め、
残りは引数でした。しかしloopではいくつかの要素が基本的な振る舞いを決めます。

for:ある範囲の値を移動してゆく変数を宣言する。デフォルトでは0から1ずつ増える。
below:その値まできたらforをストップする。指定した値自体は含まれない。
sum:与えられた式の値を全て合計し、合計値をloop全体の戻り値とする。

ここからはloopの使い方の嵐になります。

始点と終点を指定する

fromとtoを使います。

CL-USER> (loop for i from 10 to 20 sum i)
165
CL-USER>

10から20までの整数の和が出ます。

リストの要素について繰り返す

inを使うことでリストの要素をiに突っ込んで繰り返します。

CL-USER> (loop for i in '(99 1 200 399 111) sum i)
810
CL-USER>

時々他の言語で見かけるforeachみたいなやつですね。こういうのサラッと出せると便利です。

処理を記述する

doを置くことで任意の式を繰り返しの中で実行できます。

CL-USER> (loop for i below 10 do (prin1 "spam"))
"spam""spam""spam""spam""spam""spam""spam""spam""spam""spam"
NIL
CL-USER>

これで足し算以外もできるようになりました

条件が満たされたときだけ

whenで、それに続く部分を必要なときだけ処理するようにします。

CL-USER> (loop for i below 10 when (evenp i) do (princ "spam") (princ i))
spam0spam2spam4spam6spam8
NIL
CL-USER>

ほうほう。doの後はいくつも処理置けるのかな。そしてwhenはどっちにもかかってくれると。
念のために言うとPythonが好きなわけではなく、モンティ・パイソンが好きなだけです。

途中で抜ける

returnを使うことでループの途中で脱出することができます。

CL-USER> (loop for i from 0
            do (dotimes (x i) (prin1 "spam"))
              when (= i 7) return "The Spanish Inquisition")
"spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam""spam"
"The Spanish Inquisition"
CL-USER>

久しぶりにモンティ・パイソンを思い出して楽しくなっています。
最近Netflixでも観れるようになりました。最近ってほど最近でもないかな。

値を集めてリストにする

collectを使って、入力のリストの各要素を変更したりリストを作ったりできます。

CL-USER> (loop for i in '(10 25 50 79)
              collect (* i i))
(100 625 2500 6241)
CL-USER>

forを入れ子にする

勿論入れ子にできます。

CL-USER> (loop for x below 10
               for y below 10
              collect (* x y))
(0 1 4 9 16 25 36 49 64 81)
CL-USER>

インクリメントは同時に行われるようです。
こうすることもできます。

CL-USER> (loop for x below 10
              collect (loop for y below 10
                           collect (* x y)))
((0 0 0 0 0 0 0 0 0 0) (0 1 2 3 4 5 6 7 8 9) (0 2 4 6 8 10 12 14 16 18)
 (0 3 6 9 12 15 18 21 24 27) (0 4 8 12 16 20 24 28 32 36)
 (0 5 10 15 20 25 30 35 40 45) (0 6 12 18 24 30 36 42 48 54)
 (0 7 14 21 28 35 42 49 56 63) (0 8 16 24 32 40 48 56 64 72)
 (0 9 18 27 36 45 54 63 72 81))
CL-USER>

LispLispじゃないみたいに書けます。喜んでいいのかよくわかりませんが。

リストの要素に番号をふるのも簡単にできるようです。

CL-USER> (loop for i from 0
               for day in '(月曜日 火曜日 水曜日 木曜日 金曜日 土曜日 日曜日)
              collect (cons i day))
((0 . 月曜日) (1 . 火曜日) (2 . 水曜日) (3 . 木曜日) (4 . 金曜日) (5 . 土曜日) (6 . 日曜日))
CL-USER>

明日は水曜日。まだ長いですね。

これでloopマクロの用法は抑えたかな…そう思っている自分の前に現れたのは大量のloopの使い方!
いやあ、Loopマクロ周期表と銘打っていっぱい例と使い方が出されている見開きがあるのです。
これを網膜に焼き付ければ自分もLoopマスターになれるとは思います。
でも凄い眠いのでダメです…どちらにせよ全部紹介する気力はないのですが。

さらに次のページから、凄い作りたくてしょうがないゲームの作成が始まります!
でも眠い…短いですがこんな感じで今回は終わります。明日全力でゲーム作成に臨みます。

将来の不安で寝付けなくてですね…もっとPHPとかJavaとかやるべきなのかもしれないですね…。
いや、Lispのが楽しいからそんなところには行きません!
本当は時々勉強しています…流石にLispで転職しようとは思っていないので。
眠さでぼんやりして記事の最後で近況報告というとち狂ったことをしてしまいました。
おやすみなさい。