【问题标题】:Right hand side also gets assigned右手边也被分配
【发布时间】:2019-09-13 02:49:36
【问题描述】:

直接说明问题;我在 let 语句中将 'temp' 指定为 *TEMP*。当我这样做时,*TEMP* 会准确分配每次迭代时分配的“temp”。最后我最终会推动相同的“temp”值。不知道为什么,你们知道这里出了什么问题吗?

(defparameter *TEMP* `((key value)(key2 value2)(key3 x))
(defparameter *SYNS* NIL)
(defparameter *PUSH-HERE* NIL)

(dolist (i *matrix*)
  (dolist (j i)
    (if  (equal 'keyword (first j)) 
        (foo (second j))))       ;fills *SYNS*
  (let ((temp *TEMP*))
    ;(print *TEMP*) prints temps last assigned value?
    (set-key temp (get-key i))   ;gets key's value
    (set-key2 temp (get-key2 i)) ;gets key2's value    
    (loop while (not (equal 0 (length *SYNS*)))
          do (set-key3 temp (pop *syns*))
          (push temp *PUSH-HERE*))))

结果值变成这样;

 ((key last-assigned-value)(key2 last-assigned-value)(key3 last-assigned-value)
 (key last-assigned-value)(key2 last-assigned-value)(key3 last-assigned-value)
 (key last-assigned-value)(key2 last-assigned-value)(key3 last-assigned-value))

代替;

((key first-assigned-value)(key2 first-assigned-value)(key3 first-assigned-value)
 (key second-assigned-value)(key2 second-assigned-value)(key3 second-assigned-value)
 (key last-assigned-value)(key2 last-assigned-value)(key3 last-assigned-value))

我尝试过打印,就在push line之前,按我的意愿打印了temp,但是在整个迭代完成后,我得到了我放在上面的无意列表。

【问题讨论】:

  • 什么是*matrix*
  • 如果你的代码不是一个完整的列表,很难猜出它的作用。
  • matrix 是 temp 等条目的集合。我正在尝试在 foo 函数中组合新值,并通过 rplacd 函数将这些新值强制设置为模板,此处显示为 temp,然后将它们附加到 matrix。整个代码在 github.com/karavana/cogs/blob/dev/core/funcs_usingLoad.lisp
  • 顺便说一句,文件中有标签,在线阅读很困难。
  • 你的意思是github代码?是的,需要编辑,但这是我目前最少的问题..

标签: list common-lisp variable-assignment


【解决方案1】:

您正在将*temp* 的地址绑定到temp。我假设set-key 更新temprplaca(setf (car place) value)。因此,您也在修改*temp*,因为它与temp 的值相同。您覆盖的每一轮 temp 与您一直推送到新列表的地址相同,因此它会导致 3 个相同的子列表,因为它们是相同的。这也违反了 Common Lisp,因为 *temp* 是引用的文字,因此是常量数据。它在不同的实现中可能表现不同。

您可以执行非破坏性操作来创建新值,而不是破坏性操作,例如。用substitute-if,然后更新绑定:

(setf temp 
      (substitute-if (cons 'key 'new-value) 
                     (lambda (e) (eq e 'key)) 
                     temp
                     :key #'car))

如果您仍然想要突变,您可以使用 copy-alist 来解决它:

(let ((temp (copy-alist *temp*)))
  ...)

【讨论】:

  • 是的,我正在使用 replacd 来更改 setter 函数中 temp 的值。我在问题的 cmets 部分提供了指向我的代码的链接。谢谢你的建议
  • @Karavana 我看过它,它证实了我的怀疑,我的两个建议都会奏效。
【解决方案2】:

变量是一个包含值的框。 cons 单元格(例如,您放入*temp* 的列表的开头)是内存中带有一些指针的位置。当您执行(let ((temp *temp*)) ... 时,您使变量temp 指向与*temp* 相同的内存位,因此当您修改该数据结构时,您正在修改*temp* 指向的同一内存。

修复代码的一种方法是尝试查看 lisp 程序的正常编写方式并使代码更像那样,而不是将您编写 C 程序的知识应用到语义非常不同的语言中(特别是, Common Lisp 没有像 C) 这样的值语义。一项改进是构建您想要的数据,而不是用筷子戳它(即set-key 和朋友)。 我无法执行您的代码,因为您未能提供最小的可执行示例,但您可以通过执行以下操作从所需的输出构造一行:

`((key ,what-you-want-for-first-value)
  (key2 ,second-value)
  (key3 ,third))

这将在新内存中创建一个新对象。

【讨论】:

  • 我在 cmets 部分给出了我的代码的链接。您需要像我一样从路径加载文件,如果需要,我也可以提供。
【解决方案3】:

我在 let 语句中将 'temp' 指定为 *TEMP*。当我这样做时,*TEMP* 会准确分配每次迭代时分配的“temp”

变量temp 获得与变量*temp* 相同的值。完全相同的对象,如果它是一个列表。 *temp* 没有获得新值。

CL-USER 30 > (defvar *temp* '(1 2 3))
*TEMP*

CL-USER 31 > (let ((temp *temp*))
               (eq temp *temp*))
T

【讨论】:

  • 我现在知道它的行为方式,那么新的问题是,如何更改代码以使其行为符合我的意愿:))
猜你喜欢
  • 1970-01-01
  • 2014-06-27
  • 2016-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多