gpt4 book ai didi

lisp - 似乎只有lisp允许使用变量而不先定义它?

转载 作者:太空宇宙 更新时间:2023-11-03 18:55:50 24 4
gpt4 key购买 nike

我发现似乎只有在lisp中才能定义:

(lambda (x)(+ x y 1))

在其他语言中,即使是函数式语言,变量也必须先定义,然后才能使用
所以在lisp中,有“自由变量”、“绑定变量”的概念,其他语言有这个概念吗?因为所有变量必须首先定义,所以所有变量都是“绑定变量”?变量在全局或其他范围内有一个初始值?
我认为lambda的概念很早,但是如果任何变量或值必须先定义然后才能使用,那么理解和使用它不是更容易吗?
谢谢!

最佳答案

我认为这个问题有两个部分在Lisp(和其他语言)中存在一个自由变量访问的一般问题,这是一个很大的主题,因为Lisp有很多很多,甚至更多的其他语言,包括一些变量的动态作用域(CL)和只有动态作用域的语言(elisp直到最近,还有许多其他的旧实现)还有一个问题是变量引用何时需要在Lisps(和其他语言)中得到解决,特别是向前引用的需要以及何时和如何得到解决。。
在这个回答中,我只谈到了第二部分,关于远期参考,我认为这是直接让你困惑的地方我将给出使用Racket和r5rs语言的例子,因为这是您在评论中使用的。
首先,任何支持递归而不跳过极限环(即使用Y组合)的语言都需要支持对尚未绑定的名称的引用,这些名称称为前向引用考虑一下:

#lang r5rs

(define foo
(lambda (x)
(if (null? x)
0
(+ 1 (foo (cdr x))))))

好的,所以当对函数求值时,有一个对递归调用 foo的引用但是 foo还没有绑定,因为 foo将绑定到函数本身,这就是我们定义的如果你真的这样做:
(let ((r (lambda (x)
(if (null? x)
0
(+ 1 (r (cdr x)))))))
r)

这是一个错误,因为 r确实尚未定义相反,您需要使用 letrec来执行适当的魔法,以确保工作正常。
好吧,你可能会说 letrec变成了这样:
(let ((r #f))
(set! r (lambda (x)
(if (null? x)
0
(+ 1 (r (cdr x))))))
r)

也许是的但是这个呢?
#lang r5rs

(define foo
(lambda (x)
(if (null? x)
0
(bar x))))

(define bar
(lambda (x)
(+ 1 (foo (cdr x)))))

还有更详细的节目。
所以这里有一个关于前向参照的一般问题基本上不可能以这样的方式编写程序:在程序的文本中,没有对尚未知道的名称的引用注意,在上面的程序中, foo指的是 barbar指的是 foo,因此, foobar的定义没有顺序,它们不涉及对源代码中尚未绑定的名称的引用。
注意,这个问题本质上影响了所有编程语言:这并不是Lisp族语言所独有的。
那么,这是怎么解决的呢好吧,答案是“这取决于你关心的具体语言是如何定义的”我不完全确定R5RS规范对此有何规定,但我想我肯定Racket对此有何规定。
racket所说的是,转发引用都需要在模块级进行排序。所以如果我有这样一个球拍源文件:
#lang r5rs

(define a
(lambda (x) (+ x y)))

(define y 1)

这很好,因为在模块的末尾都定义了 ax,所以 a的定义很好这与上述 foobar的定义没有区别,请注意,这样的源文件:
#lang r5rs

(define foo
(lambda (x)
(if (null? x)
0
(bar x))))

不合法: bar未绑定:
$ racket ts.rkt
ts.rkt:7:9: bar: unbound identifier
[...]

因此,向前参照问题的答案有两个:
用几乎任何实用语言编写程序,特别是用Lisps编写程序时,都需要向前引用尚未绑定到引用点的名称;
语言规范需要定义何时以及如何解析这种转发引用。
我想这就是让你困惑的地方尤其是一个球拍模块,其中仅包含:
#lang r5rs

(define a (lambda (x) (+ x y)))

在球拍上是不合法的但是这个模块:
#lang r5rs

(define a (lambda (x) (+ x y)))
(define y 1)

是合法的,因为 y是在解决转发引用时绑定的。
普通的口齿不清要比这复杂得多,原因有二:
它是一个Lisp-2,所以函数和变量绑定存在于不同的名称空间中;
没有标准的TopPrEVE词汇变量绑定。
例如,如果我们采取这样的方式(假设是在顶层,没有其他用户定义在此之前):
(defun foo (x)
(+ x y))

那么 y不能是词法变量的引用,因为 foo的词法环境是空的,因为没有TopPrEVE词汇变量绑定。
因此 y必须是对动态变量(CL speak中的特殊变量)的引用但事实上,CL通过声明不允许这种引用来解决动态变量的前向引用问题所以上面的定义是不合法的。许多编译器会在编译时发出警告,但他们不必这样做。
但是,下面的变量很好(注意,我对动态变量使用了CL约定)。
(defvar *y* 1)

(defun foo (x)
(+ x *y*))

这很好,因为在定义 *y*之前就知道 foo
事实上我撒了谎:向前引用动态变量是允许的,但是你必须告诉语言关于它们:
(defun foo (x)
(declare (special *y*))
(+ x *y*))

现在,如果有一个稍后的 (defvar *y* ...)(或等效的)调用 foo就可以了如果没有这样一个全球性的特别声明 *y*,那么我认为如果调用 foo,会发生什么,要么是未定义的,要么是一个错误(我希望是后者,但我不确定)。
最后,即使没有针对 *y*的全球特别公告,这也是可以的:
(let ((*y* 3))
(declare (special *y*))
(foo 1))

这很好,因为 *y*在本地声明为special(它有一个绑定声明)。
在CL中允许对函数的前向引用,并且对于何时需要解析函数有复杂的规则,我不确定我是否记得。

关于lisp - 似乎只有lisp允许使用变量而不先定义它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56847587/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com