使用setf,defvar,let和scope分配变量

4 variables scope common-lisp

所以,我
在lisp中读过setq和defvar,
http://www.cs.ucf.edu/courses/cop4020/spr2006/plsetup.html,
在In Lisp中,如何解决"警告:假设特殊?"
关于setf和defvar之间差异的其他地方.所以我决定尝试一下这个想法:

CL-USER> (defun foo ()
       (setf x 10)
       (print x))

; in: DEFUN FOO
;     (SETF X 10)
; ==>
;   (SETQ X 10)
; 
; caught WARNING:
;   undefined variable: X
; 
; compilation unit finished
;   Undefined variable:
;     X
;   caught 1 WARNING condition
FOO
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {10040F1543}>.
CL-USER> (foo)

10 
10
CL-USER> x
10
Run Code Online (Sandbox Code Playgroud)

好吧,我知道setf应该用来改变现有变量的值,但是未定义的变量警告似乎在SBCL中得到了很好的处理(尽管我已经读过不同的CL实现可能会以不同方式处理它,因此它不是最好的事情).

输入第二个测试:

CL-USER> (defun bar ()
       (defvar y 15)
       (print y))

; in: DEFUN BAR
;     (PRINT Y)
; 
; caught WARNING:
;   undefined variable: Y
; 
; compilation unit finished
;   Undefined variable:
;     Y
;   caught 1 WARNING condition
BAR
CL-USER> y
; Evaluation aborted on #<UNBOUND-VARIABLE Y {10045033D3}>.
CL-USER> (bar)

15 
15
CL-USER> y
15
Run Code Online (Sandbox Code Playgroud)

根据链接,我将setf更改为defvar,我认为应该创建并一次绑定变量.现在我的未定义变量警告被推入(打印y)行......这里发生了什么?

作为第二个问题,我期望在函数之外的任何变量的值在函数之外是不可访问的,如Python中的情况:

>>> def foo():
...     x = 10
...     print x
... 
>>> foo()
10
>>> x
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Run Code Online (Sandbox Code Playgroud)

我猜这与常见的lisp处理范围的方式有关,即defvar创建了一个"全局特殊变量"...所以我最后一次尝试(让...)

CL-USER>(defun baz()(let((z 10))(print z))(incf z 10)(print z))

; in: DEFUN BAZ
;     (INCF Z 10)
; --> LET* 
; ==>
;   (SETQ Z #:NEW0)
; 
; caught WARNING:
;   undefined variable: Z
; 
; compilation unit finished
;   Undefined variable:
;     Z
;   caught 1 WARNING condition
Run Code Online (Sandbox Code Playgroud)

在阅读了defvar,defparameter,setf和setq之间的区别之后,这个似乎工作正常:

CL-USER> (defun apple ()
       (defparameter x 10)
       (print 10))

APPLE
CL-USER> x
; Evaluation aborted on #<UNBOUND-VARIABLE X {1004436993}>.
CL-USER> (apple)

10 
10
CL-USER> x
10
Run Code Online (Sandbox Code Playgroud)

只是重申一下我的问题:1)setf,defvar和let真正发生了什么?
2)有没有办法让常见的lisp来扩展函数内部的变量,如python示例中所示?

Rai*_*wig 5

回答2) DEFVAR定义一个变量.但它还没有被执行.所以编译器不知道print表单中的变量- 编译DEFUN表单时..它也在一个内部DEFUN.因此它不在顶层.作为顶层形式,编译器会识别DEFVAR并注意到这y是一个全局特殊变量.

只是重申一下我的问题:1)setf,defvar和let真正发生了什么?2)有没有办法让常见的lisp来扩展函数内部的变量,如python示例中所示?

1)SETF设置变量值,但不定义它.如果该变量未定义,那么Common Lisp标准并没有真正说明会发生什么.大多数Common Lisp实现都会做一些有用的事情.通常它会被执行,好像变量已被声明为特殊(因此您也会收到警告).

DEFVAR用作顶级表单(通常不在函数内部)来定义全局特殊变量.由于DEFVAR声明变量名称是特殊的,因此编写一个带有星星的变量是一个非常有用的约定:*y*而不仅仅是y.

LET 定义局部变量的范围.

2)Common Lisp函数有参数列表来引入变量.除此之外,他们没有定义变量范围.如果要在函数内引入局部变量,请使用LET.

>>> def foo():
...     x = 10
...     print x
Run Code Online (Sandbox Code Playgroud)

(defun foo ()
  (let ((x 10))
    (print x)))
Run Code Online (Sandbox Code Playgroud)

同样:函数不提供变量的范围,这样在函数内部分配变量会自动将其定义为函数本地.请LET改用.

还要注意的LET语法糖,主要是:(let ((a 1) (b 2)) (+ a b))基本上也是这样做的((lambda (a b) (+ a b)) 1 2).它只是一个简单的函数应用程序,以不同的方式编写,以便为人类读者提供改进.

Common Lisp中还支持较旧的语法:

(defun foo (&aux (x 10))
  (print x))
Run Code Online (Sandbox Code Playgroud)

上面定义了一个局部变量X,就像一个本地变量LET.