loopの:doと:while (:until) の話
comp.lang.lispのとある記事を見てて気づいたんですが、
(loop :until ... :do ...)
なんて書き方ができたんですね。
私は今まで
(loop :do ... :until)
のようなコードしか見たことがありませんでした。
当然、:untilだけでなく:whileでも同じようなことが出来るみたいです。
で、これらは同じ意味かと思ったら違うみたいです。
(loop :do (print 'oh!) :until t) ; OH! ; => NIL (loop :until t :do (print 'oh!)) ; => NIL (macroexpand '(loop :do (print 'oh!) :until t)) ; => ; (MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR))) ; (BLOCK NIL ; (LET NIL ; (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP))) ; (TAGBODY SYSTEM::BEGIN-LOOP ; (PROGN (PROGN (PRINT 'OH!)) (WHEN T (LOOP-FINISH))) ; (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP ; (MACROLET ; ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) ; '(GO SYSTEM::END-LOOP))))))))) ; ; T (macroexpand '(loop :until t :do (print 'oh!))) ; => ; (MACROLET ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-ERROR))) ; (BLOCK NIL ; (LET NIL ; (MACROLET ((LOOP-FINISH NIL '(GO SYSTEM::END-LOOP))) ; (TAGBODY SYSTEM::BEGIN-LOOP ; (PROGN (WHEN T (LOOP-FINISH)) (PROGN (PRINT 'OH!))) ; (GO SYSTEM::BEGIN-LOOP) SYSTEM::END-LOOP ; (MACROLET ; ((LOOP-FINISH NIL (SYSTEM::LOOP-FINISH-WARN) ; '(GO SYSTEM::END-LOOP))))))))) ; ; T
:doを先に書いた場合は、その内容が必ず1度は評価されるのに対し、
:whileや:untilを先に書いた場合はテストが先に行われるため、
:doの内容が評価されない場合もあるみたいです。
CLHSを眺めたところ、loopは基本的に書かれたとおりの順番に動くみたいです。
次のように気持ち悪いことも出来ます。
(loop :for i :from 0 :do (print i) :while (< i 3) :collect i) ; 0 ; 1 ; 2 ; 3 ; => (0 1 2)
:whileを:collectより先に書いてあるので、リストに3は含まれません。
もちろん、:whileと:collectの順番を逆にすると、リストに3が含まれます。
loop恐るべし。