为什么使用“let”时“setf”不起作用?

Ant*_*ana 4 variables reference common-lisp variable-assignment let

setfwhen 与 结合的行为let让我感到困惑。在这里,setf不会更改列表中的第二个值。

(defun to-temp-field (lst)
  (let ((old-value (nth 2 lst))
        (new-value 'hello))
    (progn
      (setf old-value new-value)
      lst)))
Run Code Online (Sandbox Code Playgroud)

但如果我不这样做let,它会改变:

(defun to-temp-field (lst)
  (let ((new-value 'hello))
    (progn
      (setf (nth 2 lst) new-value)
      lst)))
Run Code Online (Sandbox Code Playgroud)

是什么导致了这种行为?

ex *_*ilo 8

setf接受一个位置和一个值,或多个位置/值对,并将位置的值更改为新值。

第一个代码示例old-value是绑定在表单内的词法变量let词法变量是位置,因此调用将设置to(setf old-value new-value)的值。old-valuenew-value

函数形式也可以是地方,并且nth是一个可以指定地点的函数。在第二个代码示例中,调用(setf (nth 2 lst) new-value)将 place 的值设置(nth 2 lst)为 value new-value

old-value因此,在第一个示例中,词法变量place被更新。但在第二个示例中,表单(nth 2 lst)是一个位置,并且该表单求值的对象(列表的元素lst)被更新。

需要明确的是:地点不是一种价值,而是一种形式。

With(let ((old-value (nth 2 lst)) ;;...)) old-value绑定到表单计算的(nth 2 lst)。但随着(setf (nth 2 lst) new-value)形式被解释为一个地方 其工作原理相同:是一种形式,也是一个地方。这里,变量已被 let-bound 绑定到表单 的值,并且通过改变 的绑定,通过调用中的赋值来替换该值。结果,该地方的价值发生了变化。(nth 2 lst)(setf old-value new-value)old-valueold-value(nth 2 lst)setfold-valueold-value

有几点需要注意:

  • 第二个示例更新了 的第三个元素,lst因为列表在 Common Lisp 中是零索引的。

  • 由于表单的主体是隐式的,因此不需要progn内部的表单。letletprogn

  • 尝试修改文字会导致 Common Lisp 中出现未定义的行为,因此您不应该使用这样的带引号列表来调用第二个函数:(to-temp-field '(1 2 3))或使用绑定到列表文字的变量,而是像这样:(to-temp-field (list 1 2 3))或使用绑定到 a 的变量以其他方式构建的列表。

  • 对于将来阅读这个问题的任何人来说,这可能是应该被接受的答案。 (3认同)