嵌套列表中合并首元素相同的子列表

我有一个嵌套列表:

(
(0.103704 0.103604 0.757407 0.837838)
(0.103704 0.81982 0.757407 0.837838)

(0.109259 0.063063 0.324074 0.09009)
(0.816667 0.063063 0.890741 0.09009)

(0.103704 0.103604 0.374074 0.142643)
(0.103704 0.153153 0.890741 0.181682)
(0.103704 0.181682 0.866667 0.192192)
(0.103704 0.192192 0.894444 0.21021)
(0.103704 0.21021 0.896296 0.238739)

(0.475926 0.902402 0.524074 0.933934)

(0.103704 0.238739 0.475926 0.258258)
(0.103704 0.791291 0.864815 0.81982)
(0.103704 0.81982 0.757407 0.837838)
)

希望经过对子列表中具有相同首个元素的列表进行合并,得到如下:

(
(0.103704 0.103604 0.757407 0.837838)

(0.109259 0.063063 0.324074 0.09009)
(0.816667 0.063063 0.890741 0.09009)

(0.103704 0.103604 0.896296 0.238739)

(0.475926 0.902402 0.524074 0.933934)

(0.103704 0.238739 0.757407 0.837838)
)

我做了图片示意:

以红色标注部分为例,红色框内的第一个子列表 (0.103704 0.103604 0.374074 0.142643) 和后续的第二,三,四,五个子列表具有相同的首元素:0.103704,从而我希望第一子列表的前两个元素 (一列表的元素1 一列表的元素2)和第五子列表的后两个元素 (五列表的元素3 五列表的元素4)合并成新的列表 (一列表的元素1 一列表的元素2 五列表的元素3 五列表的元素4)

其实子列表的数据就是(左上角坐标x,左上角坐标 y,右下角坐标 x,右下角坐标 y)。

我希望只要碰到连续的 左上角坐标x 相同的几个列表就进行合并。

即可以把红色的框内的五个子列表想象成五个小的矩形,只要它们的左上角坐标的 x 值相同,就把这五个矩形合并成一个大的新的矩形,这个大矩形的左上角坐标和第一个小矩形的左上角坐标一样,大矩形的右下角坐标和最后一个小矩形的右下角坐标一样。

不知道怎么实现代码。

你这个需求写的这么明确,测试案例也很清楚,丢给 llm 应该是一个很容易解决的问题。记得同样把你的测试用例截图也丢给 llm 。

啥都问 llm 只会写出烂代码

专业 lisper 会这样实现(注意这个实现为了性能是会 in-place update 的):

(defun distructive-merge-rects (rects)
  (if rects
      (let (working return)
        (setq working rects
              return rects)
        (while (setq rects (cdr rects))
          (if (= (caar rects) (caar working))
              (setf (cddar working)
                    (cddar rects)
                    (cdr working)
                    (cdr rects))
            (setf working rects)))
        return)
    rects))

;;;
(pp (distructive-merge-rects '(
(0.103704 0.103604 0.757407 0.837838)
(0.103704 0.81982 0.757407 0.837838)

(0.109259 0.063063 0.324074 0.09009)
(0.816667 0.063063 0.890741 0.09009)

(0.103704 0.103604 0.374074 0.142643)
(0.103704 0.153153 0.890741 0.181682)
(0.103704 0.181682 0.866667 0.192192)
(0.103704 0.192192 0.894444 0.21021)
(0.103704 0.21021 0.896296 0.238739)

(0.475926 0.902402 0.524074 0.933934)

(0.103704 0.238739 0.475926 0.258258)
(0.103704 0.791291 0.864815 0.81982)
(0.103704 0.81982 0.757407 0.837838)
)))
((0.103704 0.103604 0.757407 0.837838)
 (0.109259 0.063063 0.324074 0.09009)
 (0.816667 0.063063 0.890741 0.09009)
 (0.103704 0.103604 0.896296 0.238739)
 (0.475926 0.902402 0.524074 0.933934)
 (0.103704 0.238739 0.757407 0.837838))
4 个赞

万分感谢, :+1:

你好,能帮忙解释一下代码逻辑吗。我试过问 ai,还有自己打印变量,还是没能完全理解。

我不知道 return 的值在 while 循环结束后就能获得结果。 在 while 中没有对 return 做任何数据操作啊,return 最终不是还是等于最开始时 set return rects,也就是嵌套列表的原始值吗?

可是我打印变量 return 在 while 迭代过程中确实会变化,我搞不懂,不知道是哪里没有理解到位?

把几个变量想象成指针,return 一直指向列表头部没有移动。对数据的操作则是通过移动 working 和 rects 来实现的,保持两者差1就能一直向前移动。

@LdBeth 不过原函数的 working 和 rects 在循环过程中会出现对齐的情况,造成额外的无效操作,也许可以改成:

--              return rects
--              rects (cdr rects))
--        (while rects
++              return rects)
++        (while (setq rects (cdr rects))

把 while 过程(指针移动之前的数据)打印出来看看:

          (message "==> working: %s" working)
          (message "==>   rects: %s\n" rects)

测试:

(distructive-merge-rects '(
                           (2 2 3 4)
                           (3 2 3 4)

                           (1 2 2 1)
                           (1 1 3 4)
                           ))

修改前:

==> working: ((2 2 3 4) (3 2 3 4) (1 2 2 1) (1 1 3 4))
==>   rects: ((3 2 3 4) (1 2 2 1) (1 1 3 4))

==> working: ((3 2 3 4) (1 2 2 1) (1 1 3 4)) ;; -- working 和 rects 对齐了
==>   rects: ((3 2 3 4) (1 2 2 1) (1 1 3 4)) ;; -- (setf (cddar working) (cddar rects)
                                             ;;          (cdr working) (cdr rects)) 没有任何实质改变

==> working: ((3 2 3 4) (1 2 2 1) (1 1 3 4))
==>   rects: ((1 2 2 1) (1 1 3 4))

==> working: ((1 2 2 1) (1 1 3 4)) ;; -- 再次对齐
==>   rects: ((1 2 2 1) (1 1 3 4))

==> working: ((1 2 2 1) (1 1 3 4))
==>   rects: ((1 1 3 4))

修改后:

==> working: ((2 2 3 4) (3 2 3 4) (1 2 2 1) (1 1 3 4))
==>   rects: ((3 2 3 4) (1 2 2 1) (1 1 3 4))

==> working: ((3 2 3 4) (1 2 2 1) (1 1 3 4))
==>   rects: ((1 2 2 1) (1 1 3 4))

==> working: ((1 2 2 1) (1 1 3 4))
==>   rects: ((1 1 3 4))
1 个赞
(setq tmp-1 '(a b c d))
(setq tmp-2 (cdr tmp-1))
(setf (car tmp-2) 3)
(list tmp-2 tmp-1)
;; => ((3 c d) (a 3 c d))

lisp 里变量保存的 list 是引用而不是值

    tmp-1
      |     tmp-2
      |       |
      v       v
(cons a (cons b (cons c (cons d nil))))
1 个赞