tamuraです。
http://www.lispworks.com/documentation/lw51/CLHS/Body/m_loop.htm を見ればほとんどのことは書いてあるけど、「あーarrayを走査したいんだけどどうやるんだっけ」みたいなことはサンプルがあったほうがわかりやすいのでちょっとまとめました。
ちなみに、私はlexだったり字句解析に使うことが多いです。
文字列の走査
(loop for c across "abc"
do (print c))
> #¥a
> #¥b
> #¥c
NIL
たとえばCL上でnamed parameterなSQLを定義してそれをパースしたい、というときにloopを使いました。
(defun parse-sql (sql)
(let* ((params nil)
(sql (with-output-to-string (s)
(loop with colon = NIL
with prev = #\Space
with param = '()
for c across sql
;; colonが現れたらいったん次の文字を読み込む
if (not colon)
if (char= c #\:)
do (setf colon T)
else
;; colonじゃなかったらそのまま出力する
do (write-char c s)
end
else
;; colonが出ててアルファベットor数字ならキーワードとして登録する
if (find c "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
do (push c param)
else
do (setf colon nil)
;; 前の文字がcolonならcolonが続いているだけなので(SQL的にはおかしいけど)colonを2つ出力する
and if (char= prev #\:)
do (write-char prev s) (write-char c s)
(setf colon nil)
;; アルファベットor数字orcolon以外ならキーワードの区切りとする
else
;; :keyword -> ? に置換して出力
do (write-char #\? s) (write-char c s)
(push (intern (format NIL "~:@(~{~A~}~)" (nreverse param)) "KEYWORD") params)
(setf param nil)
end
end
end
do (setf prev c)
finally (when colon
(write-char #\? s)
(push (intern (format NIL "~:@(~{~A~}~)" (nreverse param)) "KEYWORD") params))))))
`(:sql ,sql :keys ,(nreverse params))))
CL-USER> (parse-sql "select * from product where price > :price")
(:SQL "select * from product where price > ?" :KEYS (:PRICE))
CL-USER> (parse-sql "select * from product where price between :low and :hi and maker = :maker")
(:SQL "select * from product where price between ? and ? and maker = ?" :KEYS (:LOW :HI :MAKER))
※これだと文字列の中のパラメータも置換してしまうんですがそこらへんは省略してます
バイトベクタの走査
ファイルから読み込んだバイトベクタを走査したいとき。
(loop for c across #(1 2 3)
do (print c))
> 1
> 2
> 3
NIL
基本的に文字列と同じです。
ストリームから1文字ずつ読み込みたい
(loop for c = (read-char stream)
do (print c))
> #\t
> #\h
> #\i
> #\s
> #\
> #\i
> #\s
> #\
> #\a
> #\
> #\p
> #\e
> #\n
> #\Newline
NIL
メモリに余裕がある(気にしなくていい)なら、これを使うよりもバイトベクタとかにいっきに読み込んでacross
で処理したほうがいいような気がします。
回数指定
cでいうところの
for (i = 0; i < 5; i++) {
}
をするには
(loop repeat 5
do (print "a"))
> "a"
> "a"
> "a"
> "a"
> "a"
NIL
とします。
終わり
いったんこんなところで。
if
とかwhen
とかはまたの機会に(でもサンプル見れば思い出すからいいかな)。