具有深/浅绑定的动态/静态范围(练习)
我正在研究具有深/浅绑定的动态/静态作用域,并手动运行代码,以了解这些不同的作用域/绑定实际上是如何工作的。我阅读了理论并用谷歌搜索了一些示例练习,我发现的练习非常简单(例如 这个对于动态作用域非常有帮助)但是我很难理解静态作用域是如何工作的。
在这里,我发布了一个练习,以检查我是否获得了正确的解决方案:
考虑以下用伪代码编写的程序:
int u = 42;
int v = 69;
int w = 17;
proc add( z:int )
u := v + u + z
proc bar( fun:proc )
int u := w;
fun(v)
proc foo( x:int, w:int )
int v := x;
bar(add)
main
foo(u,13)
print(u)
end;
打印到屏幕上的内容是什么
使用静态作用域 a) ?答案=180
b) 使用动态作用域和深度绑定?答案= 69(u的总和= 126,但它是foo的本地v,对吗?)
c)使用动态作用域和浅绑定?答案= 69(u = 101的总和,但它是foo的本地v,对吧?)
PS:我正在尝试练习做一些这样的练习,如果你知道我在哪里可以找到这些类型的问题(最好有解决方案),请给出链接,谢谢!
I'm studying dynamic/static scope with deep/shallow binding and running code manually to see how these different scopes/bindings actually work. I read the theory and googled some example exercises and the ones I found are very simple (like this one which was very helpful with dynamic scoping) But I'm having trouble understanding how static scope works.
Here I post an exercise I did to check if I got the right solution:
considering the following program written in pseudocode:
int u = 42;
int v = 69;
int w = 17;
proc add( z:int )
u := v + u + z
proc bar( fun:proc )
int u := w;
fun(v)
proc foo( x:int, w:int )
int v := x;
bar(add)
main
foo(u,13)
print(u)
end;
What is printed to screen
a) using static scope? answer=180
b) using dynamic scope and deep binding? answer=69 (sum for u = 126 but it's foo's local v, right?)
c) using dynamic scope and shallow binding? answer=69 (sum for u = 101 but it's foo's local v, right?)
PS: I'm trying to practice doing some exercises like this if you know where I can find these types of problems (preferable with solutions) please give the link, thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您对词法(静态)范围的回答是正确的。您对动态范围的回答是错误的,但如果我正确地阅读了您的解释,那是因为您在
u
和v
之间感到困惑,而不是因为对深度和浅层绑定的工作原理。 (我假设您的u
/v
混淆只是偶然的,而不是由于对foo< 的调用中的值与引用的奇怪混淆/代码>。)
正确。
您的括号解释是正确的,但您的答案是错误的:
u
确实设置为126
,并且foo
确实本地化了v
,但由于main
打印u
,而不是v
,答案是126
。u
的总和实际上是97
(42+13+42
code>),但由于bar
本地化了u
,所以答案是42
。 (您的括号解释对此是错误的 - 您似乎在解释语句int u := w 时使用了全局变量
在w
,即17
bar
的定义中,但该语句实际上引用了foo
的局部变量w
,它的第二个参数是13
但这实际上并不影响答案。 v。)对于词法作用域,通过将伪代码翻译成具有词法作用域的语言来检查您的答案非常容易。同样具有浅绑定的动态作用域。 (事实上,如果您使用 Perl,您几乎可以同时测试这两种方式,因为它支持这两种方式;只需使用
my
作为词法范围,然后执行查找和替换将其更改为 <但是,即使您使用 JavaScript 作为词法作用域,使用 Bash 作为动态作用域,测试两者也应该很快。)具有深度绑定的动态作用域要棘手得多,因为很少有广泛的应用。 - 部署的语言支持它。如果您使用 Perl,则可以通过使用从变量名映射到标量引用的哈希(关联数组)并将此哈希从一个函数传递到另一个函数来手动实现它。在伪代码声明局部变量的任何地方,您都将现有的标量引用保存在 Perl 词法变量中,然后将新的映射放入哈希中;在函数结束时,您恢复原始标量引用。为了支持绑定,您可以创建一个包装函数来创建哈希的副本,并将该传递给其包装函数。这是使用该方法在 Perl 中动态范围、深度绑定的程序实现:
实际上它打印了
126
。这显然很混乱且容易出错,但它也确实可以帮助您了解正在发生的事情,因此出于教育目的,我认为这是值得的!Your answer for lexical (static) scope is correct. Your answers for dynamic scope are wrong, but if I'm reading your explanations right, it's because you got confused between
u
andv
, rather than because of any real misunderstanding about how deep and shallow binding work. (I'm assuming that youru
/v
confusion was just accidental, and not due to a strange confusion about values vs. references in the call tofoo
.)Correct.
Your parenthetical explanation is right, but your answer is wrong:
u
is indeed set to126
, andfoo
indeed localizesv
, but sincemain
printsu
, notv
, the answer is126
.The sum for
u
is actually97
(42+13+42
), but sincebar
localizesu
, the answer is42
. (Your parenthetical explanation is wrong for this one — you seem to have used the global variablew
, which is17
, in interpreting the statementint u := w
in the definition ofbar
; but that statement actually refers tofoo
's local variablew
, its second parameter, which is13
. But that doesn't actually affect the answer. Your answer is wrong for this one only becausemain
printsu
, notv
.)For lexical scope, it's pretty easy to check your answers by translating the pseudo-code into a language with lexical scope. Likewise dynamic scope with shallow binding. (In fact, if you use Perl, you can test both ways almost at once, since it supports both; just use
my
for lexical scope, then do a find-and-replace to change it tolocal
for dynamic scope. But even if you use, say, JavaScript for lexical scope and Bash for dynamic scope, it should be quick to test both.)Dynamic scope with deep binding is much trickier, since few widely-deployed languages support it. If you use Perl, you can implement it manually by using a hash (an associative array) that maps from variable-names to scalar-refs, and passing this hash from function to function. Everywhere that the pseudocode declares a local variable, you save the existing scalar-reference in a Perl lexical variable, then put the new mapping in the hash; and at the end of the function, you restore the original scalar-reference. To support the binding, you create a wrapper function that creates a copy of the hash, and passes that to its wrapped function. Here is a dynamically-scoped, deeply-binding implementation of your program in Perl, using that approach:
and indeed it prints
126
. It's obviously messy and error-prone, but it also really helps you understand what's going on, so for educational purposes I think it's worth it!简单绑定和深度绑定是 Lisp 解释器对伪代码的看法。作用域只是指针算术。如果没有自由变量,动态作用域和静态作用域是相同的。
静态作用域依赖于指向内存的指针。空旷的环境没有任何价值联想的象征;用“结束”一词表示。每次解释器读取一个赋值时,它都会为符号和值之间的关联腾出空间。
环境指针更新为指向最后构建的关联。
让我将此环境内存位置记录为AAA。在我的 Lisp 解释器中,当遇到一个过程时,我们将环境指针放在口袋里。
这几乎就是调用过程
add
之前的全部内容。有趣的是,如果add
从未被调用,那么您只会花费一个指针。假设程序调用
add(8)
。好吧,让我们滚吧。环境 AAA 已设为当前环境。环境是->[w,17]->[v,69]->[u,42]->End
。add
的过程参数被添加到环境的前面。环境变为[z,8]->[w,17]->[v,69]->[u,42]->End
。现在
add
的过程体被执行。自由变量v
的值为69。自由变量u
的值为42。z
的值为 8。u
将被赋值为 69 + 42 + 8,变为 119。环境将反映这一点:
[z,8]->[w,17]->[v,69]->[u,119]->End
。假设过程
add
已完成其任务。现在环境已恢复到以前的值。请注意过程
add
如何产生更改自由变量u
值的副作用。惊人的!关于动态作用域:它只是确保闭包忽略动态符号,从而避免被捕获并变得动态。
然后将动态赋值放在代码顶部。如果动态与参数名称相同,它将被传入的参数值屏蔽。
假设我有一个名为
z
的动态变量。当我调用add(8)
时,无论我想要什么,z
都会被设置为 8。这可能就是动态变量名称更长的原因。有传言说动态变量对于回溯、使用 let Lisp 构造等事情很有用。
Simple and deep binding are Lisp interpreter viewpoints of the pseudocode. Scoping is just pointer arithmetic. Dynamic scope and static scope are the same if there are no free variables.
Static scope relies on a pointer to memory. Empty environments hold no symbol to value associations; denoted by word "End." Each time the interpreter reads an assignment, it makes space for association between a symbol and value.
The environment pointer is updated to point to the last association constructed.
Let me record this environment memory location as AAA. In my Lisp interpreter, when meeting a procedure, we take the environment pointer and put it our pocket.
That's pretty much all there is until the procedure
add
is called. Interestingly, ifadd
is never called, you just cost yourself a pointer.Suppose the program calls
add(8)
. OK, let's roll. The environment AAA is made current. Environment is->[w,17]->[v,69]->[u,42]->End
.Procedure parameters of
add
are added to the front of the environment. The environment becomes[z,8]->[w,17]->[v,69]->[u,42]->End
.Now the procedure body of
add
is executed. Free variablev
will have value 69. Free variableu
will have value 42.z
will have the value 8.u
will be assigned the value of 69 + 42 + 8 becomeing 119.The environment will reflect this:
[z,8]->[w,17]->[v,69]->[u,119]->End
.Assume procedure
add
has completed its task. Now the environment gets restored to its previous value.Notice how the procedure
add
has had a side effect of changing the value of free variableu
. Awesome!Regarding dynamic scoping: it just ensures closure leaves out dynamic symbols, thereby avoiding being captured and becoming dynamic.
Then put assignment to dynamic at top of code. If dynamic is same as parameter name, it gets masked by parameter value passed in.
Suppose I had a dynamic variable called
z
. When I calledadd(8)
,z
would have been set to 8 regardless of what I wanted. That's probably why dynamic variables have longer names.Rumour has it that dynamic variables are useful for things like backtracking, using let Lisp constructs.
静态绑定,也称为词法作用域,指的是大多数现代语言中的作用域机制。
在“词法范围”中,u 的最终值既不是 180 也不是 119,这是错误的答案。
正确答案是u=101。
请参阅下面的标准 Perl 代码以了解原因。
关于浅层绑定与深度绑定,这两种机制都可以追溯到以前的LISP时代。
这两种机制都是为了实现动态绑定(相对于词法作用域绑定),因此它们产生相同的结果!
浅层绑定和深层绑定之间的区别不在于语义(它们是相同的),而是在于动态绑定的实现。
使用深度绑定,变量绑定在堆栈中设置为“varname => varvalue”对。
请参阅下面的代码,了解深度绑定动态作用域的 Perl 实现。
结果是 u=97
然而,这种对绑定堆栈的不断遍历是昂贵的:0(n) 复杂性!
浅绑定与之前的实现相比,带来了美妙的O(1)增强性能!
浅绑定通过为每个变量分配自己的“单元格”来改进前一种机制,并将变量的值存储在单元格内。
单元格(使用变量名称的哈希表,我们实现了
0(1) 访问变量值的复杂性!)
变量的单元格。
将变量(先前的绑定)放入堆栈,并存储
值单元格中的新本地值。
从堆栈中移出到变量的值单元格中。
请参阅下面的代码,了解浅绑定动态作用域的简单 Perl 实现。
正如您将看到的,结果仍然是u=97
作为结论,请记住两件事:
浅绑定产生与深度绑定相同的结果< /em>,但运行速度更快,因为永远不需要搜索绑定。
问题不在于浅层绑定与深层绑定与
静态绑定但是词法作用域与动态作用域(通过深度或浅层绑定实现)。
Static binding, also known as lexical scope, refers to the scoping mechanism found in most modern languages.
In "lexical scope", the final value for u is neither 180 or 119, which are wrong answers.
The correct answer is u=101.
Please see standard Perl code below to understand why.
Regarding shallow binding versus deep binding, both mechanisms date from the former LISP era.
Both mechanisms are meant to achieve dynamic binding (versus lexical scope binding) and therefore they produce identical results !
The differences between shallow binding and deep binding do not reside in semantics, which are identical, but in the implementation of dynamic binding.
With deep binding, variable bindings are set within a stack as "varname => varvalue" pairs.
Please see the the code below for a Perl implementation of deep-binding dynamic scope.
The result is u=97
Nevertheless, this constant traversal of the binding stack is costly : 0(n) complexity !
Shallow binding brings a wonderful O(1) enhanced performance over the previous implementation !
Shallow binding is improving the former mechanism by assigning each variable its own "cell", storing the value of the variable within the cell.
cell (using a hash table on variable names, we achieve a
0(1) complexity for accessing variable's values!)
variable's cell.
of the variable (a previous binding) onto the stack, and storing the
new local value in the value cell.
off the stack into the variable's value cell.
Please see the the code below for a trivial Perl implementation of shallow-binding dynamic scope.
As you shall see, the result is still u=97
As a conclusion, remember two things :
shallow binding produces the same results as deep binding, but runs faster, since there is never a need to search for a binding.
The problem is not shallow binding versus deep binding versus
static binding BUT lexical scope versus dynamic scope (implemented either with deep or shallow binding).