帖创建怪异的行为调用破坏性的Common Lisp函数接收作为参数列表(Strange behavi

2019-10-18 04:59发布

调用破坏性定义接收参数作为一个局部变量,其类型是当我已经得到一个奇怪的行为列表以创建报价 。

破坏性功能:

(defun insert-at-pos (pos list elem)
  (if (= pos 0)
      (cons elem list)
      (let ((aux-list (nthcdr (1- pos) list)))
        (setf (rest aux-list) (cons elem (rest aux-list)))
        list)))

WRONG:局部变量是一个特殊的运营商创建的列表报价 。

(defun test ()
 (let ((l '(1 2 3)))
   (print l)
   (insert-at-pos 2 l 4)
   (print l))) 

> (test)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test)

(1 2 4 3)
(1 2 4 4 3)
(1 2 4 4 3)

> (test)

(1 2 4 4 3)
(1 2 4 4 4 3)
(1 2 4 4 4 3) 

正确 :局部变量是函数创建一个列表清单 。

(defun test2 ()
 (let ((l (list 1 2 3)))
   (print l)
   (insert-at-pos 2 l 4)
   (print l)))

要么

(defun test2 ()
 (let ((l '(1 2 3)))
   (print l)
   (setf l (cons (first l) (cons (second l) (cons 4 (nthcdr 2 l)))))
   (print l)))

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

> (test2)

(1 2 3)
(1 2 4 3)
(1 2 4 3)

是否有人知道这种奇怪的行为的原因是什么?

Answer 1:

如果你在一个函数引用数据,那么它是文字的数据。 的破坏性修改这样的文字数据的影响是未定义在Common Lisp的标准。 在您的示例中,所有的函数调用共享相同的文字数据和实现不提醒你,你正在改变它。 这就是大多数实现做。 但它也可能会想象这使所有的代码(和它的字面数据)到一个只读存储器的一部分的实现。

你可以用这个时髦的效果。

如果你想破坏性修改列表,而不会在潜在的问题,那么你需要在运行时创建一个新的副本。 例如通过调用LISTCOPY-LISTLIST将返回一个新的consed列表。

类似的还有缺陷。 例如想象这些定义的文件:

(defvar *foo* '(1 2 3 4 5 6 ... 10000))

(defvar *foo* '(0 1 2 3 4 5 6 ... 10000))

如果你编译这样的文件与文件的编译器,编译器可以创建一个编译的文件,其中两个变量共享文字数据 - 节省空间。 如果你想在任一列表更改元素,都可能被改变。



文章来源: Strange behavior invoking destructive Common LISP function receiving as argument a list created with quote