有人可以解释以下行为吗?具体来说,为什么函数每次都返回一个不同的列表?为什么每次调用函数时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)
,推荐的实现此函数的方法是什么?
答案 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))
在上面的代码片段中,编译器可能会检测到a
和b
的文字数据是EQUAL
。然后,它可以使两个变量指向相同的文字数据。修改它可能有效,但从a
和b
可以看到更改。
摘要:文字数据的修改是一些细微错误的来源。尽可能避免使用它。然后你需要 cons 新的数据对象。 Consing 通常意味着在运行时分配新的新数据结构。
答案 3 :(得分:-5)
想自己写一个,但我在网上找到了一个好的:
CommonLisp具有第一类函数,即函数是对象 可以在运行时创建,并作为参数传递给其他函数。 --AlainPicard这些一流的功能也有自己的状态,因此它们是仿函数。所有Lisp函数都是函子;没有 “只是代码”和“功能”的功能之间的分离 对象“。状态采用捕获的词法变量的形式 绑定。您不需要使用LAMBDA来捕获绑定;一个 顶级DEFUN也可以这样做:(让((私有变量42)) (defun foo() ...))
代替...的代码在其词汇中看到私有变量 范围。该变量的一个实例与该变量相关联 并且只有全局绑定到符号FOO的函数对象;该 在评估DEFUN表达式时捕获变量。 然后,此变量的行为类似于C中的静态变量。或者, 或者,您可以将FOO视为具有“单身”的“单身”对象 “实例变量”。 --KazKylheku