微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决为什么内循环收集不返回结果?

我试图使用标准循环工具来收集结果,但它只返回 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
             ))))

但是不,我必须这样做:

  (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))

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

解决方法

正如 Ehvince 的回答所说,问题在于

(loop ...
      collect ... into x
      ...)

绑定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))))

例如。

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

(defun sublist-evens (l)
  (loop for s in l
        nconcing
        (loop for e in s
              when (evenp e)
              collect e)))

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

(loop ...
      nconcing
      (loop ...
            nconcing
            (loop ...
                  collect ...)))

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

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

(defun sublist-evens (l)
  (collecting
    (dolist (s l)
      (dolist (e s)
        (when (evenp e) (collect e))))))

> (sublist-evens '((1 2 3) (4 5 6)))
(2 4 6)

而且你可以做得更好:

(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))))

现在

> (tree-partition '(((1 2 3) (4)) 5))
(2 4)
(1 3 5)

(对于 hack 值,您可以使用 another macro 更简洁地表达上述内容:

(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)))))))

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

,

这是第一个片段,更正了 let 括号,简化为可重现:

(defun coll-intersects (bounds mv)
   (let ((res (list))) ;; <-- third closing paren 
     (loop for x from (first bounds) to (+ (first bounds) (third bounds)) do
       (loop for y from (second bounds) to (+ (second bounds) (fourth bounds))
             if (evenp y)
             collect y into res
             ))))

现在,当我将其输入到 REPL 中时,SBCL 会警告我未使用的 res

; caught STYLE-WARNING:
;   The variable RES is defined but never used.

这是一个很大的提示。

我看到的问题:

  • 您将 do 用于外循环,而不是收集,并且您不返回 res,因此函数始终返回 nil。
  • collect … into 大概使用内部变量,而不是你的 res :S 此外,循环没有说明如何处理它。我添加了 finally (return res) 并得到了结果。您也可以像在第二个示例中一样使用 push。不过好像没必要用into,用collect y就好了。
  • 通常不需要使用外部 let 声明中间变量。

这是一个返回(哑)结果的更简单的函数:

(defun coll-intersects (bounds)
     (loop for x from (first bounds) to (+ (first bounds) (third bounds)) collect
       (loop for y from (second bounds) to (+ (second bounds) (fourth bounds))
             if (evenp y)
             collect y)))

(coll-intersects '(1 2 3 4))
((2 4 6) (2 4 6) (2 4 6) (2 4 6))

如果您使用 nconcing 而不是第一个 collect,您将得到一个平面列表(如@tfb 所指出的)。

或:

(defun coll-intersects (bounds)
   (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 (evenp y)
             do (push y res)
             ))
     res))

(coll-intersects '(1 2 3 4))
(6 4 2 6 4 2 6 4 2 6 4 2)
,

在您的第一个示例中,函数的返回值是外部循环的返回值。它不收集任何值(内循环会),因此很可能只返回一个 nil

在您的第二个示例中,您的函数显式返回 res 的值。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。