setq vs Undeclared自由变量(常见的lisp)

时间:2013-07-13 01:27:36

标签: common-lisp

我是一个完整的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世界。

所以,无论如何......有人可以解释以下内容:

  • 在什么条件下Lisp“声明”变量?
  • 除了围绕(...)语句的封闭setq之外,所述变量的范围是什么? (也就是说,我希望var对(... (setq ...) ...)以外的setq parens 1级的所有内容都有效且范围广,不是吗?
  • 我是否错误地解释了未声明的自由变量消息?
  • 你想要提供的任何其他提示都可以帮助我更好地理解正在发生的事情。

注意:我非常擅长使用C,Java,Javascript,Obj-C和相关的过程语言。我觉得功能编程是不同的。现在,我只是在努力解决语法问题。

谢谢!

P.S。如果重要,defun p8位于文本文件(TextMate)中,我在Clozure CL上运行它。但是,希望这些都不重要!

3 个答案:

答案 0 :(得分:9)

在lisp中,可以使用defparameterdefvar声明变量。

(defparameter var1 5)
(defvar var2 42)

这会产生全局(动态)变量。

defvardefparameter之间的区别在于defvar不会重新初始化已存在的变量。

使用letlet*(按顺序初始化变量)例如引入了本地(词汇)变量。

未声明的自由变量表示您已使用(此处为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中,变量声明可以以多种方式执行。最值得注意的是:

  • 使用defparameterdefvar
  • 声明全局(它们被恰当地称为特殊)变量
  • 使用letlet*multiple-value-binddestructuring-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只为变量赋值。它没有声明它们。

变量定义通过DEFVARDEFPARAMETER,...

在全球范围内完成
(defparameter *this-is-a-global-dynamic-variable* 'yep)

变量定义在本地使用DEFUNLETLET*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,...)。