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

标签 lisp common-lisp argument-passing

我想知道是否有任何方法可以用LISP中的指针来模拟C行为在C语言中,如果你改变一个变量的值,指针指向的,它有一个全局的效果(即该值也会在函数之外改变)。
所以如果我有

(defun mutate ( a ) 
   (some-magic-function a 5)
)

在调用mutate之后,a将变为5,不管以前是什么。
我知道使用带有列表的元素是可能的(很大程度上是一种副作用)
In common-lisp, how do I modify part of a list parameter from within a function without changing the original list?
但我想知道如何做整个名单。

最佳答案

关于c代码的讨论
sds's answer解决了问题的要点,但看起来您模拟的C代码中发生了什么有点混乱:
我想知道是否有任何方法可以用
LISP中的指针如果你改变一个变量的值
指针指向,它具有全局效果(即
也在函数之外更改)。
请考虑以下内容,我认为这与您提供的lisp代码最为相似:

#include<stdio.h>

int a = 3;

int mutate( int a ) {
  return a = 5;
}

int main() { 
  mutate( a );         /* or mutate( 8 ) or anything other argument */
  printf( "%d\n", a ); /* prints 3 */
  return 0;
}

代码打印三,因为amutate中是一个仅存在于mutate内的变量。仅仅因为它与globala共享一个名称并不意味着更改一个将更改另一个在这段代码中,您可以更改mutate变量a的值的唯一位置是mutate你没有“改变指针指向的变量的值”的选项您可以做的是将指针传递到变量的值,通过该指针修改该值,然后观察值中的结果。这将对应于C代码:
#include<stdio.h>

int a = 3;

int mutate( int *a ) {
  return (*a = 5);
}

int main() { 
  mutate( &a );
  printf( "%d\n", a ); /* prints 5 */
  return 0;
}

间接通过结构
你也可以用普通的Lisp来做这样的事情,使用任何你喜欢的间接方式例如,如果将a设为conscar单元格,则可以传递该3并修改其cons的值:
CL-USER> (defparameter *a* (cons 3 nil))
*A*
CL-USER> (defun mutate (cons)
           (setf (car cons) 5))
MUTATE
CL-USER> (mutate *a*)
5
CL-USER> (car *a*)
5

但是,在lisp中没有operator的地址,所以不能完全模拟c代码,如果您想使用这种方法,您总是需要以某种方式“包装”该值。可以使用普通LISP中的现有结构,如CONS单元、向量或任何其他可以找到的结构。
广义参考文献
虽然Common Lisp没有C风格的指针,但它定义了一种非常广泛的引用读写内存位置的方法,称为Generalized Reference
5.1.1 Overview of Places and Generalized Reference
广义引用是一种形式的使用,有时称为
地方,好像它是一个可以读写的变量这个
place的值是place窗体计算的对象这个
一个地方的值可以通过使用setf来更改约束的概念
公共Lisp中没有定义位置,但是实现是
允许通过定义这个概念来扩展语言。
在Common Lisp中,可以使用car指定位置suggestions that sds gave共享的共同点是,您可以使用全局变量符号作为setf的位置,或使用setf来修改全局变量的值也就是说,在定义(如symbol-value之后,(defparameter *a* 3)*a*都是可以为(symbol-value '*a*)存储新值的位置因此,我宁愿编写一个变量名为*a*place的宏,这样就可以清楚地看到任何地方都可以用作参数:
(defmacro mutate (place value)
  `(setf ,place ,value))

使用词法闭包模拟C风格指针到变量
因为词法变量也是位置,还有另外一个尚未考虑的选项。可以使用词法闭包创建函数,这些函数将提供与C样式指针相同的功能。
(defmacro make-pointer (place)
  `(lambda (op &optional value)
     (ecase op
       ((read)  ,place)
       ((write) (setf ,place value)))))

(let* ((x 3)
       (xp (make-pointer x)))
  (funcall xp 'write 5)             ; write a new value to x
  (list (funcall xp 'read)          ; read the value from x through xp
        x))                         ; read the value from x directly
;=> (5 5)

在这段代码中,value返回一个可以用一个或两个参数调用的函数第一个参数应该是一个符号,或者是make-pointer或者read,第二个参数应该在第一个参数是write时提供,它是一个新的存储值当用write调用时,返回位置的值当使用read调用时,将存储并返回一个新值。
不过,这里的多重评估存在一些问题例如,如果要执行以下操作,请记住write返回值(print 2)
(make-pointer (aref some-array (print 2)))

每次使用指针读或写时,都会打印2,这可能是不需要的我不知道这个问题是否需要解决,但请继续阅读一些可能的方法来避免这个问题。
在对一个类似的问题(How to mutate global variable passed to and mutated inside function?)进行了一些研究之后,值得注意的是lisp机器(运行lisp机器lisp,而不是普通lisp)有一个更像c指针的概念,叫做locatives,在Common Lisp, reference to value and actual value的答案中简要提到。一旦你知道要搜索的词,你就很容易找到更多关于位置词的信息,包括Lisp机器手册中的Chapter 13. Locatives和常见Lisp的各种重新实现,包括Alan Crowe's,它以一个很长的注释开始,结尾是一个(有希望的)简明的总结:
;;; 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.

Crowe接着展示了如何使用2来创建函数,这些函数记住如何访问位置并将值存储到位置,而不必每次都计算get-setf-expansion这段代码当然值得一读!

关于lisp - 如何在Lisp中的函数内全局更改变量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19491275/

相关文章:

function - lisp函数的实现

lisp - 为什么 setq 削减了我的列表

common-lisp - 使用点符号访问 CLOS 槽

c++ - 扩展 Lua : check number of parameters passed to a function

在普通的 lisp 中排序子列表

functional-programming - 读SICP的前置条件是什么?

c++ - 如何在 C++ 中编写一个函数,该函数采用可变数量的 double vector ?

java - 如何在 Java 中创建一个接受任意数量任意类型参数的方法?

Emacs :TODO indicator at left side

macros - lisp 中的宏行为问题