【Common Lisp】loopマクロの使い方まとめ
Common Lispでループ処理に使われるloopマクロの使い方です。網羅的にまとめた資料が少ないので、備忘録的にまとめておこうと思います。
loopマクロには多くの機能があります。なので、最初から全てを抑える必要はないと思います。
この記事の内容
loopマクロの機能の簡単な使い方の説明と使用例をひたすら列挙します。
紹介する機能一覧
loopマクロでは様々な節を使って、色々な機能を実現します。たくさんあってごちゃごちゃするので、とりあえずどんなものがあるのかだけ最初に書いておきます。
基本
loopコマンドを使ってループする際の基本的な機能を提供する節
- do
- for
- while
- until
- repeat
- return
- initially, finally
範囲指定
ループする範囲を指定する節
- for, as
- in
- on
- across
- by
- from, to
- upfrom, downfrom
- upto, downto
条件分岐
ループ内の処理を条件によって分岐する節
- if, when
- unless
- and
- else
- end
計算結果の集積
結果を集めてリストなどの形で返す節
- collect
- count
- sum
- minimize
- maximize
- append, nconc
- into
その他
- with
- named
- return-from
各節の説明と例
では、上の節の機能と具体例を書いていきます。
基本
for, as
forループ。ループ変数を指定する。
CL-USER> (loop for i below 5 collect i) (0 1 2 3 4)
CL-USER> (loop as i below 5 collect i) (0 1 2 3 4)
複数のループ変数を指定する場合:
CL-USER> (loop for i below 10 for j below 20 collect (+ i j)) (0 2 4 6 8 10 12 14 16 18)
このように、どちらかのループ範囲が尽きるまでループを行う。
ループをネストさせる:
CL-USER> (loop for i below 10 collect (loop for j below 10 collect (cons i j))) (((0 . 0) (0 . 1) (0 . 2) (0 . 3) (0 . 4) (0 . 5) (0 . 6) (0 . 7) (0 . 8) (0 . 9)) ((1 . 0) (1 . 1) (1 . 2) (1 . 3) (1 . 4) (1 . 5) (1 . 6) (1 . 7) (1 . 8) (1 . 9)) ((2 . 0) (2 . 1) (2 . 2) (2 . 3) (2 . 4) (2 . 5) (2 . 6) (2 . 7) (2 . 8) (2 . 9)) ((3 . 0) (3 . 1) (3 . 2) (3 . 3) (3 . 4) (3 . 5) (3 . 6) (3 . 7) (3 . 8) (3 . 9)) ((4 . 0) (4 . 1) (4 . 2) (4 . 3) (4 . 4) (4 . 5) (4 . 6) (4 . 7) (4 . 8) (4 . 9)) ((5 . 0) (5 . 1) (5 . 2) (5 . 3) (5 . 4) (5 . 5) (5 . 6) (5 . 7) (5 . 8) (5 . 9)) ((6 . 0) (6 . 1) (6 . 2) (6 . 3) (6 . 4) (6 . 5) (6 . 6) (6 . 7) (6 . 8) (6 . 9)) ((7 . 0) (7 . 1) (7 . 2) (7 . 3) (7 . 4) (7 . 5) (7 . 6) (7 . 7) (7 . 8) (7 . 9)) ((8 . 0) (8 . 1) (8 . 2) (8 . 3) (8 . 4) (8 . 5) (8 . 6) (8 . 7) (8 . 8) (8 . 9)) ((9 . 0) (9 . 1) (9 . 2) (9 . 3) (9 . 4) (9 . 5) (9 . 6) (9 . 7) (9 . 8) (9 . 9)))
上の例はi, jによる2重ループとなっている。
while
whileループ。条件が偽になるまで繰り返す。
CL-USER> (loop for i from 0 while (< i 3) do (print i)) 0 1 2 NIL
until
条件が真になるまで繰り返す。
CL-USER> (loop for i from 0 until (> i 3) do (print i)) 0 1 2 3 NIL
repeat
繰り返し回数を指定する。
CL-USER> (loop repeat 5 do (print "five times")) "five times" "five times" "five times" "five times" "five times" NIL
do
do以下に記述する任意の式を実行する。なお、暗黙のprognにより、複数の式を続けて記述できる。
CL-USER> (loop for i below 3 do (fresh-line) (princ "number is ") (print i)) number is 0 number is 1 number is 2 NIL
return
値を返し、ループを抜ける。
CL-USER> (loop for i below 10 when (= i 5) return 'return do (print i)) 0 1 2 3 4 RETURN
initially, finally
それぞれ、ループ直前・直後の処理を記述する。複数の処理を続けて書ける。
CL-USER> (loop initially (print 'init-process) (print 'loop-begin) for i below 3 do (print i) finally (print 'loop-end) (print 'final-process)) INIT-PROCESS LOOP-BEGIN 0 1 2 LOOP-END FINAL-PROCESS NIL
範囲指定
in
ループする範囲をリストで指定できる。
CL-USER> (loop for i in '(1 3 5 7 9) collect i) (1 3 5 7 9)
on
inと似ているが、リストを頭から一つづつ削りながらループする。
CL-USER> (loop as i on '(1 3 5) do (print i)) (1 3 5) (3 5) (5) NIL
across
inと似ているが、リストではなく配列で範囲を指定する。
CL-USER> (loop for i across #(1 3 5) do (print i)) 1 3 5 NIL
by
ループ変数を指定した間隔で増やしながらループできる。
CL-USER> (loop for i from 1 to 10 by 2 collect i) (1 3 5 7 9)
from, to
from m to n
の形で、ループ変数がnからmまでループする。
CL-USER> (loop for i from 1 to 10 collect i) (1 2 3 4 5 6 7 8 9 10)
upfrom, downfrom
upfromはfromと同じ?downfromはループ関数をループごとに減らす時に使う。
CL-USER> (loop for i upfrom 1 to 10 collect i) (1 2 3 4 5 6 7 8 9 10)
CL-USER> (loop for i downfrom 5 to 1 collect i) (5 4 3 2 1)
upto, downto
uptoはtoと同じ?downtoはループ変数を減らしながらループする時に使う
CL-USER> (loop for i from 1 upto 5 collect i) (1 2 3 4 5)
CL-USER> (loop for i from 10 downto 1 collect i) (10 9 8 7 6 5 4 3 2 1)
条件分岐
if, when
条件が真となる場合のみ、when直下の処理を実行する。
CL-USER> (loop for i from 1 to 10 when (oddp i) collect i) (1 3 5 7 9)
unless
条件が偽となる場合のみ、unless直下の処理を実行する。
CL-USER> (loop for i below 4 unless (oddp i) do (print i)) 0 2 NIL
else
ifやwhenで条件が偽の処理を記述する。
CL-USER> (loop for i below 5 if (oddp i) do (print i) else do (print "even")) "even" 1 "even" 3 "even" NIL
and
whenやunlessで実行する処理を複数書きたい時に使う。
CL-USER> (loop for i below 5 when (oddp i) do (fresh-line) and do (princ "number is ") and do (princ i)) number is 1 number is 3 NIL
end
複数の節の終わりを表現する。
CL-USER> (loop for i below 4 when (oddp i) do (print i) end do (print "yup")) "yup" 1 "yup" "yup" 3 "yup" NIL
計算結果の集積
collect
ループごとに指定した値を要素とするリストを作る。
CL-USER> (loop for i below 5 collect i) (0 1 2 3 4)
count
ループ変数の個数を数える。
CL-USER> (loop for i below 5 when (oddp i) count i) 2
sum
総和を計算する。
CL-USER> (loop for i below 5 sum (+ i 1)) 15
minimize
最小の値を見つける。
CL-USER> (loop for i in '(3 2 1 2 3) minimize i) 1
maximize
最大の値を見つける。
CL-USER> (loop for i in '(3 2 1 2 3) maximize i) 3
append, nconc
リストを繋げてリストにする。
CL-USER> (loop for i below 5 append (list 'z i)) (Z 0 Z 1 Z 2 Z 3 Z 4) CL-USER> (loop for i below 5 nconc (list 'z i)) (Z 0 Z 1 Z 2 Z 3 Z 4)
into
ローカル変数を作り、集計した値を格納する。
CL-USER> (loop for i in '(3 8 73 4 -5) minimize i into lowest maximize i into biggest finally (return (cons lowest biggest))) (-5 . 73)
その他
with
ローカル変数を作成する。
CL-USER> (loop with x = (+ 1 2) repeat 5 do (print x)) 3 3 3 3 3 NIL
named, return-from
namedはループに名前をつける。return-fromは指定した名前のループから抜ける。
CL-USER> (loop named outer for i below 10 do (progn (print "outer") (loop named inner for x below i do (print "**inner") when (= x 2) do (return-from outer 'kicked-out-all-the-way)))) "outer" "outer" "**inner" "outer" "**inner" "**inner" "outer" "**inner" "**inner" "**inner" KICKED-OUT-ALL-THE-WAY
まとめ
ひたすらloopの機能を羅列してきました。これでも多すぎ...と感じるのですが、まだ他にもコマンドがあります。ただ、実際に使うのはこれくらいなんじゃないかな?と思ってます。また書きたくなったら随時付け足していきます。
参考
コンラッド・バルスキ(2013)「Land of Lisp」 川合史郎訳 オライリー・ジャパン