公共Lisp中的按对象引用传递
Peter Seibel在实用的Common Lisp中写道,“Common Lisp中的所有值至少在概念上都是对对象的引用。” 我用以下代码尝试了这个概念:公共Lisp中的按对象引用传递,lisp,common-lisp,pass-by-reference,pass-by-value,Lisp,Common Lisp,Pass By Reference,Pass By Value,Peter Seibel在实用的Common Lisp中写道,“Common Lisp中的所有值至少在概念上都是对对象的引用。” 我用以下代码尝试了这个概念: (setf x 5) (setf y x) (print x) % output: x is 5 (print y) % output: y is 5 (setf x 6) (print x) % output: x is 6 (print y) % output: y is 5 如果Lisp是通过对象引用传递的,y应该指向x,因此将x
(setf x 5)
(setf y x)
(print x) % output: x is 5
(print y) % output: y is 5
(setf x 6)
(print x) % output: x is 6
(print y) % output: y is 5
如果Lisp是通过对象引用传递的,y应该指向x,因此将x更改为6也应该将y更改为6。但事实并非如此。相反,看起来Lisp是通过对象值传递的。有人能解释一下发生了什么吗?在Lisp实现中,小整数通常不是引用。说所有值都是公共Lisp中的引用是不正确的。在怀疑值是引用的情况下编程通常是更安全的假设,与编写正确的代码更为一致 但是,您的示例与作为引用实现的小整数是一致的。这并不能证明他们不是 如果变量
x
包含一个类似5
的整数,然后我们用(setf x 4)
赋值给x
,我们就不会将对象5
变为4
。我们正在变异变量绑定x
:我们已经用新值4
覆盖了先前在x
中的5
值
即使我们使用正向引用到堆中的对象(如cons单元格),这也会起作用:
(setf x '(1 . 2))
(setf y x)
y -> (1 . 2)
(setf x '(4 . 5))
y -> (1 . 2)
x
和y
是自变量,独立地保存对cons单元格的引用x
最初保存对(1.2)
对象的引用。我们将x
分配给y
,因此现在y
也包含对(1.2)
的引用。它有自己的参考副本。因此,当我们分配(4.5)
时,x
的引用被(4.5)
的引用覆盖,但y
不受影响。为什么会这样
我们如何通过改变单元格本身来证明CONSE使用引用语义:
(setf x (cons 1 2)) ;; we better not use a literal any more!
x -> (1 . 2)
(setf y x)
y -> (1 . 2)
;; now mutate
(rplaca x 10)
(rplacd x 20)
x -> (10 . 20)
y -> (10 . 20)
由于突变存储在x
中的细胞会使突变出现在存储在y
中的细胞上,我们知道x
和y
必须包含对同一对象的引用
现在关键是:我们不能用整数执行这个测试,因为整数是不可变的!没有类似于rplaca
的函数可以对表示1
的实际位进行压缩,并将其转换为10
eq
函数没有帮助,因为它所能做的只是确认两个值是同一个对象:
(setf x 5)
(setf y x)
(eq x y) -> ?
如果此eq
调用返回T
,则x
和y
是同一对象。我说如果是因为ANSI Common Lisp将此实现保留为特定的。允许实现产生NIL
但是,在小整数(fixnum)直接打包到值中,而不是指向装箱堆对象(流行的实现方法)的实现中,它会产生T
。也就是说,像4
这样的值被认为是一个对象,无论它出现在何处,即使它是通过复制一个直接代表4
的位模式的值来传播的
因此,或多或少可以归结为,您只需知道您的实现是如何工作的,以确定哪些类型的对象是引用。正如@Kaz所提到的,
字符串、数字、符号等文字是以一种特殊的方式实现的,因此在分配给它们时,它们大多会被复制/重新创建
但是一旦你生成了一些更复杂的结构——组合结构-
setf
将引用该位置
(defparameter *x* (list 3))
(setf *y* *x*)
*x* ;; (3)
*y* ;; (3)
;; mutate *x*
(setf (car *x*) 5)
*x* ;; (5)
*y* ;; (5)
;; if you mutate *x* by letting things be there and adding sth e.g.
(setf (cdr *x*) 7)
*x* ;; (5 . 7)
*y* ;; (5 . 7)
(setf (cdr *x*) (list 1 2))
*x* ;; (5 1 2)
*y* ;; (5 1 2)
;; however, completely refedine *x*
;; then *y* will still refer to the old *x*
;; maybe because the creation of a new object (list 5 6)
;; makes the symbol *x* be bound to a completely new place
;; - where (list 5 6) was generated.
(setf *x* (list 5 6))
*x* ;; (5 6)
*y* ;; (5 1 2)
;; at least we can say, as long as we are mutation on the structures
;; of the cons cells - *y* will follow *x*'s mutations
您想要做的事情,可以通过使用函数(使用闭包)引用变量来实现
从现在起,无论您使用(setf*q*)
为*q*
指定什么值,
您将能够通过(y)
但是,要使用(setf(y)
更改*q*
,您必须首先定义相应的setf
函数(并以修改的方式定义它,然后直接修改*q*
)
要有更多的感觉调用变量而不是函数,
人们可能会创建一个readermacro,例如为@y
创建一个readermacro,然后扩展到(y)
。以及定义宏(并在后台定义相应的setf
函数…),这样人们至少会有处理变量的错觉
这些只是我的想法。[这个答案大部分重复了其他人的回答:我在他们存在之前就开始了,不想扔掉它。] Lisp(特别是普通的Lisp,但其他的也一样)是严格按值调用的
(let ((x ...))
(let ((y x))
(f x)
(eql y x)))
这是真的
然而,许多值都是引用,或者换句话说,许多类型的对象都具有标识
从实现上来说,这通常意味着大多数值都是内存中对象的地址
(let ((x (cons nil nil)))
(let ((y x))
...))
然后,x
的值实际上是由cons
构造的对象的地址,y
的值也是如此。这意味着,例如
(defun mutate-cdr (c to)
(setf (cdr c) to))
然后
是3
然而,在一些特殊情况下,某些类型的对象会立即被表示:通常是小数字、字符或其他一些东西。所有立即被表示的对象都是不可变的,因此不会有太大的区别,尽管与此相关的等式有复杂的规则
但是,Lisp功能强大,如果您需要,可以支持引用调用,只需使用一点语法:
(declaim (inline locf (setf locf)))
(defun locf (loc)
(funcall loc 'get))
(defun (setf locf) (new loc)
(funcall loc 'set new))
(defmacro locative (place)
`(lambda (op &optional val)
(ecase op
((get) ,place)
((set) (setf ,place val)))))
现在给
(defun mutate-loc (loc new)
(setf (locf loc) new))
然后
是4
。当然,这适用于任何地方:
(let ((x (vector 1 2 3)))
(let ((y (locative (aref x 2))))
(mutate-loc y 4)
x))
是第二个元素为4
的向量
有些实现将类似这样的东西内置到语言中:我不知道
(defun mutate-loc (loc new)
(setf (locf loc) new))
(let ((x 1))
(let ((y (locative x)))
(mutate-loc y 4)
x))
(let ((x (vector 1 2 3)))
(let ((y (locative (aref x 2))))
(mutate-loc y 4)
x))
(defun display-and-update (text)
(print text)
(setf text (concatenate 'string text ", updated.")))
(let ((my-text "test"))
(display-and-update my-text)
(print my-text))
> "test"
> "test"
(defun display-and-update (sym-text)
(print (symbol-value sym-text))
(set sym-text (concatenate 'string
(symbol-value sym-text)
", updated.")))
(let ((my-text "test"))
(declare (special my-text))
(display-and-update 'my-text)
(print my-text))
> "test"
> "test, updated."
int a = 3;
int *b = &a;
*b = 5; // a is 5
(defparameter a 3)
(defparameter b 'a)
(set b 5) ;; a is 5
(defparameter a 3)
(defparameter b nil)
;; (set b 'a) ;; this won't work, as it's trying to set the symbol nil's value to 'a
;; instead:
(setf b 'a) ;; this is a better match for C's = operator