【问题标题】:Unexpected persistence of data [duplicate]数据的意外持久性[重复]
【发布时间】:2013-09-18 08:53:07
【问题描述】:

我有一个由七个整数组成的列表,最初都是 0,我们称之为“数据”。在运行我的程序的过程中,我想定期将这些整数之一的值加一。在程序结束时,我打印数据。一切都很好,除了在程序的每次连续运行中,上次运行的所有数据值都被添加到本次运行的所有数据值中。我只想要这次运行的数据值。无论数据是类方法中的局部变量、类方法调用的单独函数中的局部变量还是类的槽,都会发生这种意外行为。无论我通过 incf 还是 (setf value (1+ value)) 增加数据的各个值,都会发生这种情况。当我重新加载程序时,数据重置为全零,但是当我再次运行程序时,数据再次将上次运行的所有数据添加到这次运行的数据中。当我增加一个数据值时,我使用函数 nth ,其中 index 是另一个对象插槽的值。什么可能导致我的“数据”列表值的这种不受欢迎的持久性?

【问题讨论】:

  • 请在这个问题中添加一些代码。我们需要看看数据是如何存储的,特别是为了回答这个问题。您提到“数据”列表,如果您在某处修改引用列表,我不会感到惊讶。
  • 正如您所怀疑的,我使用的是引用列表。当我将其更改为函数 list 时,意外行为停止了。

标签: scheme lisp common-lisp literals


【解决方案1】:

你在做这样的事情吗:

CL-USER> (defun foo ()
           (let ((value '(1)))     ; '(1) is literal data
             (incf (car value))))
FOO
CL-USER> (foo)
2
CL-USER> (foo)
3
CL-USER> (foo)
4
CL-USER> (foo)
5

引用的数据是文字数据;它只有一个副本,修改它的后果是不确定的。上述行为很常见,但您不能依赖它。执行此操作时,某些编译器会发出警告。例如,在 SBCL 中:

CL-USER> (defun foo ()
           (let ((value '(1)))
             (incf (car value))))
; in: DEFUN FOO
;     (INCF (CAR VALUE))
; --> LET* 
; ==>
;   (SB-KERNEL:%RPLACA #:TMP1 #:NEW0)
; 
; 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
; 
; compilation unit finished
;   caught 1 WARNING condition
FOO

来自quote 上的 HyperSpec 的相关文本是:

如果文字对象(包括引用对象)被破坏性修改,后果是不确定的。

创建可修改的列表,例如 (list 1),而不是 '(1)。这是一个常见的陷阱,直到您遇到它。 StackOverflow 上还有一些其他问题提到了这个问题。一个非常具体的是

但也有一堆:

同样的事情发生在 Scheme 中,尽管对文档的引用明显不同。对于 R5RS,文档如下:

4.1.2 Literal expressions

... 如第 3.4 节所述,更改常量是错误的(即 文字表达式的值)使用突变过程,如 定车!或字符串集!。

3.4 Storage model

...在许多系统中,需要常量(即 文字表达式)驻留在只读存储器中。为了表达这一点, 可以方便地想象每个表示位置的对象 与一个标志相关联,告诉该对象是可变的还是 不可变的。在这样的系统中,文字常量和返回的字符串 by symbol->string 是不可变对象,而所有对象由 本报告中列出的其他程序是可变的。这是一个错误 尝试将新值存储到由 不可变对象。

对此也有疑问:

【讨论】:

  • 另一个问题,如果我在 SBCL 中定义一个函数为:(defun set-head (x v) (rplaca x v)),那么为什么这个表达式 (let ((x '(a b))) (set-head x 'z)) 不会引发警告?我在 SBCL 1.2.7 上测试它。
  • @xiepan 没有要求发出警告。行为仍未定义。即使在某些情况下确实发出警告的实现中,我也看不到任何保证它们在所有情况下都会这样做。您的代码是否在您编译的文件中?还是您在 REPL 中输入的内容?你可以写信给 SBCL 邮件列表,询问这个具体案例。
猜你喜欢
  • 2015-12-23
  • 1970-01-01
  • 2021-02-17
  • 2016-02-24
  • 1970-01-01
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 2016-09-24
相关资源
最近更新 更多