如果我们定义类似
的函数(defun foo(x)
(setf x somevalue))
x
被定义为局部变量还是全局变量?使用setf / q将值设置为全局。
如果它是全局的,任何人都可以告诉我如何在除let
之外的lisp中定义局部变量吗?
谢谢!
考虑以下示例
(let ((x 0))
(defun foo (y)
(when (equal x 0) (setq x y))
(when (< x y) (setq x y))
x))
当我向foo
(foo 2)
提供一些输入时,它返回2,如果我们再次使用(foo 1)
执行该功能,它仍会返回2并且(foo 3)
返回3.这就是我真正想要它做的。但问题是这怎么可能,因为如果我尝试从clisp终端访问函数外的变量x
,我无法。如果我正在访问该函数再次出现,它似乎保留了x
的先前值。
答案 0 :(得分:8)
要绘制与其他语言(如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)) ...)
相当于
(funcall (lambda (x) ...) 10)
另请注意,确实在读取代码片段时,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
如果使用(defvar ...)
声明变量“special”,它将以不同的方式处理:就像每次将它用作参数一样,或者以(let ..)
形式使用它时代码将执行的操作保存当前值,使用新提供的值,然后在退出函数或let
后恢复该值。
所以这些变量都是“全局的”(因为外部函数可以看到它们)而且也是本地的(因为在你的函数之后或让终止之前的值将被恢复)。
标准惯例是使用“耳罩”命名特殊变量,即在名称的开头和结尾都带有星号,如:
(defvar *x* 12)
这有助于谁读取您的代码以了解该变量是特殊的。请注意,这不是语言强制要求,任何名称都可以用于特殊变量。
与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)))
此函数返回一个函数,该函数将继续将传递的值添加到内部累加器:
(let ((f (adder 10)))
(print (funcall f 3))
(print (funcall f 9))
(print (funcall f 11)))
输出为13(10 + 3),22(13 + 9)和33(22 + 11)。
匿名函数“捕获”了局部变量x
,即使退出adder
函数也可以使用它。在C,C ++或Java等语言中,当您退出定义变量的范围时,局部变量无法生存。
C ++ 11具有未命名的函数,但仍然无法捕获变量并在范围内存活(它们可以复制到未命名函数的局部变量,但这不是一回事)。
答案 1 :(得分:4)
实际上,x
是函数的本地,因此setf
或setq
只会更改局部变量x
。
要回答问题的第二部分,还有另一种方法来定义除let之外的局部变量:
(funcall (lambda (var1 var2...)
... use var1 var2 ...)-
val1 val2 ...)
事实上,defun
可以实现为
`(setf (symbol-function ,name) (lambda ,args ,@body))
虽然我检查过的所有实现都做得更多。
此外,symbol-function
地方允许您这样做:
(setf (symbol-function '+) (function -))
虽然这通常是一个坏主意。
正在发生的事情是x
是包含defun
的范围的本地。它是不全局变量。你正在做什么defun
正在创建一个闭包,它“捕获”周围范围内的所有词法范围(不是用defvar
/ defparameter
)变量创建并保持他们为了未来。如果要检查x
的值,请添加另一个函数
(defun get-x () x)
在let
内。换句话说,x
是函数所在的let
的本地。您提供的情况类似于:
(let ((x 3))
(print x)
(let ((y 7))
(print (list x y))
(setf x y))
(print x))
除了内部let
被defun
(lambda
)替换。
答案 2 :(得分:0)
在您的代码中x
是f
函数的参数,因此是函数的本地参数。对setf
的调用不会创建新变量,而只是设置现有局部变量x
的值。使用setq
代替setf
会表现相同。