iterate(iter)を使ってみる

loopの強力版といったところ。

loopとの比較

iterにuptoがないのはびっくりしたがほかはほとんど自然に翻訳できる。
loopのif/when/unlessはanaphoricになっているが、iterは自分でbindしないといけない。
hashの巡回が自然なのがいい。loop版はダサすぎる。

(require 'iterate)
(in-package :iter)

;;;; loop vs iter
;; for = as
(defun at-most-3times (list)
  (loop for item in list
        for i from 1 to 3
     collect (format nil "<~a>" item)))
(defun at-most-3times (list)
  (iter (for item in list)
        (for i from 1 to 3)
        (collect (format nil "<~a>" item))))
(at-most-3times '(1 2 3 4))             ; => ("<1>" "<2>" "<3>")
(at-most-3times '(1 2))                 ; => ("<1>" "<2>")

;; from downfrom upfrom 
;; to upto below downto above
;; by

(loop for i from 1 to 3 collecting i)      ; => (1 2 3)
(iter (for i from 1 to 3) (collecting i))  ; => (1 2 3)
(loop for i from 1 to 5 by 2 collect i)    ; (1 3 5)
(iter (for i from 1 to 5 by 2) (collect i)) ; => (1 3 5)
;; 0-origin
(loop for i upto 10 collect i)          ; => (0 1 2 3 4 5 6 7 8 9 10)
(iter (for i upto 10) (collect i))      ; Unknown keyword UPTO
(loop for i to 10 collect i)            ; => (0 1 2 3 4 5 6 7 8 9 10)
(iter (for i to 10) (collect i))        ; => (0 1 2 3 4 5 6 7 8 9 10)
(loop for i from 0 downto -10 collect i) ; => (0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)
(iter (for i from 0 downto -10) (collect i)) ; => (0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10)
(loop for i below 10 collect i)              ; => (0 1 2 3 4 5 6 7 8 9)
(iter (for i below 10) (collect i))          ; => (0 1 2 3 4 5 6 7 8 9)
(loop for i from 0 above -10 collect i)      ; => (0 -1 -2 -3 -4 -5 -6 -7 -8 -9)
(iter (for i from 0 above -10) (collect i))  ; => (0 -1 -2 -3 -4 -5 -6 -7 -8 -9)

(loop repeat 3 collect 1)               ; => (1 1 1)
(iter (repeat 3) (collect 1))           ; => (1 1 1)

(loop for i in '(10 20) collect (* i 9)) ; => (90 180)
(iter (for i in '(10 20)) (collect (* i 9))) ; => (90 180)
(loop for i in '(10 20 30 40) collect i)     ; => (10 20 30 40)
(iter (for i in '(10 20 30 40)) (collect i)) ; => (10 20 30 40)
(loop for i in '(10 20 30 40) by #'cdr collect i)  ; => (10 20 30 40)
(iter (for i in '(10 20 30 40) by #'cdr) (collect i)) ; => (10 20 30 40)
(loop for i in '(10 20 30 40) by #'cddr collect i)    ; => (10 30)
(iter (for i in '(10 20 30 40) by #'cddr) (collect i)) ; => (10 30)
(loop for i on '(10 20 30 40) collect i) ; => ((10 20 30 40) (20 30 40) (30 40) (40))
(iter (for i on '(10 20 30 40)) (collect i)) ; => ((10 20 30 40) (20 30 40) (30 40) (40))
(loop for i on '(10 20 30 40) by #'cdr collect i)  ; => ((10 20 30 40) (20 30 40) (30 40) (40))
(iter (for i on '(10 20 30 40) by #'cdr) (collect i)) ; => ((10 20 30 40) (20 30 40) (30 40) (40))
(loop for i on '(10 20 30 40) by #'cddr collect i)    ; => ((10 20 30 40) (30 40))
(iter (for i on '(10 20 30 40) by #'cddr) (collect i)) ; => ((10 20 30 40) (30 40))

(loop for x across "abcd" collect x)     ; => (#\a #\b #\c #\d)
(iter (for x across "abcd") (collect x)) ; Unknown keyword ACROSS
(iter (for x in-vector "abcd") (collect x)) ; => (#\a #\b #\c #\d)
(iter (for x in-string "abcd") (collect x)) ; => (#\a #\b #\c #\d)
(iter (for x in-sequence "abcd") (collect x)) ; => (#\a #\b #\c #\d)

;; the = each / of = in / singular
;; for var being the hash-keys in hash
;; for var being the hash-values in hash
;; for var being the symbols in package
;; for var being the present-symbols in package
;; for var being the external-symbols in package
;; 長っ!!
(setq hash (let ((h (make-hash-table)))
             (setf (gethash 1 h) 'one)
             (setf (gethash 2 h) 'two)
             h))                        ; {1:one, 2:two}
(loop for k being the hash-keys in hash using (hash-value v) collect (cons k v)) ; => ((1 . ONE) (2 . TWO))
(loop for v being the hash-values in hash using (hash-key k) collect (cons k v)) ; => ((1 . ONE) (2 . TWO))
;; iterのほうが自然。loopのはかっこわるい。
(iter (for (k v) in-hashtable hash) (collect (cons k v))) ; => ((1 . ONE) (2 . TWO))
;; it
(loop for key in '(1 3 2 5)
     when (gethash key hash)
     collect it)                        ; => (ONE TWO)
(iter (for key in '(1 3 2 5))
      (with it)
      (when (setf it (gethash key hash))
        (collect it)))                  ; => (ONE TWO)
(iter (for key in '(1 3 2 5))
      (for it = (gethash key hash))
      (when it
        (collect it)))                  ; => (ONE TWO)
;; use anaphoric macro
(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
     (if it ,then-form ,else-form)))
(defmacro awhen (test-form &body body)
  `(aif ,test-form
        (progn ,@body)))
(iter (for key in '(1 3 2 5))
      (awhen (gethash key hash)
        (collect it)))                  ; => (ONE TWO)


;; for - initially / for - first
(let ((list '(9 8))
      (num 9999))
  (iter (for num in list)
        (for i first num then (1+ i))   ; 前節に依存するので num == 9
        (collect (cons i num))))        ; => ((9 . 9) (10 . 8))
(let ((list '(9 8))
      (num 9999))
  (iter (for num in list)
        (for i initially num then (1+ i)) ; iterの外側で評価されるので num == 9999のはずなのに
        (collect (cons i num))))        ; なぜかエラー。

;; do* みたいなの
;; x: 0 1 2 4 8
;; y: 1 2 4 8 16
(loop repeat 5
   for x = 0 then y
   for y = 1 then (+ x y)
   collect (cons x y))                  ; => ((0 . 1) (1 . 2) (2 . 4) (4 . 8) (8 . 16))
(iter (repeat 5)
      (for x initially 0 then y)        ; first でもよい
      (for y initially 1 then (+ x y))
      (collect (cons x y)))             ; => ((0 . 1) (1 . 2) (2 . 4) (4 . 8) (8 . 16))

;; y: 1 1 2 4 8
;; x: 0 1 2 4 8
(loop repeat 5
   for y = 1 then (+ x y)
   for x = 0 then y
   collect (cons x y))                  ; => ((0 . 1) (1 . 1) (2 . 2) (4 . 4) (8 . 8))
(iter (repeat 5)
      (for y initially 1 then (+ x y))
      (for x initially 0 then y)
      (collect (cons x y)))             ; => ((0 . 1) (1 . 1) (2 . 2) (4 . 4) (8 . 8))
(iter (repeat 5)
      (for y first 1 then (+ x y))
      (for x first 0 then y)
      (collect (cons x y)))             ; => ((0 . 1) (1 . 1) (2 . 2) (4 . 4) (8 . 8))
;; do みたいなの fibonacci
;; x: 0 1 1 2 3
;; y: 1 1 2 3 5
(loop repeat 5
   for x = 0 then y
   and y = 1 then (+ x y)
   collect y)                           ; => (1 1 2 3 5)
(iter (repeat 5)
      (for x first 0 then y)
      (for px previous x)               ;前のx
      (for y first 1 then (+ px y))
      (collect y))                      ; => (1 1 2 3 5)
;; with var = expr

;; destructing
(loop for (a b) in '((1 2)(3 4)(5 6)) collect
     (format nil "a:~a, b:~a" a b))            ; => ("a:1, b:2" "a:3, b:4" "a:5, b:6")
(iter (for (a b) in '((1 2)(3 4)(5 6)))
      (collect (format nil "a:~a, b:~a" a b))) ; => ("a:1, b:2" "a:3, b:4" "a:5, b:6")

;; collect, append, nconc, count, sum, maximize, minimize

;; return
;; finally

;; when, if, unless
(loop for x from 1 to 4 when (evenp x) sum x) ; => 6
(loop for x from 1 to 4 if (evenp x) sum x)   ; => 6
(iter (for x from 1 to 4) (when (evenp x) (sum x))) ; => 6
(loop for x from 1 to 4 unless (evenp x) sum x) ; => 4
(iter (for x from 1 to 4) (unless (evenp x) (sum x))) ; => 4
;; named
(setq lists '((20 12) (6 7 120)))
(loop named outer for list in lists do
     (loop for item in list do
          (if (oddp item)
              (return-from outer item)))) ; => 7
(iter outer
      (for list in lists)
      (iter (for item in list)
            (if (oddp item)
                (return-from outer item)))) ; => 7

;; initially, finally
;; while, until, always, never, thereis
(loop for n in '(2 4 6) always (evenp n)) ; => T
(iter (for n in '(2 4 6)) (always (evenp n))) ; => T
(loop for n in '(2 4 6) never (oddp n))       ; => T
(iter (for n in '(2 4 6)) (never (oddp n)))   ; => T
(loop for char across "abc123" thereis (digit-char-p char)) ; => 1
(iter (for char in-string "abc123") (thereis (digit-char-p char))) ; => 1
(loop for char across "abcdef" thereis (digit-char-p char))        ; => NIL
(iter (for char in-string "abcdef") (thereis (digit-char-p char))) ; => NIL

高度な機能

以前、loopで足して最大になる数字の組を求めようとしたが、無理っぽかった。
iterだとそれが可能になる。
iterに出会う前までloopに萌えていたが、乗り換えることにした。

Rolling Your Own - The Iterate Manualによると新しくclauseを作ることができる。
難しいので今はパス。

以下の紹介ではloopにはないであろう機能も含まれている。

;; sum / declare
(iter (for i from 1 to 10) (sum i))     ; => 55
(iter (for i from 1 to 10)
      (sum i into x)
      (declare (x fixnum))
      (finally (return x)))             ; => 55
;; factorial 6! / reducing
(iter (for i from 1 to 4) (multiply i)) ; => 24
(iter (for i from 1 to 4) (reducing i by #'*))                 ; => 24
(iter (for i from 1 to 4) (reducing i by #'* initial-value 1)) ; => 24

;; collect
(iter (for i from 1 to 5) (collect i))  ; => (1 2 3 4 5)
(iter (for i from 1 to 5) (collect i at end)) ; => (1 2 3 4 5)
(iter (for i from 1 to 5) (collect i at beginning)) ; => (5 4 3 2 1)
(iter (for i from 1 to 5) (collect i result-type vector)) ; => #(1 2 3 4 5)

;; adjoining
(iter (for x in '(1 1 2 2 3)) (adjoining x)) ; => (1 2 3)
(iter (for x in '(1 1 2 2 3))
      (adjoining x test #'= at end  result-type vector)) ; => #(1 2 3)
;; appending/nconcing/unioning/nunioning
(iter (for l in '((1 1 2)(2 3 3))) (appending l)) ; => (1 1 2 2 3 3)
(iter (for l in '((1 1 2)(2 3 3))) (nconcing l))  ; => (1 1 2 2 3 3)
(iter (for l in '((1 1 2)(2 3 3))) (unioning l))  ; => (1 1 2 3 3)
(iter (for l in '((1 1 2)(2 3 3))) (nunioning l)) ; => (1 1 2 3 3)
(union '(1 1 2) '(2 3 3))                         ; => (1 1 2 3 3)
(iter (for i from 1 to 4) (accumulating i by #'* initial-value 1)) ; => 24

;; finding such-that
(iter (for i from 1 to 4) (finding i such-that #'evenp)) ; => 2
(iter (for i from 1 to 4) (if (evenp i) (collecting i))) ; => (2 4)

;; finding (min|max)imizing (min|max)imizeではダメ!
(iter (for pair in '((1 . 10)(2 . 3)(4 . 6)))
      (for (a . b) = pair)
      (finding pair maximizing (+ a b))) ; => (1 . 10)
(iter (for pair in '((1 . 10)(2 . 3)(4 . 6)))
      (for (a . b) = pair)
      (for sum = (+ a b))
      (finding (list sum pair) maximizing sum)) ; => (11 (1 . 10))
(iter (for i in '(7 -4 2 -3))
      (if (plusp i)
          (finding i such-that (evenp i))
          (finding (- i) such-that (oddp i)))) ; => 2

;; if-first-time
(iter (for i in '(1 2 3))
      (if-first-time (collect i into 1st)
                     (collect i into last))
      (finally (return (list 1st last)))) ; => ((1) (2 3))
;; initially, after-each, else, finally, finally-protected
(iter (with sum) (with i)
      (initially (setf sum 0 i 1))
      (while (<= i 10))
        (incf sum i)
      (after-each (incf i))
      (finally (return sum)))           ; => 55
(iter (with loop-is-executed = nil)
      (until t)
      (setf loop-is-executed t)
      (else (setf loop-is-executed 'not-executed)
            (return loop-is-executed))
      (finally (return 'executed)))     ; => NOT-EXECUTED
(iter (with loop-is-executed = nil)
      (for i from 1 to 2)
      (setf loop-is-executed t)
      (else (setf loop-is-executed 'not-executed)
            (return loop-is-executed))
      (finally (return 'executed)))     ; => EXECUTED
;; finally-protectedはよくわからん。returnできない…

;; next-iteration, finish
(iter (for c in-string "ab123!24")
      (unless (digit-char-p c) (next-iteration))
      (collect c))                      ; => (#\1 #\2 #\3 #\2 #\4)
(iter (for c in-string "ab123!24")
      (if (char= c #\!) (finish))
      (unless (digit-char-p c) (next-iteration))
      (collect c))                      ; => (#\1 #\2 #\3)
;; leave
(iter (for i from 1 to 10)
      (leave i))                        ; => 1

;; multiple accumulattion
(iter (for i from 1 to 3)
      (collect i into nums)
      (collect (sqrt i) into nums)
      (finally (return nums)))          ; => (1 1.0 2 1.4142135 3 1.7320508)
(iter (for i from 1 to 3)
      (collect i)
      (collect (sqrt i)))               ; => (1 1.0 2 1.4142135 3 1.7320508)

;; in (nested iter)
(iter (for i from 1 to 2)
      (iter (for j from 1 to 3)
            (collect (cons i j))))      ; => NIL (wrong version)
(iter outer (for i from 1 to 2)
      (iter (for j from 1 to 3)
            (in outer (collect (cons i j))))) ; => ((1 . 1) (1 . 2) (1 . 3) (2 . 1) (2 . 2) (2 . 3))