10

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

(defun foo(x)
  (setf x somevalue))

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))

当我给foolike一些输入时(foo 2),它返回 2,如果我们再次执行该函数,(foo 1)它仍然返回 2 并(foo 3)返回 3。这就是我真正想要的。但问题是这怎么可能,因为如果我尝试x从 clisp 终端访问函数外部的变量,我无法。如果我再次访问该函数,它似乎保留了以前的值x

谢谢你!

4

3 回答 3

10

为了与其他语言(如 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 ...),则处理方式将有所不同:就像每次将其用作参数或以某种(let ..)形式使用它时,代码将使用新提供的值保存当前值,然后退出函数后恢复值或let.

因此,这些变量既是“全局的”(因为外部函数可以看到它们),也是局部的(因为在您的函数或 let 终止后,先前的值将被恢复)。

标准约定是用“earmuffs”命名特殊变量,即在名称的开头和结尾都用星号,如:

(defvar *x* 12)

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

没有什么类似于 C、C++、Java 或 Python 中的特殊变量。

最后一点关于setqsetf。这里的事情有点棘手,因为您需要了解较低级别的 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 有未命名的函数,但仍然无法捕获变量并在作用域内存活(可以将它们复制到未命名函数的局部变量中,但这不是一回事)。

于 2012-09-08T19:58:20.293 回答
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被替换为 a defun(这是 a lambda)。

于 2012-09-08T19:14:36.497 回答
0

在您的代码x中是f函数的参数,因此是函数的本地参数。调用setf不会创建新变量,而只是设置现有局部变量的值x。使用setq而不是setf行为相同。

于 2012-09-08T19:03:19.227 回答