问题
I'm wondering how to permanently alter the value of a global variable from inside a function, without using the variable's name inside the function, i.e.:
(defvar *test1* 5)
(defun inctest (x) (incf x))
(inctest *test1*) ;after it runs, *test1* is still 5, not 6
According to this:
if the object passed to a function is mutable and you change it in the function, the changes will be visible to the caller since both the caller and the callee will be referencing the same object.
Is that not what I'm doing above?
回答1:
If you want inctest
to be a function, pass it the name of the global variable.
(defun inctest (x) (incf (symbol-value x)))
(inctest '*test1*)
回答2:
You are not doing what the quote says, i.e. mutating the object passed to your function. You are mutating the parameter x
, i.e. a local variable of your function that holds a copy of the object.
To do what the quote says, you need an object that is actually mutable which is not the case for a number. If you use a mutable object like e.g. a list, it works:
(defvar *test2* (list 5))
(defun inctest2 (x) (incf (car x)))
(inctest2 *test2*)
*test2* ; => (6)
回答3:
In portable Common Lisp there is no explicit concept of "pointer" and all parameters are passed by value. To be able pass a function the ability to modify a variable you need to provide a way to reach the variable.
If the variable is a global then you can use the name of the variable (a symbol
) and then use symbol-value
to read/write the variable:
(defvar *test1* 42)
(defun inctest (varname)
(incf (symbol-value varname)))
(inctest '*test1*) ;; Note we're passing the NAME of the variable
if instead the variable is local you could for example provide a closure that when called without parameters returns the current value and that when called with a parameter instead it will set the new value 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))
You can also write a small helper for building an accessor:
(defmacro accessor (name)
(let ((value (gensym))
(value-passed (gensym)))
`(lambda (&optional (,value nil ,value-passed))
(if ,value-passed
(setf ,name ,value)
,name))))
after which the code becomes
(let ((x 42))
(inctest (accessor x))
(print x))
回答4:
No, it changes a copy that you have provided.
To change the variable itself, change it in the body of the function using its name:
(incf *test1*)
EDIT: in case you'd accept the macro, here it is, piping hot from my slime:
(defmacro my-incf (variable-name)
`(incf ,variable-name))
来源:https://stackoverflow.com/questions/19560063/how-to-mutate-global-variable-passed-to-and-mutated-inside-function