一般解决方案 - 破坏性访问变量
我想,你想知道的其实是这样的:
(defparameter a 1)
(defmacro foo (x)
`(setf ,x (+ ,x 1))) ;; this macro takes the variable given for x
;; accesses its memory location using `setf`
;; and changes its value to 2
;; by increasing the accessed value by 1
(foo a) ;; a's value becomes 2
a ;; 2
但是,作为普通 lisp 的初学者,要小心地改变周围的值。虽然我不想带你去享受 Lisp 的力量。 :)
特殊解决方案-foo = incf
但是foo(将给定值加 1)
在 common lisp 中恰好是 incf,因为经常需要这个增量。所以对于这种非常特殊的情况,你可以使用incf。
(defparameter a 1)
a ;; => 1
(incf a) ;; incf mutates a to 2 - in exactly the way foo does
a ;; => 2 ;; a ist after that changed.
注意,还有 decf 将变量减 1 - 与 incf 相反。
全局变量的特殊解决方案
但是对于全局变量,你可以使用很正常的defparameter重新赋值给a:
(defparameter a 1)
(defparameter a (foo a)) ;; reassigns new value `2` to `a`
a ;; returns 2, since mutated now
对第一个版本的评论:
(setf <var> <new-value>) 就像一个指针访问变量的内存位置并将<new-value> 给定的内容写入它。
所以在 Lisp 中,你必须使用 defmacro 和 setf 的组合来模仿或实际执行引用调用,就像在 C 或 Python 中一样。
但是您已经从 Python 或 C 中知道,通过引用调用会引发意想不到的副作用。由于性能原因,Python 默认执行按引用调用(因为 Python 被解释,如果每次调用函数时都按值调用会变得非常慢)。但是对于 Lisp,你/你至少可以在你想要比解释 Python 快的数量级时(好吧,我正在简化 - Python 当然可以调用 C 函数并且也非常快 - 但是,纯 Python 当然会是数量级比编译的 Lisp 慢)。你还有很多其他的可能性来编程同样的东西。所以不要试图在 Lisp 中模仿 Python 或其他程序的函数调用行为——这是错误的做法。
学习函数式编程 (FP) 规则,它尽量不改变任何值,而是始终按值调用(就像你的函数 foo 所做的那样)。它产生 2 但留下 a 相同。推理此类程序对大脑更友好,因此在许多情况下可以避免出现可避免的错误。顺便说一句,在处理 Lisp 时,FP 绝对是你应该学习的东西。 FP 是您在学习 Lisp 时学到的许多其他概念之一——我不想错过它。
破坏性函数只有在状态确实不可避免和需要时才有意义,尤其是在性能很重要的情况下。