首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >为什么内循环collect不返回结果?

为什么内循环collect不返回结果?
EN

Stack Overflow用户
提问于 2021-06-03 15:42:06
回答 3查看 63关注 0票数 2

我试图使用标准的循环工具来收集结果,但它只返回nil。为什么会这样呢?我觉得这应该行得通:

代码语言:javascript
运行
复制
 (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
             ))))

但不,我必须这么做:

代码语言:javascript
运行
复制
  (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))

为什么?我真的很困惑为什么第一个不能工作

EN

回答 3

Stack Overflow用户

发布于 2021-06-03 19:48:33

正如Ehvince的回答所说,问题是

代码语言:javascript
运行
复制
(loop ...
      collect ... into x
      ...)

绑定x。此构造的目的实际上是让您可以收集多个列表:

代码语言:javascript
运行
复制
(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))))

例如。

如果你想从嵌套循环中收集一个列表,并且你关心的是顺序,你可以这样做:

代码语言:javascript
运行
复制
(defun sublist-evens (l)
  (loop for s in l
        nconcing
        (loop for e in s
              when (evenp e)
              collect e)))

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

代码语言:javascript
运行
复制
(loop ...
      nconcing
      (loop ...
            nconcing
            (loop ...
                  collect ...)))

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

但是,如果您想从一些深度嵌套的循环(或任何其他搜索过程)按顺序构建一些列表,我发现使用collecting macro来做这件事几乎总是更令人愉快的(免责声明:这篇文章是我写的)。使用这样的宏,上面的sublist-evens函数如下所示:

代码语言:javascript
运行
复制
(defun sublist-evens (l)
  (collecting
    (dolist (s l)
      (dolist (e s)
        (when (evenp e) (collect e))))))

代码语言:javascript
运行
复制
> (sublist-evens '((1 2 3) (4 5 6)))
(2 4 6)

你可以做得更好:

代码语言:javascript
运行
复制
(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))))

而现在

代码语言:javascript
运行
复制
> (tree-partition '(((1 2 3) (4)) 5))
(2 4)
(1 3 5)

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

代码语言:javascript
运行
复制
(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)))))))

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

票数 6
EN

Stack Overflow用户

发布于 2021-06-03 18:04:40

下面是第一个代码片段,其中更正了let括号,并将其简化为可重现:

代码语言:javascript
运行
复制
(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

代码语言:javascript
运行
复制
; caught STYLE-WARNING:
;   The variable RES is defined but never used.

这是一个很大的提示。

我想看到的问题是:

对于外部循环,你使用的是nil.

  • collect … into,而不是res,而且你不会返回res,所以函数总是返回nil.

  • collect … into,大概是使用了内部变量,而不是你的res :S此外,循环没有说明如何处理它。我添加了finally (return res),我得到了结果。您还可以像第二个示例中那样使用push。但是,使用外部let.

声明中间变量时,通常不需要使用collect y.

  • it,似乎没有必要使用into

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

代码语言:javascript
运行
复制
(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所指出的)。

或者:

代码语言:javascript
运行
复制
(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)
票数 4
EN

Stack Overflow用户

发布于 2021-06-03 15:49:06

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

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

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67817363

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档