我是一个完整的Lisp n00b,所以请保持温和。
我无法理解CL关于[un-]声明的自由变量的想法。我想是的:
(defun test ()
(setq foo 17)
)
将定义一个声明变量foo并将其设置为17的函数。但是,我得到了
;Compiler warnings :
; In TEST: Undeclared free variable FOO
我的实际案例有点大;我的代码(代码段)看起来像这样:
(defun p8 ()
;;; [some other stuff, snip]
(loop for x from 0 to (- (length str) str-len) do
(setq last (+ x str-len)) ; get the last char of substring
(setq subs (subseq str x last)) ; get the substring
(setq prod (prod-string subs)) ; get the product of that substring
(if (> prod max) ; if it's bigger than current max, save it
(setq max prod)
(setq max-str subs)
)
)
;;; [More stuff, snip]
)
这让我:
;Compiler warnings for "/path/to/Lisp/projectEuler/p6-10.lisp":
; In P8: Undeclared free variable LAST (2 references)
;Compiler warnings for "/Volumes/TwoBig/AllYourBits-Olie/WasOnDownBelowTheOcean/zIncoming/Lisp/projectEuler/p6-10.lisp" :
; In P8: Undeclared free variable PROD (3 references)
;Compiler warnings for "/Volumes/TwoBig/AllYourBits-Olie/WasOnDownBelowTheOcean/zIncoming/Lisp/projectEuler/p6-10.lisp" :
; In P8: Undeclared free variable SUBS (3 references)
;Compiler warnings for "/Volumes/TwoBig/AllYourBits-Olie/WasOnDownBelowTheOcean/zIncoming/Lisp/projectEuler/p6-10.lisp" :
; In P8: Undeclared free variable =
是的,是的,我意识到我使用了太多的中间变量,但是我试图理解发生了什么,然后我太过于喜欢将所有内容压缩到最小类型字符,这似乎很受欢迎CL世界。
所以,无论如何......有人可以解释以下内容:
(...)
语句的封闭setq
之外,所述变量的范围是什么? (也就是说,我希望var对(... (setq ...) ...)
以外的setq
parens 1级的所有内容都有效且范围广,不是吗?注意:我非常擅长使用C,Java,Javascript,Obj-C和相关的过程语言。我觉得功能编程是不同的。现在,我只是在努力解决语法问题。
谢谢!
P.S。如果重要,defun p8
位于文本文件(TextMate)中,我在Clozure CL
上运行它。但是,希望这些都不重要!
答案 0 :(得分:9)
在lisp中,可以使用defparameter
或defvar
声明变量。
(defparameter var1 5)
(defvar var2 42)
这会产生全局(动态)变量。
defvar
和defparameter
之间的区别在于defvar
不会重新初始化已存在的变量。
使用let
或let*
(按顺序初始化变量)例如引入了本地(词汇)变量。
未声明的自由变量表示您已使用(此处为setq
- ed)一个变量,该变量未在其使用的上下文中绑定。然后可以为您声明它,但随后可能作为全局(动态)变量。这样做的结果是,如果在多个函数中使用具有相同名称的未声明变量,则将在所有函数中引用相同的变量。
您的代码可以这样写:
(loop for x from 0 to (- (length str) str-len) do
(let* ((last (+ x str-len)) ; get the last char of substring
(subs (subseq str x last)) ; get the substring
(prod (prod-string subs))) ; get the product of that substring
(if (> prod max) ; if it's bigger than current max, save it
(setq max prod)
(setq max-str subs))))
使用循环的变量绑定属性,它也可以写为
(loop for x from 0 to (- (length str) str-len)
for last = (+ x str-len)
for subs = (subseq str x last)
for prod = (prod-string subs)
when (> prod max) do
(setq max prod)
(setq max-str subs))
答案 1 :(得分:5)
在Lisp中,变量声明可以以多种方式执行。最值得注意的是:
defparameter
和defvar
let
,let*
,multiple-value-bind
,destructuring-bind
和其他约束表单声明局部变量您也可以在很多地方了解它们的范围,例如CLtL2。
setq
/ setf
不是变量声明运算符,而是变量修饰运算符,如其名称所暗示的那样。
PS。在交互模式下,如果您尝试设置未声明的变量,则某些实现将使用DWIM方法并在后台将变量声明为特殊变量,但这纯粹是为了方便。
答案 2 :(得分:3)
Common Lisp HyperSpec(基本上是HTML格式的Common Lisp标准)说:
http://www.lispworks.com/documentation/HyperSpec/Body/s_setq.htm
为变量分配值。
所以SETQ
只为变量赋值。它没有声明它们。
变量定义通过DEFVAR
,DEFPARAMETER
,...
(defparameter *this-is-a-global-dynamic-variable* 'yep)
变量定义在本地使用DEFUN
,LET
,LET*
,LOOP
以及许多其他定义完成。
(defun foo (v1 v2)
...)
(let ((v1 10)
(v2 20))
...)
(loop for v1 in '(10 30 10 20)
do ...)
这是基本的Lisp,阅读介绍会很有用。我建议:
http://www.cs.cmu.edu/~dst/LispBook/
以上书籍可免费下载。
此外,上面提到的Common Lisp Hyperspec为您提供了Common Lisp的定义,并详细描述了各种工具(DEFUN,LOOP,DEFPARAMETER,...)。