我在静态/动态范围界定方面遇到以下问题:
以下程序片段是用允许全局的编程语言编写的 变量并且不允许函数的嵌套声明。
global int i = 100, j = 5;
void P(x) {
int i = 10;
print(x + 10);
i = 200;
j = 20;
print (x);
}
main() {P(i + j);}
Q1. If the programming language uses static scoping and call by need parameter passing mechanism, the values printed by the above program are
(A) 115, 220 (B) 25, 220 (C) 25, 15 (D) 115, 105
Q2. If the programming language uses dynamic scoping and call by name parameter passing mechanism, the values printed by the above program are
(A) 115, 220 (B) 25, 220 (C) 25, 15 (D) 115, 105
我的想法:
关于问题 1:由于它是静态范围,并且根据需要调用,x 应该替换为 i + j。但它会导致局部名称冲突,因为已经有一个名称为 i 的变量。所以它(全局 i)可能会被重命名,比如说 i1,然后调用将是:
first call: print(x+10) -> (i1 + j + 10) -> (100 + 5 + 10) -> 115
second call: print(x) -> print(i1 + j) -> 105 (Already evaluated - call by need)
关于问题 2:在动态作用域中,您首先在局部函数中搜索变量,然后在调用局部函数的函数中搜索,然后在调用该函数的函数中搜索,依此类推,向上调用堆栈。
根据名字调用:
print (i1 + j + 10) -> print (100 + 5 +10 ) -> 115
第二次调用将是
print(x) -> print(i1 + j) -> (100 + 20) = 120 // Evaluate again - Call be name.
这个答案正确吗? (不存在于选项中) 有什么我想念的吗? (可能是动态绑定(bind)?)
最佳答案
Q1
OP 的答案是正确的 (D)。实际上,由于P
执行过程中没有修改全局i
,所以call by need和call by 没有区别值(value)。
这是一个示例,它确实有所作为:
global int i = 100, j = 5;
void IncreaseTheGlobal() {
i = i + 1; // static scoping means this is the GLOBAL i!
print(i);
}
void P(x) {
int i = 10;
IncreaseTheGlobal(); // 101 (increased global i)
print(i); // 10 (local i)
print(x); // 106 (x is evaluated; picks up increased global i)
IncreaseTheGlobal(); // 102 (2nd time increased global i)
print(x); // 106 (x not re-evaluated; unaffected by 2nd increase)
}
main() {
print(i); // 100 (original global i)
P(i + j);
print(i); // 102 (new global i)
}
正如 OP 已经指出的那样,第一次评估 x
时,它会选择全局 i
在该特定时刻具有的任何值。在初始评估之后,x
不再受稍后对全局 i
的修改的影响。
Q2
按名称调用 通常用于宏语言。那么为什么不使用有史以来最著名的宏语言:C 预处理器呢?
#include <stdio.h>
int i = 100, j = 5;
#define print(num) printf("%d\n", num)
#define P(x) { \
int i = 10; \
print(x + 10); \
i = 200; \
j = 20; \
print(x); \
}
main() {
P(i + j);
}
编译运行,看到:25、220。
通过名称调用可以使用简单的搜索和替换;在 P
的正文中,用 i + j
替换每次出现的 x
。
int i = 10;
print(i + j + 10); // 10 + 5 + 10 = 25
i = 200;
j = 20;
print(i + j); // 200 + 20 = 220
换句话说,i + j
中的 i
和 j
只是获取范围内的任何值的现值当评估 x
时。
所以正确答案是B,对吧?好吧,差不多……正确答案取决于 print
的实现。
假设 print
也采用按名称调用,和 print
定义了自己的局部变量 i
,那么这将极大地改变结果。试试这个:
#define print(num) { int i = 0; printf("%d\n", num); }
结果现在变为 15、20。
这可能是动态作用域不利于代码可维护性的最重要原因。函数 print
中的实现更改(即使是更改局部变量名称这样微不足道的事情)也可能会破坏更高级别的函数。
关于使用动态范围按名称调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14725225/