词法作用域绑定何时发生——在运行时还是编译时?

When does lexical scoping binding take place - in runtime or compile time?

C 语言在编译期间进行范围绑定(变量引用获得固定地址 - 根本不改变),即[=27 的示例=]静态作用域.

Elisp 语言在 运行 时间内进行范围绑定(变量指向自己的个人引用堆栈的顶部,let/defun/...特殊形式在离开时从顶部添加到堆栈顶部 - 在那个时候捕获修改),这是动态范围[=39的示例=].

词法范围中使用什么类型的绑定?

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

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

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

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

这样的东西

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

环境

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

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

我将使用方案来说明这一点:

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

这里的过程值bar定义为一个过程。变量 y 在过程主体中再次空闲。但是封闭环境是由 let 形式创建的,其中 y 被定义为 100。因此,当 bar 被调用时,它是 y 的值被获取并不是顶级(在动态范围的语言中它会返回 2)。

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

SICP, Chapter 3

中对环境进行了很好的解释

其他评论

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

Common Lisp 对局部变量使用词法作用域(由 let 形式引入),对全局变量使用动态作用域(它们也称为 special;可以声明一个特殊的局部变量,但很少使用)用 defvardefparameter 定义。为了区别于词法范围的变量,它们的名称通常带有"ear muffs",例如*standard-input*。顶层函数在 CL 中也很特殊,这可能是相当危险的:一个人可以通过隐藏顶层函数在不知不觉中改变行为。这就是为什么 CL 标准指定锁定标准库函数以防止它们重新定义的原因。

相比之下,Scheme 总是使用词法范围。然而,动态范围有时很有用 (Richard Stallman makes a good point on it)。为了克服这个问题,许多 Scheme 实现引入了所谓的 parameters(使用词法作用域实现)。

Common Lisp、Scheme、Clojure 等语言 Python 保持对变量的动态引用:您可以从字符串构造变量名(intern a symbol用 Lisp 的术语)并找到它的价值。更多的静态语言,如 C、OCaml 或 Haskell,无法做到这一点(除非使用某种形式的反射)。但这与他们使用的范围界定的关系很弱。