Ответ 1
С статическим (лексическим) охватом структура исходного кода программы определяет, на какие переменные вы ссылаетесь. При динамическом охвате состояние выполнения стека программы определяет, к какой переменной вы обращаетесь. Вероятно, это очень незнакомая концепция, поскольку в основе каждого используемого сегодня языка программирования (кроме, возможно, emacs lisp) используется лексическая область, которая, как правило, намного проще для людей и инструментов анализа, чтобы рассуждать.
Рассмотрим эту более простую примерную программу (написанную в синтаксисе псевдокода):
program a() {
x: integer; // "x1" in discussions below
x = 1;
procedure b() {
x = 2; // <-- which "x" do we write to?
}
procedure c() {
x: integer; // "x2" in discussions below
b();
}
c();
print x;
}
Программа и компилятор ссылаются на обе переменные как x
, но я обозначил их x1
и x2
, чтобы упростить обсуждение ниже.
С лексическим охватом мы определяем во время компиляции, которое x
мы имеем в виду на основе статической лексической структуры исходного кода программы. Самое внутреннее определение x
в области, когда , определяющее b
, x1
, и поэтому рассматриваемая запись разрешается до x1
и что там, где x = 2
записывается, поэтому мы печатаем 2
при запуске этой программы.
С динамическим охватом мы имеем стек определений переменных, отслеживаемых во время выполнения, поэтому x
, который мы пишем, зависит от того, что именно находится в области видимости и динамически определено в время выполнения. Начиная с запуска a
нажимает x => x1
на стек, вызывая c
нажимает x => x2
на стек, а затем, когда мы переходим к b
, вершина стека x => x2
, и поэтому мы пишем в x2
. Это оставляет x1
нетронутым, и поэтому мы печатаем 1
в конце программы.
Кроме того, рассмотрим эту немного другую программу:
program a() {
x: integer; // "x1" in discussions below
x = 1;
procedure b() {
x = 2; // <-- which "x" do we write to?
}
procedure c() {
x: integer; // "x2" in discussions below
b();
}
c();
b();
}
Примечание b
вызывается дважды - первый раз через c
, второй раз напрямую. При использовании лексического охвата объяснение выше не изменяется, и мы пишем в x1
оба раза. Тем не менее, с динамическим охватом, это зависит от того, как x
привязан во время выполнения. В первый раз, когда мы называем b
, мы пишем в x2
, как объяснялось выше, но во второй раз пишем в x1
, так как это то, что сверху стека! (x => x2
появляется, когда возвращается c
.)
Итак, вот ваш код профессора, аннотированный тем, что используется точная переменная, на которой записывается лексическая область. Записи, заканчивающиеся в конце программы, помечены знаком *
:
program A()
{
x, y, z: integer; // x1, y1, z1
procedure B()
{
y: integer; // y2
y=0; // y2 = 0
x=z+1; // x1 = z1 + 1 = 12 + 1 = 13*
z=y+2; // z1 = y2 + 2 = 0 + 2 = 2*
}
procedure C()
{
z: integer; // z2
procedure D()
{
x: integer; // x2
x = z + 1; // x2 = z2 + 1 = 5 + 1 = 6
y = x + 1; // y1 = x2 + 1 = 6 + 1 = 7*
call B();
}
z = 5; // z2 = 5
call D();
}
x = 10; // x1 = 10
y = 11; // y1 = 11
z = 12; // z1 = 12
call C();
print x, y, z; // x1, y1, z1
}
И здесь это с динамическим охватом. Обратите внимание, что изменения только находятся в b
и в расположении тегов *
:
program A()
{
x, y, z: integer; // x1, y1, z1
procedure B()
{
y: integer; // y2
y=0; // y2 = 0
x=z+1; // x2 = z2 + 1 = 5 + 1 = 6
z=y+2; // z2 = y2 + 2 = 0 + 2 = 2
}
procedure C()
{
z: integer; // z2
procedure D()
{
x: integer; // x2
x = z + 1; // x2 = z2 + 1 = 5 + 1 = 6
y = x + 1; // y1 = x2 + 1 = 6 + 1 = 7*
call B();
}
z = 5; // z2 = 5
call D();
}
x = 10; // x1 = 10*
y = 11; // y1 = 11
z = 12; // z1 = 12*
call C();
print x, y, z;
}