4

我想知道如何从函数内部永久更改全局变量的值,而不使用函数内部的变量名称,即:

(defvar *test1* 5)
(defun inctest (x) (incf x))
(inctest *test1*) ;after it runs, *test1* is still 5, not 6

据此:_

如果传递给函数的对象是可变的并且您在函数中对其进行了更改,则调用者将可以看到更改,因为调用者和被调用者都将引用同一个对象。

这不是我在上面做的吗?

4

4 回答 4

2

如果您想inctest成为一个函数,请将全局变量的名称传递给它。

(defun inctest (x) (incf (symbol-value x)))
(inctest '*test1*)
于 2013-10-24T08:23:22.027 回答
2

您没有按照报价所说的去做,即改变传递给您的函数的对象。您正在改变参数x,即保存对象副本的函数的局部变量。

要做到报价所说的,您需要一个实际上是可变的对象,而数字并非如此。如果您使用诸如列表之类的可变对象,它可以工作:

(defvar *test2* (list 5))
(defun inctest2 (x) (incf (car x)))
(inctest2 *test2*)
*test2* ; => (6)
于 2013-10-24T08:53:54.207 回答
1

在可移植的 Common Lisp 中,没有明确的“指针”概念,所有参数都是按值传递的。为了能够传递函数修改变量的能力,您需要提供一种访问变量的方法。

如果变量是全局变量,那么您可以使用变量的名称 (a symbol),然后使用symbol-value来读取/写入变量:

(defvar *test1* 42)

(defun inctest (varname)
  (incf (symbol-value varname)))

(inctest '*test1*) ;; Note we're passing the NAME of the variable

如果变量是本地的,例如,您可以提供一个闭包,当不带参数调用时返回当前值,而当使用参数调用时,它将设置变量的新值:

(defun inctest (accessor)
  (funcall accessor (1+ (funcall accessor))))

(let ((x 42))
  (inctest (lambda (&optional (value nil value-passed))
             (if value-passed
                 (setf x value)
                 x)))
  (print x))

您还可以编写一个小助手来构建访问器:

(defmacro accessor (name)
  (let ((value (gensym))
        (value-passed (gensym)))
  `(lambda (&optional (,value nil ,value-passed))
     (if ,value-passed
         (setf ,name ,value)
         ,name))))

之后代码变为

 (let ((x 42))
   (inctest (accessor x))
   (print x))
于 2013-10-24T09:11:37.860 回答
0

不,它会更改您提供的副本。

要更改变量本身,请在函数体中使用其名称更改它: (incf *test1*)

编辑:如果你接受宏,这里是,从我的粘液中滚烫:

(defmacro my-incf (variable-name)  
  `(incf ,variable-name))
于 2013-10-24T08:00:10.897 回答