静态(词法)范围与动态范围(伪代码)

pet*_*rov 57 scope pseudocode output

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;
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,使用静态作用域运行时该程序的结果是:x = 13,y = 7,z = 2.

但是,当使用动态范围运行时,结果为:x = 10,y = 7,z = 12.

这些结果是我们教授给我们的结果.但是,我无法理解他的生活如何达到这些结果.有人可能会通过伪代码并在两种不同类型的范围内解释它们的值吗?

Jos*_*man 185

使用静态(词法)作用域,程序源代码的结构决定了您所指的变量.使用动态范围,程序堆栈的运行时状态决定了您所引用的变量.这可能是一个非常陌生的概念,因为基本上今天广泛使用的每种编程语言(除了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;
}
Run Code Online (Sandbox Code Playgroud)

程序和编译器将这两个变量称为x,但我已对它们进行了标记x1,x2以便于下面的讨论.

使用词法作用域,我们在编译时x根据程序源代码的静态,词法结构确定我们所指的.最里面的定义x范围时,定义 bx1,因此,在讨论的写解析x1,而这也正是x = 2写,所以我们打印2时运行此程序.

使用动态范围,我们在运行时跟踪了一堆变量定义 - 因此x我们写入的变量定义取决于范围内究竟是什么,并且已在运行时动态定义.开始运行ax => x1送到堆栈,调用cx => 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();
}
Run Code Online (Sandbox Code Playgroud)

注意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
}
Run Code Online (Sandbox Code Playgroud)

这里是动态范围.请注意,唯一的更改是在标记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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案应该被接受; 它很好地解释了词法和动态范围之间的区别,特别是增加了更容易阅读的示例程序. (18认同)
  • 这是一个很好的答案,非常清楚地解释了静态作用域和动态作用域之间的区别.我也很欣赏使用x1,x2来区分不同变量的方法.顺便说一句,当我在谷歌搜索"动态范围"时,它突出了这个答案,并在此指出了我.酷UX! (7认同)
  • 只是想知道JavaScript的``this``关键字是否也可以被视为动态范围的一个例子?因为``this``不受词法范围的约束. (4认同)

Pra*_*yat 12

静态作用域和动态作用域是在以任何语言编写的程序中使用特定唯一名称查找特定变量的不同方法.

它特别有助于解释器或编译器决定在何处以及如何找到变量.

考虑代码如下,

f2(){

   f1(){
   }

   f3(){
    f1()
   }

}
Run Code Online (Sandbox Code Playgroud)

静态的:

这基本上是文本的,第一个变量是否定义将在本地函数中检查(让它命名为f1()),如果不在本地函数f1()中,那么变量将在函数f2()中搜索,函数包含函数(由我的意思是F1()),......这样下去......直到变量被发现.

动态:

这与静态不同,在某种意义上,因为它更多是运行时或动态的,所以定义或不定义的第一个变量将在本地函数中检查,如果不是在本地函数f1()中,那么将在函数f3中搜索变量( )调用函数(由我的意思是F1()再次),......这样下去......直到变量被发现.

  • 这真的很简单。 (2认同)