lisp - 在 Common Lisp 中传递对象引用
问题描述
Peter Seibel 在 Practical 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 更改为 6 也应该将 y 更改为 6。但事实并非如此。看起来 Lisp 是按对象值传递的。有人可以帮忙解释发生了什么吗?
解决方案
在 Lisp 实现中,小整数通常不被引用。说所有值都是 Common Lisp 中的引用是不正确的。在怀疑值是引用的情况下进行编程通常是更安全的假设,更符合编写正确的代码。
但是,您的示例与作为引用实现的小整数一致。这并不能证明他们不是。
如果一个变量x
持有一个像 一样的整数5
,然后我们分配给x
with (setf x 4)
,我们并没有将对象变异5
为4
。我们正在改变变量绑定x
:我们已经用新值覆盖了5
之前存在的值。x
4
即使我们使用正向引用到堆中的对象(如 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
不受影响。为什么应该这样?
我们如何证明 conses 使用引用语义是通过改变单元格本身:
(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
!
但是,它T
在小整数(fixnums)被直接打包到一个值中的实现中产生,而不是指向装箱堆对象的指针(流行的实现方法)。也就是说,一个像这样的值4
在它出现的任何地方都被认为是一个对象,即使它是通过复制一个保存4
直接表示的位模式的值来传播的。
所以它或多或少归结为你只需要知道你的实现是如何工作的,以确定哪些类型的对象是引用。
推荐阅读
- r - 如何绘制黑体辐射曲线?
- android - 是什么导致 c++ 函数签名不同?
- javascript - 如何克隆svg中的一组元素并在指定坐标中显示克隆?
- c# - C# 可以写入但不能读取任何串行数据
- java - Mapstruct 1.4.x - 可迭代到 nonit。示例与目标点运算符一起工作很奇怪
- sql - 选择存储为字符串的 Oracle 中的嵌套 JSON
- javascript - 使用 axios POST 时出现 CORS 策略问题
- c# - 图片附件未显示在 Facebook Messenger 中
- f# - F# 模式匹配字符串
- python - 如何“剪断”/提取C char [](内存地址)的一部分作为python字符串