循环遍历列表窗口
大小为 3 的窗口的一些示例:
;; Naïve attempt:
(loop for (first second third) on '(1 2 3 4 5)
do (print (* first second third)))
;; prints 6 24 60 then Errors on (* 4 5 NIL)
;; We will try again and put our attempt into a function
(defun loop-3-window1 (function list)
(loop for (first second third) on list
while (and second third)
do (funcall function first second third)))
(loop-3-window1 (lambda (a b c) (print (* a b c))) '(1 2 3 4 5))
;; prints 6 24 60 and returns NIL
(loop-3-window1 (lambda (a b c) (print (list a b c))) '(a b c d nil nil e f))
;; prints (a b c) (b c d) then returns NIL
;; A second attempt
(defun loop-3-window2 (function list)
(loop for x on list
while (nthcdr 2 x) ;checks if there are at least 3 elements
for (first second third) = x
do (funcall function first second third)))
(loop-3-window2 (lambda (a b c) (print (list a b c))) '(a b c d nil nil e f))
;; prints (a b c) (b c d) (c d nil) (c nil nil) (nil nil e) (nil e f)
;; A (possibly) more efficient function:
(defun loop-3-window2 (function list)
(let ((f0 (pop list))
(s0 (pop list)))
(loop for first = f0 then second
and second = s0 then third
and third in list
do (funcall function first second third))))
;; A more general function:
(defun loop-n-window (n function list)
(loop for x on list
while (nthcdr (1- n) x)
do (apply function (subseq x 0 n))))
;; With potentially efficient implementation:
(define-compiler-macro loop-n-window (n function list &whole w)
(if (typep n '(integer 1 #.call-arguments-limit))
(let ((vars (loop repeat n collect (gensym)))
(vars0 (loop repeat (1- n) collect (gensym)))
(lst (gensym)))
`(let ((,lst ,list))
(let ,(loop for v in vars0 collect `(,v (pop ,lst)))
(loop for
,@(loop for v0 in vars0 for (v vn) on vars
collect v collect '= collect v0 collect 'then collect vn
collect 'and)
,(car (last vars)) in ,lst
do ,(if (and (consp function) (eq 'function (car function))
w