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

Oli*_*lie 5 common-lisp

我是一个完整的Lisp n00b,所以请保持温柔.

我无法理解CL关于[un-]声明的自由变量的想法.我认为:

(defun test ()
    (setq foo 17)
)
Run Code Online (Sandbox Code Playgroud)

将定义一个声明变量foo并将其设置为17的函数.但是,我得到了

;Compiler warnings :
;  In TEST: Undeclared free variable FOO
Run Code Online (Sandbox Code Playgroud)

我的实际案例有点大; 我的代码(代码段)看起来像这样:

(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]
)
Run Code Online (Sandbox Code Playgroud)

这给了我:

;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 =
Run Code Online (Sandbox Code Playgroud)

是的,是的,我意识到我正在使用太多的中间变量,但是我试图理解在我过于喜欢将所有内容压缩到最小类型字符之前发生了什么,这在CL世界中很流行.

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

  • 在什么条件下Lisp"声明"变量?
  • 所谓变量的范围除了(...)围绕setq声明吗?!(也就是说,我希望var对于从(... (setq ...) ...)parens 1级以外的所有内容都有效并且范围有限setq,不是吗?
  • 我是否错误地解释了未声明的自由变量消息?
  • 你关心的任何其他提示都可以帮助我更好地理解正在发生的事情.

注意:我非常擅长使用C,Java,Javascript,Obj-C和相关的过程语言.我觉得功能编程是不同的.现在,我只是在咀嚼语法.

谢谢!

PS如果重要,defun p8是在文本文件(TextMate)中,我正在运行它Clozure CL.但是,希望这一切都不重要!

Ter*_* D. 9

在lisp中,变量可以使用defparameter或声明defvar.

(defparameter var1 5)
(defvar var2 42)
Run Code Online (Sandbox Code Playgroud)

这导致全局(动态)变量.

defvar和之间的区别defparameterdefvar不重新初始化已存在的变量.

引入局部(词汇)变量,例如使用letlet*(顺序初始化变量).

未声明的自由变量意味着您已使用(此处setq)变量,而该变量未在其使用的上下文中绑定.然后可以为您声明它,但随后可能作为全局(动态)变量.这样做的结果是,如果在多个函数中使用具有相同名称的未声明变量,则将在所有函数中引用相同的变量.

您的代码可以这样写:

(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))))
Run Code Online (Sandbox Code Playgroud)

使用循环的变量绑定属性,它也可以写成

(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))
Run Code Online (Sandbox Code Playgroud)


Vse*_*kin 5

在Lisp中,变量声明可以以多种方式执行.最值得注意的是:

  • 声明全局(它们正确称为特殊变量)与defparameterdefvar
  • 声明局部变量有let,let*,multiple-value-bind,destructuring-bind,和其他形式的结合
  • 作为函数参数

您也可以在许多地方了解它们的范围,例如在CLtL2中.

setq/ setf不是变量声明运算符,而是变量修饰运算符,如其名称所暗示的那样.

PS.在交互模式下,如果您尝试设置未声明的变量,某些实现将使用DWIM方法并在后台将变量声明为特殊变量,但这纯粹是为了方便起见.