【问题标题】:Splitting a list of a-lists into sub-lists将 a-list 列表拆分为子列表
【发布时间】:2019-02-13 23:22:25
【问题描述】:

对于一个爱好项目,我处理像

这样的 a-lists 列表

((0 . 0) (0 . 1) (0 . 3) (0 . 4) (0 . 7) (0 . 8))

其中列表最多可以包含九个元素,并且 a-lists 仅包含从 09 的整数。我想将列表拆分为具有连续cdrs 的子单元。

(((0 . 0) (0 . 1)) ((0 . 3) (0 . 4)) ((0 . 7) (0 . 8)))

一个子单元可以只有一个元素,列表可以没有子单元,例如:

((0 . 0) (0 . 1) (0 . 2) (0 . 4))((0 . 0) (0 . 1) (0 . 2) (0 . 3) (0 . 4))

结果应该是:

(((0 . 0) (0 . 1)) ((0 . 3) (0 . 4)) ((0 . 7) (0 . 8)))

(((0 . 0) (0 . 1) (0 . 2)) ((0 . 4)))

(((0 . 0) (0 . 1) (0 . 2) (0 . 3) (0 . 4)))

使用iterate 我想出了一个两步法。首先,扫描列表,检查是否有子单元,返回间隙的位置。其次,使用我之前实现的函数split-at 拆分列表:

(defun split-at (count original-list)
  (unless (or (null original-list) (minusp count)
              (>= count (length original-list)))
    (list (subseq original-list 0 count)
          (subseq original-list count))))

(defun sub-units (units)
  "Scan UNITS for sub-units."
  (iter
    (for (a . b) in units)
    (for last-b previous b initially -1)
    (for pos from 0)
    (unless (= 1 (- b last-b))
      (collecting pos))))

(defun split-sub-units (units)
  "Splits UNITS into its sub-units."
  (iter
    (with pos = (or (sub-units units) (list 0)))
    (for p in pos)
    (for last-p previous p)
    (for (head tail) first (split-at p units) then (split-at last-p tail))
    (when head
      (collect head into result))
    (finally (return (nconc result (list tail))))))

是否可以将sub-unitssplit-sub-units 这两个函数合并为一个?它对风格或效率有什么影响吗?

【问题讨论】:

    标签: list common-lisp


    【解决方案1】:

    我认为可以通过以下方式迭代来解决问题:收集一个列表中的所有元素,直到它们的cdr连续,然后重复前面的过程,直到原始列表为空,收集所有生成的列表。这可以迭代完成,总成本为 O(n),其中 n 是原始列表的长度。

    我使用loop而不是iterate,因为我更熟悉第一个构造,将其转换为迭代应该很简单。

    (defun split (l)
      (loop for (a b) = (loop for (x . y) on l
                              collect x into a
                              when (and y (/= (cdar y) (1+ (cdr x))))
                                do (return (list a y))   ; split here
                              finally (return (list a nil))) ; never split
            collect a        ; a list has been produced, collect it
            while b          ; if there are other elements
            do (setf l b)))  ; repeat splitting over them
    

    一些测试:

    CL-USER> (split '((0 . 0) (0 . 1) (0 . 2)))
    (((0 . 0) (0 . 1) (0 . 2)))
    CL-USER> (split '((0 . 0) (0 . 1) (0 . 3) (0 . 4) (0 . 7) (0 . 8)))
    (((0 . 0) (0 . 1)) ((0 . 3) (0 . 4)) ((0 . 7) (0 . 8)))
    CL-USER> (split '((0 . 0)))
    (((0 . 0)))
    CL-USER> (split '())
    (NIL)
    

    【讨论】:

    • 谢谢!我无法直截了当地理解这一点。 Loop 在这里很完美!我刚开始使用iterate,还在练习。
    猜你喜欢
    • 2019-01-14
    • 1970-01-01
    • 2012-09-18
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-27
    • 2018-10-24
    相关资源
    最近更新 更多