loopマクロ

Common Lispのloopマクロは大変気持ち悪いので好きになりそうです。
一度じゃ覚えられないだろうからメモ。

;; collectは結果をリストにして返す
CL-USER > (loop for x in '(0 1 2 3 4 5) collect (expt 2 x))
(1 2 4 8 16 32)

;; onだとcdrを繰り返す
CL-USER > (loop for x on '(one two three) do (print x))
(ONE TWO THREE)
(TWO THREE)
(THREE)
NIL

;; Pythonでいうrange関数の代わりに使える!
CL-USER > (loop for x from 1 to 10 collect x)
(1 2 3 4 5 6 7 8 9 10)
;; 10は含まない
CL-USER > (loop for x from 1 below 10 collect x)
(1 2 3 4 5 6 7 8 9)

CL-USER > (loop for x from 10 downto 1 collect x)
(10 9 8 7 6 5 4 3 2 1)
CL-USER > (loop for x from 10 above 1 collect x)
(10 9 8 7 6 5 4 3 2)

;; byで2を指定してみると
CL-USER > (loop for x from 0 to 10 by 2 collect x)
(0 2 4 6 8 10)
;; withで一時的な変数を用意できる。
;; "collect"が"collecting"になっても動作は同じらしい。。
CL-USER > (loop with temp = (loop for x from 0 to 10 collect x)
                for x in temp collecting (expt 2 x) )
(1 2 4 8 16 32 64 128 256 512 1024)

;; appendの例。 appendingも↑と同様。
CL-USER > (loop for x on '(* * *) appending (list x))
((* * *) (* *) (*))
;; これら(do, collect, append)のほかに,
;; nconc, sum, maximize, minimize があるらしい
;; acrossでvector(一次元配列)を扱える
CL-USER > (loop for x across (vector 'one 'two 'three) do (print x))

ONE 
TWO 
THREE 
NIL


;; ハッシュテーブルを扱う(記述が結構めんどい。。)
CL-USER > (setq ht (make-hash-table))
#<EQL Hash Table{0} 216D82DB>
CL-USER > (setf (gethash 'one ht) 1)
1
CL-USER > (setf (gethash 'two ht) 2)
2

CL-USER > (loop for key being the hash-key in ht collect key)
(ONE TWO)
CL-USER > (loop for value being the hash-value in ht collect value)
(1 2)
;; usingでvalueも扱う
CL-USER > (loop for key being the hash-key in ht
                using (hash-value value)
                collect (cons key value) )
((ONE . 1) (TWO . 2))
CL-USER > (loop for x from 1 to 10
                when (evenp x)
                collect x )
(2 4 6 8 10)

CL-USER > (loop for x from 1 to 50
                while (<= x 10)
                count x )
10

CL-USER > (loop for x from 1 to 50
                until (= x 11)
                collect x )
(1 2 3 4 5 6 7 8 9 10)


;; elseは省略可
CL-USER > (loop for x from 1 to 10
                if (evenp x) collect x
                else collect (* x 10) )
(10 2 30 4 50 6 70 8 90 10)


;; returnで脱出
CL-USER > (loop with acc = nil
                for x from 1 to 10
                do (if (= x 5) (return acc) (push x acc)) )
(4 3 2 1)
;; repeatによる繰り返しと,変数の値の更新
CL-USER > (loop repeat 5
                for i = 0 then (+ i 1)
                do (print i) )

0 
1 
2 
3 
4 
NIL
;; 複数のケース。 平行して動作するらしい。
;; 長さがバラバラの場合は一番短いものに合わせる。
CL-USER > (loop for a in '(h l o d)
                for b in '(e o r !)
                for c in '(l w l !)
                append (list a b c) )
(H E L L O W O R L D ! !) ;; これ表示するためにちょっとがんばった!!


まだ色々な姿があるようだけど,これぐらいで・・・。。
loopマクロ実装するの凄いめんどくさそう。

追記 08/07/07

ちょっとメモ

(setq ht (make-hash-table))
(setq foo '(one two three four five))
(loop for n in foo
      for i = 1 then (1+ i);;repeat以外でも使えるみたい!
      do (setf (gethash n ht) i) )

(loop for k being the hash-keys in ht2
      using (hash-value v)
      collect (cons k v) )
;; ((FIVE . 5) (ONE . 1) (FOUR . 4) (THREE . 3) (TWO . 2))

あと、withで用意した一時変数はincf等で値を変えられない?っぽい。

>(loop repeat 5
       with i = 1
       do (prog (setq i (1+ i)) (print i)) )
NIL 
NIL 
NIL 
NIL 
NIL 
NIL

09/01/23 訂正
ご指摘いただきました。progの使い方を間違っていました。
prog使ったことが無かったのですがprognと同じだろうと勘違いして変なことしちゃった。

;; let 同様、progの第一引数は変数の宣言
CL-USER> (prog ((x 1) (y 2) z)
            (print x)
            (print y)
            (print z))

;; 本題
CL-USER> (loop repeat 5
               with i = 0
               do (prog ()
                     (incf i)
                     (princ i) ))
12345 ; できた!
NIL

ご指摘どうもありがとうございました。