Scheme/Racket:将单个元素附加到列表末尾的最惯用方式

Scheme/Racket: most idiomatic way to append single element to end of list

我想将元素 b 附加到列表 a(比方说 (a1, a2, ... an)),例如将数字 3 附加到 (1 2) 得到 (1 2 3)

到目前为止我一直在做 (append a (list b)),有点长而且不雅,所以我想知道是否有 "better" 方式...

你必须多久追加一次?

如果你想做很多(而不是站在前面),那你就错了。正确的方法是翻转:想想 cons 把东西放在后面,first 检索最后一个元素,rest 检索除最后一个以外的所有元素,等等。然后,您可以使用 list通常。

但是,如果您想将事物放在列表末尾的频率与将事物放在前面的频率一样高,那么这是您可以使用一个列表做的最好的事情。您可以编写一个函数来包装您认为 "inelegant" 的内容。传统上称为snoc(向后cons

(define (snoc lst e) (append lst (list e)))

或者如果您更喜欢自己实现整个事情:

(define (snoc lst e)
  (cond
    [(empty? lst) (list e)]
    [(cons? lst) (cons (first lst) (snoc (rest lst) e))]))

请注意,这两种方法具有相同的时间复杂度:O(n) 其中 n 是列表的长度。

但是如果你想让它高效,你可以使用一种叫做双端队列的数据结构,它非常高效(每个操作的时间恒定)。有关详细信息,请参阅 http://www.westpoint.edu/eecs/SiteAssets/SitePages/Faculty%20Publication%20Documents/Okasaki/jfp95queue.pdf

您是否正在逐步构建列表,一次构建一个项目?如果是这样,惯用的方法是向后构建列表,使用 cons,然后 reversing 最终结果:

(define (map-using-cons-and-reverse f lst)
  (let loop ((result '())
             (rest lst))
    (if (null? rest)
        (reverse result)
        (loop (cons (f (car rest)) (cdr rest))))))

或者,如果您的列表构建适合 "right-fold" 递归方法,那也是惯用的:

(define (map-using-recursion f lst)
  (let recur ((rest lst))
    (if (null? rest)
        '()
        (cons (f (car rest)) (recur (cdr rest))))))

以上代码片段只是为了说明一般情况下的解决方法;对于可以直接使用 fold 实现的东西,比如 map,使用 fold 更加地道:

(define (map-using-cons-and-reverse f lst)
  (reverse (foldl (lambda (item result)
                    (cons (f item) result))
                  '() lst)))

(define (map-using-recursion f lst)
  (foldr (lambda (item result)
           (cons (f item) result))
         '() lst))