今天想了一些类似的东西(在 C 中通过引用样式传递),并检查了 Rainer 对 Variable references in lisp 的回答,我意识到还有另一种方法。
对于引用传递问题,全局变量是答案之一,部分原因是它们是特殊变量。我们可以将一个局部变量定义为特殊变量,并将其传递给一个引用函数。
我们希望有一个函数来改变参数的引用值。
如果你写下面的函数,那将是不正确的,因为这个函数更新的是局部变量文本,而不是引用:
(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"
相反,我们应该使用不同的显式机制来显示我们的目的(例如,在 C 中作为指针运算符 * 和地址 / 引用运算符 & )。这样做的方法是使用符号(在 C 中,最接近的等价物是变量,而不是文字,因为我们也不能在 C 文字上使用指针和地址运算符)。
现在,让我们重新定义函数以使用引用:
(defun display-and-update (sym-text)
(print (symbol-value sym-text))
(set sym-text (concatenate 'string
(symbol-value sym-text)
", updated.")))
这里,符号值函数是我们的“解引用”运算符,就像 C 中的 * 一样。set 是 C 中的 *_ = 运算符,它设置符号的值。
现在,我们如何将局部符号(我实际上应该说词法变量)传递给这个函数的问题由特殊变量解决,因为词法变量不能被另一个范围访问 - 例如函数 display- and-udpate 的范围。上面的相同调用稍作修改以实现此目的:
(let ((my-text "test"))
(declare (special my-text))
(display-and-update 'my-text)
(print my-text))
输出:
> "test"
> "test, updated."
您可能已经注意到,我们使用 'my-text 而不是 my-text 调用 display-and-update,因为 my-text 会评估值并通过“test”,这会失败。 ' 这里就像 C 中的 & 运算符。
现在,为偏离原始问题而道歉,但它们是相关的,至少为了更好地理解。
要让一个变量引用另一个变量,同样在 C 中,我们可以这样写:
int a = 3;
int *b = &a;
*b = 5; // a is 5
在 common-lisp 中实现同样的效果:
(defparameter a 3)
(defparameter b 'a)
(set b 5) ;; a is 5
现在,我们可以再次看到 set 函数实际上不是 C 中的 = 运算符,而是 *var = 运算符,因为它首先获取符号的值并设置它。
例如,如果您创建 b 以等于其他值,则可能为 nil:
(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
会达到同样的效果。