如何改变传递给函数并在函数内部改变的全局变量?

问题描述 投票:0回答:5

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

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

根据这个

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

这不是我上面做的吗?

lisp common-lisp
5个回答
3
投票

如果您希望

inctest
成为一个函数,请将全局变量的 name 传递给它。

(defun inctest (x) (incf (symbol-value x)))
(inctest '*test1*)

2
投票

您没有按照引用所说的去做,即改变传递给您的函数的对象。您正在改变参数

x
,即保存对象副本的函数的局部变量。

要按照引文所述,您需要一个实际上可变的对象,而数字则不然。如果您使用可变对象,例如一个列表,它有效:

(defvar *test2* (list 5))
(defun inctest2 (x) (incf (car x)))
(inctest2 *test2*)
*test2* ; => (6)

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))

0
投票

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

要更改变量本身,请使用其名称在函数体内更改它:

(incf *test1*)

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

(defmacro my-incf (variable-name)  
  `(incf ,variable-name))

0
投票

这是一个老问题,但由于我自己在函数、局部和全局变量的上下文中一直在努力解决

setf
一段时间,所以我想概述一下我所学到的知识。

(setf place newvalue)
place
中,根据 Common Lisp 语言,第二版,可能是以下之一:

  • 变量的NAME。 或
  • 一种函数调用形式,其第一个元素是以下任意一个函数的名称:
    aref
    ,
    car
    ,...

接下来是在函数或宏内使用

setf
进行调用的大量实验,以及对传递给函数或宏的外部变量有或没有的影响。在所有四种情况下,目标都是让函数或宏将全局变量
*y*
的值从
(1 2)
更改为
(2 2)

(defvar *y*)

; Exp. 1
(defun test1 (y)
  (setf y '(2 2)))

(setf *y* '(1 2)) ; (1 2)
(test1 *y*)       ; (1 2) because *y* is a VARIABLE and therefore not a PLACE

; Exp. 2
(defun test2 (y)
  (setf (car y) 2))

(setf *y* '(1 2)) ; (1 2)
(test2 *y*)       ; (2 2) because (car *y*) is a PLACE

; Exp. 3
(defun test3 (y)
  (setf (symbol-value y) '(2 2)))

(setf *y* '(1 2)) ; (1 2)
(test3 '*y*)      ; (2 2) because '*y* is the NAME of VARIABLE *y* and therefore a SYMBOL as well as a PLACE

; Exp. 4
(defmacro test4 (y)
  `(setf ,y '(2 2)))

(setf *y* '(1 2))  ; (1 2)
(test4 *y*)        ; (2 2) because since test4 is a macro, *y* will be evaluated in the global environment
© www.soinside.com 2019 - 2024. All rights reserved.