如果我们定义一个类似的函数
(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
.
谢谢!
要绘制与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中没有类似于特殊变量的东西.
最后一点关于setq
和setf
.这里的事情有点棘手,因为你需要了解较低级别的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具有未命名的函数,但仍然无法捕获变量并在范围内存活(它们可以复制到未命名函数的局部变量,但这不是一回事).