使用非列表尾部构建列表的重点是什么?

时间:2015-02-17 23:54:25

标签: list emacs lisp elisp cons

关于函数nconc的Emacs lisp manual状态:

  

由于nconc的最后一个参数本身并未修改,因此使用常量列表是合理的,例如'(4 5),如上例所示。出于同样的原因,最后一个参数不必是列表

确实我可以写

(setq x '(1 2 3))
=> (1 2 3)

(nconc x 0)
=> (1 2 3 . 0)

但是这会产生一个完全破碎的列表:

(length x)
=> eval: Wrong type argument: listp, 0

(butlast x)
=> butlast: Wrong type argument: listp, 0
  • 如何检索原始列表? (reverse (cdr (reverse '(1 2 3 . 0))))也没有削减它。
  • 在哪种情况下,这是一种有用的模式?在标准分布中,minibuffer.el中的某些函数使用它,特别是completion-all-completions等。

3 个答案:

答案 0 :(得分:5)

他们不是“破碎”的名单;它们实际上称为不正确的列表(与nil - 已终止的列表相对应,这些列表是正确的列表)。许多列表函数(例如您刚刚命名的lengthbutlast期望正确的列表,而listp仅对正确的列表返回true。

不正确的列表用在关联列表中(关联通常不合适;虽然列表本身必须正确)。


如果您想正确制作一个不正确的清单,您有两种选择:

  • 删除“不正确”元素。
  • 将不正确的元素视为正确列表的最后一个元素。

这是我写的一个名为properise的程序,它将执行前者:

(defun properise (x)
  (let ((r nil))
    (while (consp x)
      (push (pop x) r))
    (nreverse r)))

(如果您想要后一种行为,请在(unless (null x) (push x r))行之前添加nreverse。)

答案 1 :(得分:2)

通常我会避免创建这样的数据结构,其中最后一个元素是一个cons单元格,在cdr中有一个非NIL的对象...它使调试更难,它是一个黑客,使代码更难以明白,...

答案 2 :(得分:0)

我仍然不确定为什么这是一个好的模式,但是这里有一个简单的方法可以从一个不合适的列表中获取一个正确的列表,而无需复制:

(defun nmake-proper-list (x)
  (let ((y (last x)))
    (setcdr y nil)
    x))