如何在lisp中全局更改函数中的变量值

如何在lisp中全局更改函数中的变量值,lisp,common-lisp,argument-passing,Lisp,Common Lisp,Argument Passing,我想知道是否有任何方法可以用LISP中的指针模拟C的行为。在C语言中,如果更改指针指向的变量的值,则该变量具有全局效应(即,该值也将在函数外部更改) 所以如果我有 (defun mutate ( a ) (some-magic-function a 5) ) a在调用mutate后会变为5,不管它之前是什么 我知道有列表的元素是可能的(很大程度上是一种副作用) 但我想知道如何为整个列表执行此操作。使用函数 如果要使用函数,必须传递符号本身: (defun mutate (symbol

我想知道是否有任何方法可以用LISP中的指针模拟C的行为。在C语言中,如果更改指针指向的变量的值,则该变量具有全局效应(即,该值也将在函数外部更改)

所以如果我有

(defun mutate ( a ) 
   (some-magic-function a 5)
)
a在调用mutate后会变为5,不管它之前是什么

我知道有列表的元素是可能的(很大程度上是一种副作用) 但我想知道如何为整个列表执行此操作。

使用函数 如果要使用函数,必须传递符号本身:

(defun mutate (symbol value)
  (setf (symbol-value symbol) value))
(mutate 'foo 42)
foo
==> 42
请注意,
mutate
symbol
参数被引用

使用宏
符号
参数不再需要引用

讨论 参数在Lisp中,这意味着函数看到的是其参数的值,而不是其存储位置。 因此,作为
(foo(!5))
(foo 120)
(foo x)
调用的函数只看到数字
120
,完全无法知道该数字是由函数返回的(第一种情况)、是文字(第二种情况)还是存储在变量中(第三种情况)

宏接收它们转换的代码并将新代码传递给编译器(或解释器)。因此,宏可以决定如何修改它们的参数(在这种情况下调用)。

C代码讨论 解决问题的要点,但看起来确实有一点混淆,即您正在模拟的C代码中发生了什么:

我想知道是否有任何方法可以模仿C语言的行为 LISP中的指针。在C语言中,如果你改变一个变量的值 指针指向,它具有全局效果(即该值将为 在功能之外也发生了更改)

考虑以下内容,我认为这与您提供的Lisp代码最为相似:

#包括
INTA=3;
int变异(int a){
返回a=5;
}
int main(){
改变(a);/*或改变(8)或任何其他论点*/
printf(“%d\n”,a);/*打印3*/
返回0;
}
代码打印三个,因为
mutate
中的
a
是一个仅存在于
mutate
中的变量。仅仅因为它与全局
a
共享一个名称并不意味着更改一个将更改另一个。此代码中唯一可以更改
mutate
变量
a
值的位置是
mutate
。您没有“更改[a]指针指向的变量的[a]值”选项。您可以做的是将指针传递给变量的值,通过该指针修改值,然后观察值中的结果。这将对应于此C代码:

#包括
INTA=3;
int突变(int*a){
返回(*a=5);
}
int main(){
突变&a;
printf(“%d\n”,a);/*打印5*/
返回0;
}
通过结构间接寻址 您也可以在CommonLisp中使用您喜欢的任何间接方式来执行类似的操作。例如,如果将
a
a
cons
单元格设置为
car
3
,则可以传递该
cons
并修改其
car
的值:

CL-USER>(定义参数*a*(cons 3 nil))
*A*
CL-USER>(解除突变(cons)
(环境运输及工务局局长(车辆管制)5)
变异
CL-USER>(变异*a*)
5.
CL-USER>(汽车*a*)
5.
但是,在Lisp中没有运算符的地址,因此无法完全模拟C代码,如果要使用这种方法,则始终需要以某种方式“包装”该值。您可以使用公共Lisp中的现有结构,如cons单元格、向量或任何其他可以找到的内容

广义参考文献 尽管Common Lisp没有C风格的指针,但它定义了一种非常广泛的方法来引用用于读写的内存位置,称为

广义引用是一种形式的使用,有时称为 放置,就好像它是一个可以读写的变量。这个 place的值是place表单计算到的对象。这个 可以使用setf更改位置的值。绑定的概念 在Common Lisp中没有定义位置,但可以定义实现 允许通过定义此概念来扩展语言

在Common Lisp中,可以使用
setf
指定位置。您可以使用全局变量符号作为
setf
的位置,或使用
symbol value
修改全局变量值的共同点。也就是说,在诸如
(defparameter*a*3)
之类的定义之后,
*a*
(符号值'*a*)
都是可以为
*a*
存储新值的位置。因此,我宁愿用变量名
place
value
编写一个宏,这样就可以清楚地看到,任何位置都可以用作参数:

(defmacro mutate(位置值)
`(setf、地点、价值)
使用词汇闭包模拟指向变量的C风格指针 因为词汇变量也是位置,所以还有另一个选项尚未考虑。您可以使用词法闭包来创建函数,这些函数将为您提供与C风格指针相同的功能

(定义宏生成指针(位置)
`(λ(op和可选值)
(电子计算机操作)
((读),地点)
((写入)(setf,放置值щщ))
(让*((x 3)
(xp(生成指针x)))
(funcall xp'write 5);将新值写入x
(列表(funcall xp'读取);从x到xp读取值
x) );直接从x读取值
;=> (5 5)
在这段代码中,
makepointer
返回一个可以用一个或两个参数调用的函数。第一个参数应该是符号,
read
(defmacro mutate (symbol value)
  `(setf ,symbol ,value))
(mutate foo 42)
foo
==> 42
(make-pointer (aref some-array (print 2)))
;;; The basic idea is to use closures
;;; It looks as though we are done
;;; now we can translate C code
;;; &x = (addr x), *x = (data x)
;;; The trouble is, we have a multiple evaluation bug.
(defun mutate-to-five (ptr)          |            void mutate_to_five(int *a)
  (setf (deref ptr) 5))              |            { *ptr = 5; }
                                     |
(defparameter a 42)                  |            int a = 42;
                                     |            /*...*/
(mutate-to-five (ref a))             |              mutate_to_five(&a);
;;;
;;; Lisp references: pointer-like place locators that can be passed around.
;;; Source: http://www.kylheku.com/cgit/lisp-snippets/plain/refs.lisp
;;; 
;;; How to use:
;;;
;;; Produce a reference which "lifts" the place designated
;;; by form P:
;;;
;;;   (ref p)
;;;
;;; Dereference a reference R to designate the original place:
;;;
;;;   (deref r)
;;;   (setf (deref r) 42) ;; store new value 42
;;;
;;; Shorthand notation instead of writing a lot of (deref)
;;; Over FORMS, A is a symbol macro which expands to 
;;; (DEREF RA), B expands to (DEREF RB):
;;;
;;;   (with-refs ((a ra) (b rb) ...)
;;;     
;;;     ... forms)
;;; 
(defstruct ref 
  (get-func) 
  (set-func))

(defun deref (ref) 
  (funcall (ref-get-func ref))) 

(defun (setf deref) (val ref) 
  (funcall (ref-set-func ref) val)) 

(defmacro ref (place-expression &environment env)
  (multiple-value-bind (temp-syms val-forms 
                        store-vars store-form access-form)
                        (get-setf-expansion place-expression env)
    (when (cdr store-vars)
      (error "REF: cannot take ref of multiple-value place"))
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms)
       (make-ref
         :get-func (lambda () ,access-form)
         :set-func (lambda (,@store-vars) ,store-form)))))

(defmacro with-refs ((&rest ref-specs) &body forms) 
  `(symbol-macrolet 
     ,(loop for (var ref) in ref-specs 
            collecting (list var `(deref ,ref))) 
     ,@forms))