将项目附加到列表的末尾?

时间:2021-05-24 03:24:48

标签: append scheme lisp sicp

要在方案中的位置 0 插入一个项目,我可以执行以下操作:

NsdManager.DiscoveryListener discoveryListener = new NsdManager.DiscoveryListener() {

是否有一种内置方法可以在列表的末尾插入一个元素(即,将其附加到列表中)?

4 个答案:

答案 0 :(得分:2)

使用append

在标准 Scheme 中,没有内置的方法可以将项目添加到列表的末尾。如果您确实需要这样做,但很少这样做,您可以使用 append:

;;; Using append: linear in the length of the list `xs`.
(define (append-item xs x)
  (append xs (list x)))
> (append-item '(1 2 3 4) 5)
(1 2 3 4 5)

选择更好的数据结构

如果您需要在列表的末尾添加大量项目,append 将变得昂贵,因为它具有线性时间复杂度。具体的用例将在这里指导您的选择;如果目标是从末尾而不是从前面构建一个列表,那么最好将列表合并并反转它,正如 @Sylwester 所讨论的那样。相反,如果您需要能够从列表的两端 添加和删除元素,您可以考虑使用 double-ended queue。您可以创建自己的库,也可以使用预先存在的库,例如 SRFI 117SRFI 134

在 Scheme 中,如果不遍历列表、维护索引或维护指向列表最后一对的指针,就无法获得列表的最后一对。您可以直接遍历列表(但具有线性时间复杂度);您可以通过维护状态(例如,索引或尾指针)来获得恒定的时间复杂度。当您沿着这条道路开始时,您可能希望创建某种抽象细节的数据结构。双端队列是这种数据结构的一个例子,但作为 @WillNess has observed,它可能是比您需要的更多的数据结构。可以创建更简单的数据结构,但细节取决于实际用例。

列表上的突变

可以使用mutation将一个元素添加到列表的末尾,但这在Scheme中不是惯用的,无论如何这可能是一个坏主意。 (尽管您可能会发现在双端队列或类似数据结构的实现中使用了突变)。您可以改变输入列表中的最后一对,使用 set-cdr! 附加一个包含新元素的列表,如下所示:

;;; Using `set-cdr!`: this is still linear in the length of `xs`, since it
;;; requires `length`.
(define (append-item! xs x)
  (set-cdr! (list-tail xs (- (length xs) 1)) (list x))
  xs)
> (append-item! (list 1 2 3 4) 5)
(1 2 3 4 5)
> (append-item! (list 1 2 3 4) 5)
(1 2 3 4 5)
> (define xs (list 1 2 3 4))
> (append-item! xs 5)
(1 2 3 4 5)
> xs
(1 2 3 4 5)

这是一个坏主意,因为:1) 你不应该尝试修改 Scheme 中的列表文字,这意味着你必须注意给定 append-item! 的列表的出处,以及 2) 它是线性的无论如何,在其输入列表的长度中。

答案 1 :(得分:1)

将单个元素附加到列表中是线性的列表大小,因此它不是“常见”操作。

虽然可以使用 last(setf car)

(defparameter *l* (list 1 2 3))
==> *L*
* (setf (cdr (last *l*)) (list 4))
==> (4)
* *l*
==> (1 2 3 4)

如果你真的想在 end 追加,你可能想使用 vector-push-extend 代替 (适用于可扩展数组而不是列表)。

答案 2 :(得分:1)

原因是列表是单向链表。这样的结构可以在 O(1) 中删除或添加到开头,而对结尾做同样的事情意味着重新创建列表或至少迭代它并改变尾部。

如果您正在构建一个列表,最好反向构建它,然后对结果进行一次反向操作。例如。

(define (add-1 lst)
  (define (helper lst acc)
    (if (null? lst)
        (reverse acc)
        (helper (cdr lst) (cons (+ 1 (car lst)) acc))))

  (helper lst '()))

现在的最终结果是 O(n) 而如果我改为使用 (append acc (list (+ 1 (car lst)))) 而不是使用 reverse,结果将是相同的,但它会创建一个新的 0 列表。 ..n 个元素以及除最后一个元素之外的所有元素都需要垃圾回收。时间复杂度是 O(n^2) 因为 append 是 O(n) 并且它是针对每个元素完成的。随着列表变大,append 版本的性能会很快变得很差。

答案 3 :(得分:0)

以下是您可以实现此目的的示例方法:

(define (list-append lst elem)
  (if (null? lst)
      (cons elem nil) ; for empty element, extend by the list of elem
      (cons (car lst) (list-append (cdr lst) elem))))

(list-append (list-append 1-to-3 77) 200)
; (1 2 3 77 200)
相关问题