Common Lisp:有什么方法可以避免 defvar 或 defparameter?
Common Lisp: Any way to avoid defvar or defparameter?
我正在使用 SBCL 2.0.1.debian 和 Paul Graham 的 ANSI Common Lisp 来学习 Lisp。
虽然在第 2 章,我意识到我不能像作者那样使用 setf
!稍微谷歌一下,我了解到我必须使用 defvar
或 defparameter
来 'introduce' 我的全局变量,然后才能使用 setq
!
设置它们
有什么方法可以避免通过 defvar
或 defparameter
从 SBCL 内部或通过开关从外部引入全局变量?其他 Lisp 也强制执行此操作吗?
我理解它们在大型代码库中的增值,但现在我只是通过编写小程序来学习,所以我发现它们很麻烦。我习惯在其他语言中使用全局变量,所以不必介意全局/局部变量错误。
如果您正在编写程序,在某些持久存在于文件的意义上,则使用 def*
形式。未定义变量的顶级 setf
/ setq
在 CL 中具有未定义的语义,更糟糕的是,在不同的实现中具有不同的语义。输入 defvar
、defparameter
或 defconstant
并不比输入 setf
或 setq
难多少,这意味着您的程序将定义语义,这通常被认为是一个很好的事物。所以不,对于程序来说,没有办法避免使用 def*
形式,或类似的形式。
如果你只是简单地在 REPL / listener 中输入东西来玩东西,那么我认为只在顶层使用 setf
就可以了(没有人使用在 REPL 中输入东西的环境我觉得真的很执着)。
你说你习惯于在其他语言中使用全局变量。根据其他语言的不同,这很可能意味着您 不 习惯了 CL 的语义,以使用 def*
形式定义的绑定,这不仅是全局的,而且 全局特殊,或全局动态。我不知道还有哪些其他语言甚至有 CL 的特殊/词汇区别,但我怀疑没有那么多。例如考虑这个 Python (3) 程序:
x = 1
def foo():
x = 0
print(x)
def bar():
nonlocal x
x += 1
return x
return bar
y = foo()
print(y())
print(y())
print(x)
如果你运行这将打印
0
1
2
1
因此请考虑 'equivalent' CL 程序(注意:永远不要使用这样命名的变量编写程序):
(defvar x 1)
(defun foo ()
(let ((x 0))
(print x)
(lambda ()
(incf x))))
(let ((y (foo)))
(print (funcall y))
(print (funcall y))
(print x)
(values))
如果你运行这将打印
0
2
3
3
我认为你可以同意与 Python 程序打印的内容有很大不同。
那是因为 CL 中的顶级变量是特殊的或动态变量:在上面的 CL 程序中 x
是动态作用域的,因为 x
已被 [=23= 声明为全局特殊变量],这意味着调用 foo
返回的函数正在改变 x
.
的全局值
Python 根本没有动态绑定(但您可以编写 Python 模块,将它们模拟为访问显式绑定堆栈的函数,但有一点曲折)。类似的事情也是其他语言公开动态绑定的方式。例如,Racket 是这样做的:
(define d (make-parameter 1))
(define (foo)
(parameterize ([d 0])
(display (d))
(λ ()
(d (+ (d) 1))
(d))))
(let ([y (foo)])
(display (y))
(display (y))
(display (d)))
将打印
0
2
3
3
虽然这个程序
(define x 1)
(define (foo)
(let ([x 0])
(displayln x)
(λ ()
(set! x (+ x 1))
x)))
(let ([y (foo)])
(displayln (y))
(displayln (y))
(displayln x))
将打印
0
1
2
1
就像 Python 一样。
如果不是因为 CL 的兼容性要求,这也许是 CL 应该做的(这显然是个人意见,我对 CL 的做法很满意去做吧)。
CL 根本没有顶级(全局)词法 绑定(但您可以编写一个 CL 程序,使用符号宏以非常令人信服的方式模拟它们).如果您想要这样的东西,我怀疑互联网上已经有一些东西,或者我可以继续整理我的实现。作为使用这种东西的程序示例:
(defglex x 1)
(defun foo ()
(let ((x 0))
(print x)
(lambda ()
(incf x))))
(let ((y (foo)))
(print (funcall y))
(print (funcall y))
(print x))
将打印
0
1
2
1
CL 具有词法和动态(特殊)非全局绑定。
注意:用 def*
形式定义的东西是 globally 特殊/动态的,这意味着 all 它们的绑定是动态的,这就是为什么你应该始终区分这些变量的名称,使用 *
约定:(defvar *my-var* ...)
和 never (defvar my-var ...)
. (不过 (defconstant my-constant ...)
没问题。)
我正在使用 SBCL 2.0.1.debian 和 Paul Graham 的 ANSI Common Lisp 来学习 Lisp。
虽然在第 2 章,我意识到我不能像作者那样使用 setf
!稍微谷歌一下,我了解到我必须使用 defvar
或 defparameter
来 'introduce' 我的全局变量,然后才能使用 setq
!
有什么方法可以避免通过 defvar
或 defparameter
从 SBCL 内部或通过开关从外部引入全局变量?其他 Lisp 也强制执行此操作吗?
我理解它们在大型代码库中的增值,但现在我只是通过编写小程序来学习,所以我发现它们很麻烦。我习惯在其他语言中使用全局变量,所以不必介意全局/局部变量错误。
如果您正在编写程序,在某些持久存在于文件的意义上,则使用 def*
形式。未定义变量的顶级 setf
/ setq
在 CL 中具有未定义的语义,更糟糕的是,在不同的实现中具有不同的语义。输入 defvar
、defparameter
或 defconstant
并不比输入 setf
或 setq
难多少,这意味着您的程序将定义语义,这通常被认为是一个很好的事物。所以不,对于程序来说,没有办法避免使用 def*
形式,或类似的形式。
如果你只是简单地在 REPL / listener 中输入东西来玩东西,那么我认为只在顶层使用 setf
就可以了(没有人使用在 REPL 中输入东西的环境我觉得真的很执着)。
你说你习惯于在其他语言中使用全局变量。根据其他语言的不同,这很可能意味着您 不 习惯了 CL 的语义,以使用 def*
形式定义的绑定,这不仅是全局的,而且 全局特殊,或全局动态。我不知道还有哪些其他语言甚至有 CL 的特殊/词汇区别,但我怀疑没有那么多。例如考虑这个 Python (3) 程序:
x = 1
def foo():
x = 0
print(x)
def bar():
nonlocal x
x += 1
return x
return bar
y = foo()
print(y())
print(y())
print(x)
如果你运行这将打印
0
1
2
1
因此请考虑 'equivalent' CL 程序(注意:永远不要使用这样命名的变量编写程序):
(defvar x 1)
(defun foo ()
(let ((x 0))
(print x)
(lambda ()
(incf x))))
(let ((y (foo)))
(print (funcall y))
(print (funcall y))
(print x)
(values))
如果你运行这将打印
0
2
3
3
我认为你可以同意与 Python 程序打印的内容有很大不同。
那是因为 CL 中的顶级变量是特殊的或动态变量:在上面的 CL 程序中 x
是动态作用域的,因为 x
已被 [=23= 声明为全局特殊变量],这意味着调用 foo
返回的函数正在改变 x
.
Python 根本没有动态绑定(但您可以编写 Python 模块,将它们模拟为访问显式绑定堆栈的函数,但有一点曲折)。类似的事情也是其他语言公开动态绑定的方式。例如,Racket 是这样做的:
(define d (make-parameter 1))
(define (foo)
(parameterize ([d 0])
(display (d))
(λ ()
(d (+ (d) 1))
(d))))
(let ([y (foo)])
(display (y))
(display (y))
(display (d)))
将打印
0
2
3
3
虽然这个程序
(define x 1)
(define (foo)
(let ([x 0])
(displayln x)
(λ ()
(set! x (+ x 1))
x)))
(let ([y (foo)])
(displayln (y))
(displayln (y))
(displayln x))
将打印
0
1
2
1
就像 Python 一样。
如果不是因为 CL 的兼容性要求,这也许是 CL 应该做的(这显然是个人意见,我对 CL 的做法很满意去做吧)。
CL 根本没有顶级(全局)词法 绑定(但您可以编写一个 CL 程序,使用符号宏以非常令人信服的方式模拟它们).如果您想要这样的东西,我怀疑互联网上已经有一些东西,或者我可以继续整理我的实现。作为使用这种东西的程序示例:
(defglex x 1)
(defun foo ()
(let ((x 0))
(print x)
(lambda ()
(incf x))))
(let ((y (foo)))
(print (funcall y))
(print (funcall y))
(print x))
将打印
0
1
2
1
CL 具有词法和动态(特殊)非全局绑定。
注意:用 def*
形式定义的东西是 globally 特殊/动态的,这意味着 all 它们的绑定是动态的,这就是为什么你应该始终区分这些变量的名称,使用 *
约定:(defvar *my-var* ...)
和 never (defvar my-var ...)
. (不过 (defconstant my-constant ...)
没问题。)