即使我增加了该功能的值,它也会在屏幕上打印1。有没有一种方法可以通过引用来调用参数,以便在函数调用后可以使用它?
(defparameter a 1)
(defun foo (x)
(+ x 1))
(foo a)
(print a)
答案 0 :(得分:6)
您的代码不包含任何可就地修改存储位置的运算符的痕迹。变量a
和x
都不会发生突变。
函数foo
返回的数字比输入数字大1
。
请注意,a
和x
是不同的变量;如果我们要递增x
,则对a
无效。
数字在ANSI Lisp中不是可变对象;如果我们将变量x
与(incf x)
一起递增,则会发生计算值(+ 1 x)
,并将此新值存储回x
变量中的情况。
ANSI Lisp函数参数使用“按值传递”,就像大多数主要的Lisp方言一样。调用(foo a)
时,参数表达式a
减少为一个值:它产生值1
。该值不再与变量关联。它被传递到函数中,没有任何来自a
的记忆。在函数内部,实例化了一个在函数本地的新变量x
;它收到参数值的副本:它用1
初始化。因此x
与a
无关;它刚刚收到了相同的值。
答案 1 :(得分:2)
常规解决方案-对变量的破坏性访问
我想,你想知道的实际上是这个:
(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)
在常见的lisp中恰好是incf
,因为经常需要将此增量加1。因此,对于这种非常特殊的情况,您可以只使用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
不变。推理此类程序对大脑更友好,因此可以在许多情况下避免可避免的错误。 FP当然是与Lisp打交道时应该学习的东西。 FP是学习Lisp时所学的知识之一,而我也不想错过它。
破坏性函数仅在状态确实不可避免且需要时才有意义,尤其是在性能很重要的情况下。