为什么这个函数每次都返回一个不同的值?

19 lisp scope common-lisp literals

有人可以解释以下行为吗?具体来说,为什么函数每次都返回一个不同的列表?为什么每次调用函数时都没有some-list初始化'(0 0 0)

(defun foo ()
  (let ((some-list '(0 0 0)))
    (incf (car some-list))
    some-list))
Run Code Online (Sandbox Code Playgroud)

输出:

> (foo)
(1 0 0)
> (foo)
(2 0 0)
> (foo)
(3 0 0)
> (foo)
(4 0 0)
Run Code Online (Sandbox Code Playgroud)

谢谢!

编辑:

另外,假设我希望'(1 0 0)每次输出函数,推荐的实现此函数的方法是什么?

Vse*_*kin 25

'(0 0 0)是一个文字对象,它被假定为一个常量(虽然不受修改保护).所以你每次都有效地修改同一个对象.要在每个函数调用时创建不同的对象(list 0 0 0).

所以除非你知道,你正在做什么,你应该总是使用文字列表(比如'(0 0 0))作为常量.

  • "除非你知道,你正在做什么"修改文字数据的行为是未定义的.根据规范,你实际上无法知道你在做什么(确定),所以"你应该**总是**只使用文字列表(如'(0 0 0))作为常量". (4认同)
  • 可能很高兴补充说,quasiquoting也不能保证返回新的列表. (2认同)

gil*_*gan 11

另外,在sbcl REPL中定义此函数会出现以下警告:

  caught WARNING:
    Destructive function SB-KERNEL:%RPLACA called on constant data. 
    See also: 
      The ANSI Standard, Special Operator QUOTE 
      The ANSI Standard, Section 3.2.2.3
Run Code Online (Sandbox Code Playgroud)

这提供了对手头问题的良好暗示.


Rai*_*wig 5

'(0 0 0)在代码中是文字数据.修改此数据具有未定义的行为.Common Lisp实现可能无法在运行时检测到它(除非数据放在某些只读存储空间中).但它可能会产生不良影响.

  • 你会看到这些数据可能(通常是)在同一函数的各种调用中共享

  • 其中一个更微妙的可能错误是:Common Lisp已经定义了各种优化,可以通过编译器来完成.例如,允许编译器重用数据:

例:

(let ((a '(1 2 3))
      (b '(1 2 3)))
  (list a b))
Run Code Online (Sandbox Code Playgroud)

在上面的代码段,编译器可以检测到的文字数据abEQUAL.然后它可以使两个变量都指向相同的文字数据.修改它可能会起作用,但是从a和可以看到更改b.

简介:文字数据的修改是几个微妙错误的来源.尽可能避免使用它.然后,你需要利弊新的数据对象.Consing一般指新鲜的,新的数据结构在运行时的分配.