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

时间:2012-01-22 17:08:10

标签: scope lisp common-lisp literals

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

(defun foo ()
  (let ((some-list '(0 0 0)))
    (incf (car some-list))
    some-list))

输出:

> (foo)
(1 0 0)
> (foo)
(2 0 0)
> (foo)
(3 0 0)
> (foo)
(4 0 0)

谢谢!

编辑:

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

4 个答案:

答案 0 :(得分:24)

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

除非你知道,你在做什么,否则你应该始终只使用文字列表(如'(0 0 0))作为常量。

答案 1 :(得分: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

对于手头的问题提供了很好的暗示。

答案 2 :(得分:5)

代码中的

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

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

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

示例:

(let ((a '(1 2 3))
      (b '(1 2 3)))
  (list a b))

在上面的代码片段中,编译器可能会检测到ab的文字数据是EQUAL。然后,它可以使两个变量指向相同的文字数据。修改它可能有效,但从ab可以看到更改。

摘要:文字数据的修改是一些细微错误的来源。尽可能避免使用它。然后你需要 cons 新的数据对象。 Consing 通常意味着在运行时分配新的新数据结构。

答案 3 :(得分:-5)

想自己写一个,但我在网上找到了一个好的:

  

CommonLisp具有第一类函数,即函数是对象   可以在运行时创建,并作为参数传递给其他函数。   --AlainPicard这些一流的功能也有自己的状态,因此它们是仿函数。所有Lisp函数都是函子;没有   “只是代码”和“功能”的功能之间的分离   对象“。状态采用捕获的词法变量的形式   绑定。您不需要使用LAMBDA来捕获绑定;一个   顶级DEFUN也可以这样做:(让((私有变量42))       (defun foo()         ...))

     

代替...的代码在其词汇中看到私有变量   范围。该变量的一个实例与该变量相关联   并且只有全局绑定到符号FOO的函数对象;该   在评估DEFUN表达式时捕获变量。   然后,此变量的行为类似于C中的静态变量。或者,   或者,您可以将FOO视为具有“单身”的“单身”对象   “实例变量”。   --KazKylheku

参考 http://c2.com/cgi/wiki?CommonLisp