Common Lisp:按值传递与按引用传递

IgS*_*lov 2 lisp common-lisp pass-by-reference pass-by-value

我想用 Common Lisp 编写一个函数,它会破坏性地修改它的参数。在 C 或 Rust 中,我将使用指向对象的指针/引用,该对象可以在函数体内取消引用。在 CL 中我写道:

(defun foo (lst)
  (setf lst NIL))
Run Code Online (Sandbox Code Playgroud)

但在评估此表格后:

(let ((obj (list "a" "b")))
  (foo obj)
  obj) => ("a" "b")
Run Code Online (Sandbox Code Playgroud)

可以看到该函数foo没有任何效果。我可以解释按值传递语义,即我们修改推入函数堆栈的参数的本地副本。如果我们定义另一个函数:

(defun bar (lst)
  (setf (car lst) NIL))
Run Code Online (Sandbox Code Playgroud)

并评估类似的形式

(let ((obj (list "a" "b")))
  (bar obj)
  obj) => (NIL "b")
Run Code Online (Sandbox Code Playgroud)

我们将清楚地看到 已lst被修改,就好像我们将使用按引用传递语义一样。所以,(setf lst NIL)没有工作,但(setf (car lst) NIL)做了。您能解释一下原因吗?

jki*_*ski 5

Common Lisp 按值传递参数,但在大多数情况下(除了像fixnums 或float 这样的原始值类型之外)该值是一个引用。这与大多数托管语言(例如 Java、Python、JS)的工作方式相同。

LET- 形式中,变量的值OBJ是对列表的引用,而不是列表本身。该引用是按值传递的,因此在函数内部,参数的值LST是对同一列表的另一个引用。

在 中FOO,参考值被替换为NIL,但之前参考的列表根本没有被触及。在 中BAR,从堆中检索列表并将其CAR替换为NIL。由于OBJ持有对同一列表的引用,因此修改也会影响它。