scope - Lisp函数和/或let语句中的函数和变量的范围

标签 scope lisp common-lisp

当在函数调用中定义变量和函数时,我无法理解它们的作用域我试着搜索这个范围,但找不到合适的答案(或者可能是在搜索错误的东西),所以我决定自己编写一些函数来测试:

(defun test-scope1 ()
  (setf myvar 1)
  (defun set-var1 ()
    (setf myvar 2))
  (set-var1))

有了这个函数,我只想看看是否有全局设置我希望myvar和set var是全局定义的,因为这里没有范围如所料,在调用(test-scope1)之前,命令myvar(set-var)会给出错误信息在调用(test-scope1)之后,我可以在解释器中运行myvar(set-var),得到2。
我想封装我的变量会更好,于是决定在函数中添加一个let,从而得到下一个测试:
(defun test-scope2 ()
  (let ((myvar 10))
    (defun set-var2 ()
      (setf myvar 20))
    (set-var2)))

我希望myvar被困在let块的范围内,但无法猜测set-var2它可能被困在let块中,也可能被全局定义运行(test-scope2)后,我尝试访问myvar并获取2这意味着这个新函数有自己的myvar,因为它仍然是前一个函数的2我试着跑(set-var2)得到20分。
函数在let块中运行后被全局定义,我并不完全感到惊讶,但现在我非常困惑myvar变量的访问因为它没有改变我的myvar的全局副本,所以它似乎仍然引用了一些浮动的变量。
现在我想看看我是否可以操作这个浮动变量,所以我创建了第三个函数。
(defun test-scope3 ()
  (let ((myvar (if (ignore-errors myvar)
                   myvar
                   100)))
    (defun set-var3 ()
      (setf myvar (+ myvar 100)))
    (set-var3)))

我不想仅仅将变量设置为固定值,而是希望在前一个值的基础上增加它我在这里检查了两件事第一个是调用test-scope3时我想看看是否可以提取myvar的“前一个值”,因为如果它在某个地方浮动,也许我可以再次访问它这可能不是好的做法,但这不是重点忽略错误是存在的,以防以前没有真正的值浮动,在这种情况下,我选择默认值100。
我测试的第二件事是让set-var3在前面的myvar值上加100。我想看看这是否会调整浮动变量,或者是否是静态的我不知道我的函数“应该”返回什么。
在运行(test-scope3)之后,我完全惊讶地看到102显然,我的test-scope3从运行test-scope1中找到了myvar的值但是,在检查了解释器中myvar的值之后,它仍然是2。接下来,我运行(set-var3)并得到返回值202。好吧,所以它又增加了100倍再次调用它将返回302,依此类推但是,再次调用(test-scope3)会将该值重置回102。
我又编写了一个作为双嵌套let命令的函数我只是用两种方式运行:let参数没有myvar定义。此函数返回10002然后,我尝试将本地myvar设置为50,得到1050的返回。
(defun test-scope4 ()
  (let () ; or ((myvar 50))
    (let ((myvar (if (ignore-errors myvar)
                      myvar
                      2000)))
      (defun set-var4 ()
        (setf myvar (+ myvar 1000)))
    (set-var4))))

综上所述,以下是我的一些具体问题:
当我在另一个defun中执行defun时,即使是在let块中,为什么该函数可以全局访问?
当我调用set var#时,访问的是什么变量(或在什么范围内)或者更恰当地说,当我在另一个语句中定义一个函数时,我实际绑定的是什么?
当我在函数中使用变量myvar时,它从何而来从我的第四个例子推测,它在当前作用域中查找符号myvar,然后检查一个更高的级别,直到找到它的值。
对不起,如果一切都很罗嗦,我的问题格式不好我尽力调查事情事实上,这一切都引出了我真正的问题,在写下所有我意识到的事情之后,我可能已经超出了这个问题的范围(没有双关语的意图),因为我已经建立了这个问题。
上面给出的隐藏内部函数的问题可以用lambda表达式来处理;但是,我真正想做的是在一个更大的块中有一个递归函数,它使用块来存储值,而不直接将它们输入到函数中据我所知,这在lambda表达式中是不可能的。例如,考虑下面的函数。
(defun outer-function (start)
  (let ((x start))
    (defun increment-to-ten ()
      (setf x (+ x 1))
      (if (< x 10)
          (increment-to-ten)))
    (increment-to-ten)
    (print x)))

它可以用如下参数递归实现
(defun increment-to-ten-recursive (x)
  (if (< x 10)
      (increment-to-ten-recursive (+ x 1))
      10))

如果有解决办法的话,那就太好了,或者如果我的想法完全错了,有更好的办法来解决,那就太好了。为您提供一个块存储数据,然后调用一个没有参数的递归函数来处理该数据似乎很方便。

最佳答案

为什么很难讨论你看到的效果:
您所做的事情在Common Lisp中是未定义的:在test-scope1myvar中设置未声明的变量从那时起就不清楚下面的代码是如何运行的。
未声明的变量
当设置了未声明的变量foo时,未定义它有什么影响实现允许它有些人会发出警告SBCL公司:

* (setf foo 10)
; in: SETF FOO
;     (SETF FOO 10)
; ==>
;   (SETQ FOO 10)
; 
; caught WARNING:
;   undefined variable: FOO
; 
; compilation unit finished
;   Undefined variable:
;     FOO
;   caught 1 WARNING condition

全局变量用DEFPARAMETERDEFVAR定义局部变量由LETLET*和函数参数定义。由于DEFPARAMETERDEFVAR定义了全局特殊(使用动态绑定)变量,因此通常将它们写成*some-variable*-请注意其周围的*,这是符号名的一部分,而不是特殊语法这是一个特殊变量的约定。
嵌套defuns
嵌套DEFUNs不用于普通LispDEFUN是一个顶级表单,用于设置全局函数要定义本地函数,请使用FLETLABELS您可以嵌套DEFUN表单,但它的样式不好,而且在实际的Lisp代码中找不到它不要嵌套DEFUN表单。注意:这与Scheme不同,Scheme可以嵌套DEFINE表单由于没有使用nestedDEFUNs,因此讨论其效果没有什么意义将示例转换为使用由FLETLABELS定义的本地函数。
当我在另一个defun中执行defun时,即使是在let块中,为什么该函数可以全局访问?
因为DEFUN定义了全局函数这就是它的目的。
您需要重写示例,以便能够讨论效果:
声明所有变量
不要使用嵌套的
我们可以尝试理解您当前示例中的代码,但这在很大程度上取决于实现和操作序列我们要么不在可移植的公共Lisp领域,要么做一些在公共Lisp中没有实际用途的事情(嵌套DEFUNs)。

关于scope - Lisp函数和/或let语句中的函数和变量的范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26231194/

相关文章:

javascript - 为什么初始化多个变量会导致范围泄漏?

javascript - JQuery AJAX 和 OOP JS 作用域问题

javascript - Node.js 中的变量作用域

JavaScript 将作用域传递给另一个函数

list - 在 Lisp 中使用 AND

lisp - "UIOP"包在 SBCL 中不存在,有人可以帮我找到吗?

loops - 在 (LOOP FOR A ...) 的宏扩展期间出错

lisp - 难以理解 Lisp 中的嵌套表达式

macros - Common Lisp 反引号/反引号 : How to Use?

common-lisp - 如何加载和使用quicklisp程序