LISP本地/全局变量赋值

Sid*_*Sid 9 lisp common-lisp

如果我们定义一个类似的函数

(defun foo(x)
  (setf x somevalue))
Run Code Online (Sandbox Code Playgroud)

x定义为局部变量还是全局变量?使用setf/q将值设置为全局.如果它是全局的,任何人都可以告诉我如何在lisp中定义局部变量而不是let

谢谢!

请考虑以下示例

(let ((x 0)) 
  (defun foo (y) 
    (when (equal x 0) (setq x y)) 
    (when (< x y) (setq x y))
    x))
Run Code Online (Sandbox Code Playgroud)

当我给一些输入到foo喜欢(foo 2),它返回2,如果我们再次执行功能与(foo 1)它仍返回2和(foo 3)返回3,本是问题是我真正想要什么do.But这怎么可能,因为如果我尝试x从clisp终端访问函数外部的变量,我无法.如果我再次访问该函数,它似乎保留了以前的值x.

谢谢!

650*_*502 9

要绘制与C,C++,Java或Python等其他语言的并行,你的代码更改的是一个"局部变量",即使这不是Lisper会使用的措辞(Lisp用语中的措辞是本地的"绑定") ).

您可以使用函数参数创建局部变量,例如您的示例,或使用一些标准形式,如:

  • (let ((x 12)) ...)
  • (do ((x 0 (1+ i))) ...)
  • (dotimes (x 10) ...)
  • (loop for x from 0 to 10 do ...)

另一方面,在您的实现中,可能使用参数创建所有局部变量,而其他形式只是宏扩展到那些.例如:

(let ((x 10)) ...)
Run Code Online (Sandbox Code Playgroud)

相当于

(funcall (lambda (x) ...) 10)
Run Code Online (Sandbox Code Playgroud)

还要注意,确实在读取代码片段时,它可能x在某种意义上是一个"全局变量",因为它可能被声明为特殊:

(defvar x 12)
;; ==> x

(defun bar ()
  (format t "The value of x is ~a" x))
;; ==> bar

(defun foo (x)
  (bar))
;; ==> foo

(foo 42)
The value of x is 42
;; ==> NIL

x
;; ==> 12
Run Code Online (Sandbox Code Playgroud)

如果使用(defvar ...)它声明变量"special" 将以不同的方式处理:就像每次将它用作参数一样,或者在(let ..)表单中使用它,代码将使用新提供的值保存当前值,然后退出函数后恢复该值或let.

所以那些变量都是"全局的"(因为外部函数可以看到它们)而且也是本地的(因为在你的函数或者终止之后,将恢复之前的值).

标准惯例是使用"耳罩"命名特殊变量,即在名称的开头和结尾都带有星号,如:

(defvar *x* 12)
Run Code Online (Sandbox Code Playgroud)

这有助于谁读取您的代码以了解该变量是特殊的.请注意,这不是语言强制要求,任何名称都可以用于特殊变量.

在C,C++,Java或Python中没有类似于特殊变量的东西.

最后一点关于setqsetf.这里的事情有点棘手,因为你需要了解较低级别的Lisp以了解为什么setq需要它.如果您使用的是Common Lisp,那么您应该忘记setq并始终使用它setf.

setf是一个宏,它将setq在需要时扩展(但也setq可以setf在需要时更改为(符号宏),这就是新手可能会感到困惑的地方).

你的最后一个例子是"封闭"的情况.当您定义一个函数(使用(lambda ...)表单命名或未命名)时,该函数可以"捕获"可见的变量并在以后使用它们.通常显示的一个更简单的情况是"加法器":

(defun adder (x)
  (lambda (y) (incf x y)))
Run Code Online (Sandbox Code Playgroud)

此函数返回一个函数,该函数将继续将传递的值添加到内部累加器:

(let ((f (adder 10)))
  (print (funcall f 3))
  (print (funcall f 9))
  (print (funcall f 11)))
Run Code Online (Sandbox Code Playgroud)

输出将是13(10 + 3),22(13 + 9)和33(22 + 11).

匿名函数"捕获"了局部变量x,即使在退出adder函数后也可以使用它.在C,C++或Java等语言中,当您退出定义变量的作用域时,局部变量无法生存.

C++ 11具有未命名的函数,但仍然无法捕获变量并在范围内存活(它们可以复制到未命名函数的局部变量,但这不是一回事).