没有区别:所有对象都是通过Lisp中的值传递的(至少在我所知道的所有Lisp中)。 然而 一些对象是 易变的 和conses就是这样一种类型。因此,您可以将cons单元传递给过程并在该过程中对其进行变更。因此,重要的考虑因素是对象是否可变。
特别是这个(Common Lisp)函数总是返回 T 作为它的第一个值,即使它的第二个值可能没有 0 作为汽车或司机。
T
0
(defun cbv (&optional (f #'identity)) (let ((c (cons 0 0))) (let ((cc c)) (funcall f c) (values (eq c cc) c)))) > (cbv (lambda (c) (setf (car c) 1 (cdr c) 2))) t (1 . 2)
但是,由于Common Lisp具有词法范围,第一类函数和宏,您可以做一些欺骗,这使得它看起来有点像发生引用调用:
(defmacro capture-binding (var) ;; Construct an object which captures a binding `(lambda (&optional (new-val nil new-val-p)) (when new-val-p (setf ,var new-val)) ,var)) (defun captured-binding-value (cb) ;; value of a captured binding (funcall cb)) (defun (setf captured-binding-value) (new cb) ;; change the value of a captured binding (funcall cb new)) (defun cbd (&optional (f #'identity)) (let ((c (cons 0 0))) (let ((cc c)) (funcall f (capture-binding c)) (values (eq c cc) c cc))))
现在:
> (cbd (lambda (b) (setf (captured-binding-value b) 3))) nil 3 (0 . 0)
如果你了解它是如何工作的,你可能会理解很多范围和范围。宏在Lisp中工作。
在Common Lisp中按值传递对象的普遍性有一个例外,Rainer在下面的注释中提到了这一点:在某些情况下可能会复制某些基本类型的实例以提高效率。这种情况只发生在特定类型的实例上,并且它发生的对象总是不可变的。为了处理这种情况,CL提供了一个等式谓词, eql 它做同样的事情 eq , 除了 它知道可能以这种方式秘密复制的对象并正确地比较它们。
eql
eq
所以,安全的事情是使用 eql 代替 eq :因为可能被复制的对象总是不可变的,这意味着你永远不会被这个绊倒。
这是一个例子,你自然会认为这些对象是不相同的。鉴于此定义:
(defun compare (a b) (values (eq a b) (eql a b)))
然后在我正在使用的实现中我发现:
> (compare 1.0d0 1.0d0) nil t
所以双精度浮零不是 eq 对自己而言,永远,但它始终如此 eql 对自己。并尝试看起来应该是相同的东西:
> (let ((x 1.0d0)) (compare x x)) t t
因此,在这种情况下,看起来函数调用不是复制对象,而是我开始使用来自阅读器的两个不同对象。但是,始终允许实现随意复制数字,并且可能使用不同的优化设置。