Подтвердить что ты не робот

Статический (лексический) охват и динамическое масштабирование (псевдокод)

Program A()
{
    x, y, z: integer;

    procedure B()
    {
        y: integer;
        y=0;
        x=z+1;
        z=y+2;
    }

    procedure C()
    {
        z: integer;

        procedure D()
        {
            x: integer;
            x = z + 1;
            y = x + 1;
            call B();
        }

        z = 5;
        call D();
    }

    x = 10;
    y = 11;
    z = 12;
    call C();
    print x, y, z;
}

По моему мнению, результатом этой программы при использовании статического охвата является: x = 13, y = 7 и z = 2.

Однако, когда он запускается с использованием динамического охвата, результат: x = 10, y = 7 и z = 12.

Это результаты, которые дал нам наш профессор. Однако я не могу понять, насколько он достиг этих результатов. Может ли кто-нибудь пройти через псевдокод и объяснить свои значения в двух разных типах областей?

4b9b3361

Ответ 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;
}

Ответ 2

Статическая область охвата и динамическое масштабирование - это разные способы поиска конкретной переменной с определенным уникальным именем в программе, написанной на любом языке.

Это особенно полезно для интерпретатора или компилятора, чтобы определить, где и как найти переменную.

Рассмотрим код, как показано ниже,

f2(){

   f1(){
   }

   f3(){
    f1()
   }

}

Static

Это в основном текстовое значение, первая переменная определена или не будет проверена в локальной функции (давайте назовите ее f1()), если не в локальной функции f1(), тогда переменную будет искать в функции f2(), которая ( this Я имею в виду f1()),... это продолжается... до тех пор, пока не будет найдена переменная.

Динамическая:

Это отличается от статического, в смысле, поскольку это больше времени исполнения или динамического, первая переменная определена или не будет проверена в локальной функции, если не в локальной функции f1(), тогда переменная будет искать в функция f3(), которая называется этой функцией (через this я имею в виду f1() снова),... это продолжается... пока не будет найдена переменная.