词法作用域绑定何时发生——在运行时还是编译时?
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。 顶级 环境是所有常用变量、函数(+
、-
、sin
、map
等)和语法的地方(与可以扩展语法的语言相关,如 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 解释器),这正是语言开发人员正在做的事情。
中对环境进行了很好的解释
其他评论
AFAIK,来自 Emacs 23,ELisp 使用词法范围和动态范围(类似于 Common Lisp,见下文)。
Common Lisp 对局部变量使用词法作用域(由 let
形式引入),对全局变量使用动态作用域(它们也称为 special;可以声明一个特殊的局部变量,但很少使用)用 defvar
和 defparameter
定义。为了区别于词法范围的变量,它们的名称通常带有"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,无法做到这一点(除非使用某种形式的反射)。但这与他们使用的范围界定的关系很弱。
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。 顶级 环境是所有常用变量、函数(+
、-
、sin
、map
等)和语法的地方(与可以扩展语法的语言相关,如 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 解释器),这正是语言开发人员正在做的事情。
中对环境进行了很好的解释其他评论
AFAIK,来自 Emacs 23,ELisp 使用词法范围和动态范围(类似于 Common Lisp,见下文)。
Common Lisp 对局部变量使用词法作用域(由 let
形式引入),对全局变量使用动态作用域(它们也称为 special;可以声明一个特殊的局部变量,但很少使用)用 defvar
和 defparameter
定义。为了区别于词法范围的变量,它们的名称通常带有"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,无法做到这一点(除非使用某种形式的反射)。但这与他们使用的范围界定的关系很弱。