(defun zigzag-scan (ary &optional (fn (lambda (&key subscripts
index
nth
direction
element)
(list :nth nth
:element element
:subscripts subscripts
:index index
:direction direction)))
&aux (northern-end 0)
(eastern-end (1- (cadr (array-dimensions ary))))
(southern-end (1- (car (array-dimensions ary))))
(western-end 0))
(let ((result '())
(nth 0)
(x 0)
(y 0)
(dir (if (and (= western-end eastern-end)
(/= northern-end southern-end))
'S 'E)))
(loop
(push (funcall fn
:nth nth
:element (aref ary y x)
:subscripts (list y x)
:index (array-row-major-index ary y x)
:direction dir)
result)
(incf nth)
;; 右下隅に到達したら終了。
(when (and (= y southern-end)
(= x eastern-end))
(return (nreverse result)))
;; 移動する。
(ecase dir
(E (incf x))
(S (incf y))
(SW (decf x) (incf y))
(NE (incf x) (decf y)))
;; 次に移動する方向を設定する。
(setf dir
(or (ecase dir
(E (cond ((= y northern-end southern-end) 'E)
((= x western-end eastern-end) 'S)
((= y southern-end) 'NE)
(t 'SW)))
(S (cond ((= x western-end eastern-end) 'S)
((and (= x eastern-end) (= y southern-end) 'NE))
((= x western-end) 'NE)
(t 'SW)))
(SW (cond ((and (= x western-end) (/= y southern-end)) 'S)
((= y southern-end) 'E)))
(NE (cond ((and (= y northern-end) (= x eastern-end)) 'S)
((= x eastern-end) 'S)
((= y northern-end) 'E))))
dir)))))
(loop for (M N) in '((3 3) (4 2) (3 5) (1 8) (9 11))
do (let* ((A (make-array (list M N)))
(path (zigzag-scan A)))
(loop initially (format t "~D ~D~%"
(first (array-dimensions A))
(second (array-dimensions A)))
finally (format t "~2%")
with width = (second (array-dimensions A))
for sq in (sort path #'< :key (lambda (sq) (getf sq :index)))
for index from 0
when (zerop (mod index width))
do (fresh-line)
do (format t "~2D~@{~A~}"
(1+ (getf sq :nth))
(ecase (getf sq :direction)
(N #\↑) (NE #\↗) (E #\→) (SE #\↘)
(S #\↓) (SW #\↙) (W #\←) (NW #\↖))))))