How to mutate global variable passed to and mutated inside function?

℡╲_俬逩灬. 提交于 2019-12-10 19:08:41

问题


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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!