所以,我从中读过
setq and defvar in lisp,
http://www.cs.ucf.edu/courses/cop4020/spr2006/plsetup.html和
In Lisp, how do I fix "Warning: Assumed Special?"
关于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
好的,我知道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
根据链接,我将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
我猜这与普通的lisp处理范围的方式有关,即defvar创建了一个“全局特殊变量”...所以我最后一次尝试(让...)
CL-使用者&gt; (defun baz() (让((z 10))(打印z)) (incf z 10) (打印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
阅读What's difference between defvar, defparameter, setf and 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
重申我的问题:
1)setf,defvar和let真正发生了什么?
2)有没有办法让常见的lisp来扩展函数内的变量,就像在python示例中一样?
答案 0 :(得分: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
时
(defun foo ()
(let ((x 10))
(print x)))
同样:函数不提供变量的范围,这样在函数内部分配变量会自动将其定义为函数本地。请改用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))
上面定义了一个局部变量X
,就像LET
一样。
答案 1 :(得分:4)
我会简短地说:
1)setf,defvar和let真正发生了什么?
defvar
和defparameter
用于声明和设置全局特殊变量。它们仅在名称已绑定时才有所不同。
setf
用于赋值(特殊变量,词汇变量和其他 - 可能是自定义 - 可设置的位置。)
let
(和let*
)创建在其正文中可见的新变量绑定。它可以创建词法或特殊绑定,具体取决于(全局或本地)声明。如果没有特殊声明,则会创建词法绑定。
2)有没有办法让常见的lisp来扩展a中的变量 函数如python示例?
将代码放在let
的正文中,可以看到绑定:
CL-USER> (defun baz ()
(let ((z 10))
(print z)
(incf z 10) ; i.e. (setf z (+ z 10))
(print z)))
BAZ
CL-USER> (baz)
10
20
20
CL-USER> z
; Evaluation aborted on #<UNBOUND-VARIABLE #x186C611E>.