【问题标题】:How to transform list into sub lists?如何将列表转换为子列表?
【发布时间】:2020-02-25 01:02:20
【问题描述】:
((1 2 3)
(2 3 4)
(3 4 5)
(4 5 6))

来自

(1 2 3 4 5 6)

这种操作的类型是什么?

我尝试了什么:

(loop
   :with l2 = '()
   :with l1 = '(1 2 3 4 5 6)
   :for i :in l1
   :do (push (subseq l1 0 3) l2))

【问题讨论】:

标签: list lisp common-lisp


【解决方案1】:

每次循环都推送同一个子列表。

您可以使用:for sublist on 循环遍历列表的连续尾部。

并使用:collect 列出所有结果,而不是推送到您自己的列表中

(loop
   :for l1 on '(1 2 3 4 5 6)
   :if (>= (length l1) 3)
      :collect (subseq l1 0 3)
   :else 
      :do (loop-finish))

【讨论】:

  • 不错!我还没有使用:on。谢谢。
  • 在您链接的问题中!
  • 这是二次的,因为(length l1)
  • 它也不会在可能的情况下停止,并继续迭代直到列表结束。
  • 谢谢。我没有修复(length l1),但我添加了(loop-finish) 以提前停止。
【解决方案2】:

或者使用map:

(let ((l '(1 2 3 4 5 6)))
  (map 'list #'list l (cdr l) (cddr l)))

;; ((1 2 3) (2 3 4) (3 4 5) (4 5 6))

你可以这样理解:

  • 用于列表 l 与值 (1 2 3 4 5 6)
  • map 超过列表及其两个连续的cdrs
  • 通过在列表的元素上应用#'list map 是并行循环的
  • (当最短列表用完时停止)
  • 并将结果收集为'list

@WillNess 建议更简单:

(let ((l '(1 2 3 4 5 6)))
  (mapcar #'list l (cdr l) (cddr l)))

谢谢!那么我们可以只使用map 变体进行泛化:

(defun subseqs-of-n (l n)
  (apply #'mapcar #'list (subseq (maplist #'identity l) 0 n)))

(maplist #'identity l) 等价于(loop for sl on l collect sl)。 不过,

(loop for sl on l
      for i from 0 to n
      collect sl)

更好,因为它在第 n 轮循环处停止...

【讨论】:

  • (mapcar #'list ... 更简单,也可以工作。但不适用于字符串。 :)
  • @WillNess 谢谢!啊,我不知道mapcar 是可变参数xD。
  • (apply #'mapcar #'list ... 是一个巧妙的著名技巧,如果你问我的话,这是关于 Lisp 的最 Lisp 的一件事。 :) 其他语言必须经过循环来转置列表列表。
  • @WillNess 哦,是的,表达式中包含一些著名的 lisp 词 apply map car list #' 并且做了 lisp 真正擅长的事情! ;) - 我希望在野外/现实生活中更频繁地使用普通的 lisp ...
  • 我的意思是this。 :)(虽然它在 Scheme 中,所以 list 是函数,而不是列表 :))
【解决方案3】:

首先让我们定义一个函数take-n,如果没有足够的项目,它要么返回n 个项目,要么返回一个空列表。它不会扫描整个列表。

(defun take-n (n list)
  (loop repeat n
        when (null list) return (values nil nil)
        collect (pop list)))

然后我们将这个函数take-n移到列表上,直到它返回NIL

(defun moving-slice (n list)
  (loop for l on list
        for p = (take-n n l)
        while p
        collect p))

例子:

CL-USER 207 > (moving-slice 3 '(1 2))
NIL

CL-USER 208 > (moving-slice 3 '(1 2 3))
((1 2 3))

CL-USER 209 > (moving-slice 3 '(1 2 3 4 5 6 7))
((1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7))

【讨论】:

  • 这个答案很好!
  • 是的,有很好的功能感觉。 :) 除非——如果我们在谈论切片——让它也适用于 vectors 是很自然的。
【解决方案4】:

这是 Barmar 的答案的一个版本(应该是公认的),它更笼统,只调用一次 length

(defun successive-leading-parts (l n)
  (loop repeat (1+ (- (length l) n))
        for lt on l
        collect (subseq lt 0 n)))
> (successive-leading-parts '(1 2 3 4) 3)
((1 2 3) (2 3 4))

> (successive-leading-parts '(1 2 3 4) 2)
((1 2) (2 3) (3 4))

【讨论】:

  • Barmar 的代码不仅是二次方的,它甚至不会尽快停止,例如(切片 9999 list-of-length-10000)它将进行 10000 次迭代而不是 2。
【解决方案5】:

或者经典的更多C-like for-loop-ing 用索引来解决它。 但是更多地在字符串/向量上使用它,而不是在列表上使用它,因为它的性能是

  • 对于二次列表
  • 用于向量(字符串!)线性,因此最好与它们一起使用!

感谢@WillNess,他指出了这两个点(参见下面的 cmets)。

(defun subseqs-of-n (ls n) ;; works on strings, too!
  (loop :for i :from 0 :to (- (length ls) n)
        :collect (subseq ls i (+ i n))))

所以在向量/字符串上使用:

(subseqs-of-n "gattaca" 5)
;; ("gatta" "attac" "ttaca")

【讨论】:

  • 这是不必要的二次方。
  • @WillNess True,但它适用于字符串。 (这就是我实际写它的原因)。所以并非完全没有必要;)。
  • 是的,我完全忘记了向量。我的错。 :) 也许只是在答案中提到列表的这个问题?
  • (谢谢!;) - 是的 - 昨天我写了一些其他答案。但后来在工作中需要那个平铺功能。 - 我尝试在工作中使用 CL。并记住:啊,我写了这样的东西来回答!所以更巧合的是,我回到了那个问题并添加了它;))。
  • 它在列表上是二次的,但在向量上是线性的(string 是一个向量)。
猜你喜欢
  • 1970-01-01
  • 2019-10-18
  • 2021-08-21
  • 1970-01-01
  • 2017-01-21
  • 2016-05-04
  • 2022-01-24
  • 2021-05-26
  • 2016-12-16
相关资源
最近更新 更多