为什么内循环收集不返回结果?

Aka*_*sha 2 loops common-lisp

我试图使用标准循环设施来收集结果,但它只返回 nil。为什么是这样?感觉这应该有效:

 (defun coll-intersects (bounds mv)
   (let ((res (list))
     (loop for x from (first bounds) to (+ (first bounds) (third bounds)) do
       (loop for y from (second bounds) to (+ (second bounds) (fourth bounds))
             if (not (member (cl-byte (aref mapa x y)) mv))
             collect (aref mapa x y) into res
             ))))
Run Code Online (Sandbox Code Playgroud)

但不,我必须这样做:

  (defun coll-intersects (bounds mv)
    (let ((res (list)))
     (loop for x from (first bounds) to (+ (first bounds) (third bounds)) do
       (loop for y from (second bounds) to (+ (second bounds) (fourth bounds))
            do
               (if (not (member (cl-byte (aref mapa x y)) mv))
                   (push (aref mapa x y) res))
            ))
      res))
Run Code Online (Sandbox Code Playgroud)

为什么?我真的很困惑为什么第一个不起作用

小智 6

正如埃文斯的回答所说,问题是

(loop ...
      collect ... into x
      ...)
Run Code Online (Sandbox Code Playgroud)

绑定x。这个构造的目的真的是让你可以收集多个列表:

(defun partition (l)
  (loop for e in l
        if (evenp e)
        collect e into evens
        else
        collect e into odds
        finally (return (values evens odds))))
Run Code Online (Sandbox Code Playgroud)

例如。

如果您想从嵌套循环中收集单个列表并且您关心顺序,您可以使用以下技巧:

(defun sublist-evens (l)
  (loop for s in l
        nconcing
        (loop for e in s
              when (evenp e)
              collect e)))
Run Code Online (Sandbox Code Playgroud)

这里外循环本质上nconc是将内循环的结果放在一起。这当然可以嵌套:

(loop ...
      nconcing
      (loop ...
            nconcing
            (loop ...
                  collect ...)))
Run Code Online (Sandbox Code Playgroud)

将工作。也有可能它loop足够聪明,可以将尾指针指向它正在使用nconc/构建的列表nconcing,尽管您必须检查它。

但是,如果您想从某个深层嵌套循环(或任何其他搜索过程)中按顺序构建一些列表,我发现使用收集宏来做到这一点几乎总是更令人愉快(免责声明:我写了这个)。有了这样一个宏,上面的sublist-evens函数看起来像这样:

(defun sublist-evens (l)
  (collecting
    (dolist (s l)
      (dolist (e s)
        (when (evenp e) (collect e))))))
Run Code Online (Sandbox Code Playgroud)

> (sublist-evens '((1 2 3) (4 5 6)))
(2 4 6)
Run Code Online (Sandbox Code Playgroud)

你可以做得更好:

(defun tree-partition (tree)
  (with-collectors (evens odds)
    (labels ((search (it)
               (typecase it
                 (list
                  (dolist (e it)
                    (search e)))
                 (integer
                  (if (evenp it)
                      (evens it)
                    (odds it)))
                 (t
                  (warn "unexpected ~A" (type-of it))))))
      (search tree))))
Run Code Online (Sandbox Code Playgroud)

现在

> (tree-partition '(((1 2 3) (4)) 5))
(2 4)
(1 3 5)
Run Code Online (Sandbox Code Playgroud)

(对于 hack 值,您可以使用另一个宏来更简洁地表达上述内容:

(defun tree-partition (tree)
  (with-collectors (evens odds)
    (iterate search ((it tree))
      (typecase it
        (list
         (dolist (e it)
           (search e)))
        (integer
         (if (evenp it)
             (evens it)
           (odds it)))
        (t
         (warn "unexpected ~A" (type-of it)))))))
Run Code Online (Sandbox Code Playgroud)

免责声明:我也写了那个宏。)