替换嵌套引用列表中的元素会添加新元素吗?

时间:2014-06-23 16:08:10

标签: lisp common-lisp

我有一个嵌套列表,我试图非破坏性地替换它的所有元素(也在嵌套列表中)。也就是说,根据我的输入列表

'(1 '(2 3 4) '(5 6 7) 8 9)

我正在努力实现

'(0 '(0 0 0) '(0 0 0) 0 0)

我尝试了以下

 (defun subs-list (list value)
  "Replaces all elements of a list of list with given value"
  (loop for elt in list
       collect (if (listp elt)
           (subs-list elt value) 
           value)))

但是当我尝试

(subs-list '(1 '(2 3 4) '(5 6 7) 8 9) 0)
(0 (0 (0 0 0)) (0 (0 0 0)) 0 0)

是我得到的输出。 我做错了什么?

3 个答案:

答案 0 :(得分:4)

  

我做错了什么?

您实际上已经使用loop做了很好的工作并且它有效!请注意'代表quote,所以:

'(1 '(2 3 4) '(5 6 7) 8 9)

等于

(quote (1 (quote (2 3 4)) (quote (5 6 7)) 8 9))
;       |  |      | | |    |      | | |   | |
       (0 (0     (0 0 0)) (0     (0 0 0)) 0 0)

你看,你的引号也被替换了(除了第一个,在评估函数参数时已经消耗了)!一句话足以暂停执行。

答案 1 :(得分:3)

Mark's answerwdebeaum's answer解释了为什么你得到了你得到的结果;嵌套引号意味着您实际上有一个类似(1 (quote (2 3 4)) (quote (5 6 7)) 8 9)的列表,并且您正在用quote替换符号0,这就是您获得(0 (0 (0 0 0)) (0 (0 0 0)) 0 0)的原因。您可能只希望'(1 (2 3 4) (5 6 7) 8 9)没有嵌套引号。

值得指出的是,Common Lisp已经提供了在cons-tree中进行非破坏性替换的功能:substsubst-ifsubst-if-not。还有破坏性版本:nsubstnsubst-ifnsubst-if-not。特别是,对于这种情况,您可以使用带有listp和subst-if的complement函数或使用listp和subst-if-来替换列表为0的所有内容。不:

;; With `extra' elements because of the quotes:

(subst-if-not 0 #'listp '(1 '(2 3 4) '(5 6 7) 8 9))
;=> (0 (0 (0 0 0)) (0 (0 0 0)) 0 0) 

(subst-if 0 (complement #'listp) '(1 '(2 3 4) '(5 6 7) 8 9))
;=> (0 (0 (0 0 0)) (0 (0 0 0)) 0 0) 
;; With no `extra' elements:

(subst-if-not 0 #'listp '(1 (2 3 4) (5 6 7) 8 9))
;=> (0 (0 0 0) (0 0 0) 0 0)

(subst-if 0 (complement #'listp) '(1 (2 3 4) (5 6 7) 8 9))
;=> (0 (0 0 0) (0 0 0) 0 0)

如果您想采用wdebeaum's answer中建议的不替换引号的混合方法,您可以这样做:

(subst-if 0 (lambda (x)
              (not (or (listp x)
                       (eq 'quote x))))
          '(1 '(2 3 4) '(5 6 7) 8 9))
;=> (0 '(0 0 0) '(0 0 0) 0 0)

(subst-if-not 0 (lambda (x)
                  (or (listp x)
                      (eq 'quote x)))
          '(1 '(2 3 4) '(5 6 7) 8 9))
;=> (0 '(0 0 0) '(0 0 0) 0 0)

答案 2 :(得分:1)

你必须意识到'foo是(quote foo)的语法糖,所以当你在已经引用的列表中使用引号时,这个:

'(1 '(2 3 4) '(5 6 7) 8 9)

评估为:

(1 (quote (2 3 4)) (quote (5 6 7)) 8 9)

因此,当您将所有列表元素替换为0时,您将得到:

(0 (0     (0 0 0)) (0     (0 0 0)) 0 0)

您需要不在示例中添加额外的引号,或者您需要在子列表中专门处理引号运算符:

(defun subs-list (list value)
  "Replaces all elements of a list of list with given value"

  (loop for elt in list
       collect
         (cond 
           ((listp elt) 
             (subs-list elt value))
           ((eq 'quote elt)
             elt)
           (t
             value))))