1

C 语言在编译时采用范围绑定(变量引用获得固定地址 - 根本不改变),即静态范围的示例。

Elisp 语言在运行时进行范围绑定(变量指向自己的个人参考堆栈的顶部,let// defun... 特殊形式在离开时从顶部添加到堆栈顶部 - 那时捕获已修改),即示例的动态范围

词法作用域中使用什么类型的绑定?

Common Lisp、Python、R、JavaScript 等语言声明它们实现了词法作用域。

这些语言的实现中使用了哪些技术?

我听说带有功能外观的环境。如果我是对的 - 环境是什么时候创建的?

是否可以或通常由开发人员手动构建和绑定环境以发挥作用?就像是call( bind_env(make_env({buf: [...], len: 5}), myfunc) )

4

1 回答 1

4

简而言之,词法作用域发生在编译时(或者更准确地说,在评估函数定义时)。此外,词法作用域可以是静态作用域:这就是 ML 语言(SML、OCaml、Haskell)的做法。

环境

每个功能都在某个环境中定义。此外,每个函数都会创建自己的本地环境,嵌套在封闭环境中。顶级环境是定义所有常用变量、函数(+-sinmap等)和语法(与可以扩展语法的语言相关的语言,如 Common Lisp、Scheme、Clojure)的地方。

每个函数创建自己的本地环境,嵌套在封闭环境中(例如,顶层或其他函数)。函数参数和所有局部变量都存在于这个环境中。如果函数引用了未在本地环境中定义的变量或函数(在此环境中称为free),则可以在函数定义的封闭环境中找到它(如果在封闭环境的封闭环境中找不到,则在封闭环境中更高等)上)。这与动态范围不同,其中值将在调用函数的环境中找到。

我将使用 Scheme 来说明这一点:

y在这个定义中是免费的

(define (foo x)
    (+ x y))

这里是y在顶层环境中定义的

(define y 1)

引入本地'y',但foo将使用y来自定义的封闭(顶级)环境。因此,结果是 2 而不是 11。

(let ((y 10))
     (foo 1))
 => 2

您还可以定义一个函数(或 Scheme 术语中的一个过程),其中包含一个本地环境:

 (define bar
     (let ((y 100))
          (lambda (x) (+ x y))))

(bar 1)
=> 101

这里的procedure valuebar被定义为一个procedure。变量y在过程体中又是自由的。但是封闭环境是由定义为 100的let表单创建的。因此,当被调用时,它是获取的值而不是顶级值(在动态范围的语言中,它会返回 2)。ybary

回答您的最后一个问题,可以手动创建自己的环境,但工作量太大,可能效率不高。当实现语言(例如,Scheme 解释器)时,这正是语言开发人员正在做的事情。

SICP,第 3 章中显示了对环境的良好解释

其他的建议

AFAIK,来自 Emacs 23,ELisp 使用词法作用域和动态作用域(类似于 Common Lisp,见下文)。

Common Lisp 对局部变量使用词法作用域(由let表单引入)和对全局变量使用动态作用域(它们也称为special;可以声明一个局部变量 special,但很少使用)用defvarand定义defparameter。为了将它们与词法范围的变量区分开来,它们的名称通常带有“耳罩”,例如*standard-input*. 顶级函数在 CL 中也很特殊,这可能相当危险:人们可以通过隐藏顶级函数在不知不觉中改变行为。这就是为什么 CL 标准在标准库函数上指定锁以防止它们重新定义的原因。

相反,Scheme 总是使用词法作用域。然而,动态范围有时很有用(Richard Stallman 对此提出了很好的观点)。为了克服这个问题,许多 Scheme 实现引入了所谓的参数(使用词法作用域实现)。

Common Lisp、Scheme、Clojure、Python 等语言保持对变量的动态引用:您可以从字符串(在 Lisp 的术语中是一个符号)构造变量名并找到它的值。更多的静态语言,如 C、OCaml 或 Haskell,无法做到这一点(除非使用某种形式的反射)。但这与他们使用什么样的范围关系不大。

于 2016-02-15T08:28:53.347 回答