ADA 95教程 变量作用域范围

大型项目分解

由于Ada是一种高度结构化的语言,它可以通过使用过程和函数将一个大项目划分成许多小项目。因为过程和函数可以嵌套在其他过程和函数中,所以存在类型、变量、常量和子程序的可见性和范围问题。

 

变量的范围是什么?

Example program ------> e_c09_p1.ada

  -- Chapter 9 - Program 1
procedure Scope is

   Count : INTEGER;

   procedure Level1 is
      Index : INTEGER;

      procedure Level2 is
         Count : INTEGER;
      begin
         null;
      end Level2;

      procedure Level2_Prime is
         Data : INTEGER;
      begin
         null;
      end Level2_Prime;

   begin
      null;
   end Level1;

   procedure Other_Level1 is
   begin
      null;
   end Other_Level1;

begin
   null;
end Scope;




-- Result of execution

--   (No output from this program.)

检查名为e_c09_p1.ada的程序,以获得几个不同范围的变量示例。您应该花几分钟熟悉包含主程序或过程的程序结构,以及其中嵌入的四个过程。我们将从第4行中声明的名为Count的变量开始,并声明它的作用域从声明末尾的分号扩展到第32行中整个程序的结尾。它的作用域扩展到程序的末尾,因为它是在主程序的声明部分声明的。它通常被称为全局变量。

 

变量在哪里可见?

第4行中声明的名为Count的变量在其作用域的任何位置都可见,但程序的一个小区域除外。由于第10行中定义了另一个同名变量,因此第一个变量有效地隐藏在较新的本地变量范围内的视图中。注意,优先于本地变量并隐藏全局变量,而不是另一种方式。从第4行命名的Count变量,从第10行的末尾到第13行的末尾是不可见的。应该清楚的是,局部变量的范围扩展到声明它的子程序的可执行部分的末尾。

与此类似,在第7行中定义的名为Index的变量具有一个范围,该范围从第7行的末尾扩展到以第23行结束的过程的结束。在其范围内,名为Index的变量是可见的,因为在较低级别子程序中没有同名的其他变量。在从第16行末尾到第19行末尾的范围内,名为Data的变量可见。

 

那其实是个谎言

全局变量计数从第10行到第13行是不可见的,但是有一种方法可以使用它,尽管它的本质是隐藏的。这将是下一个示例程序的主题,但是您应该编译并运行当前的程序,以查看它是否真的会按照给定的方式编译。没有输出,因此执行将是无趣的。

 

使用点符号

Example program ------> e_c09_p2.ada

  -- Chapter 9 - Program 2
procedure Scope2 is

   Count, Index : INTEGER;

   procedure Level1 is
      Index, Count : INTEGER;

      procedure Level2 is
         Count : INTEGER;
      begin
         Count :=                             -- Count from line 10
                  Scope2.Count;               -- Count from line 4
      end Level2;

      procedure Level2_Prime is
         Data, Index, Count : INTEGER;
         Outer_Index : INTEGER renames Scope2.Level1.Index;
      begin

         Count := Index                        -- Count from line 17
                        + Scope2.Level1.Count; -- Count from line 7

         Index :=                              -- Index from line 17
                  Scope2.Level1.Index +        -- Index from line 7
                   Scope2.Index;               -- Index from line 4

         Index :=                              -- Index from line 17
                  Outer_Index +                -- Index from line 7
                   Scope2.Index;               -- Index from line 4

      end Level2_Prime;

   begin
      null;
   end Level1;

   procedure Other_Level1 is
   begin
      Count := Index;                         -- Both from line 4
   end Other_Level1;

begin
   Count := Index;                            -- Both from line 4
end Scope2;




-- Result of execution

-- (No output from this program)

检查名为e_c09_p2.ada的程序,以获取使不可见变量可见的一些示例。细心的观察者会注意到,这是最后一个程序的结构,声明了额外的变量,并添加了一些赋值语句。

我们将考虑三个相同名称的变量Count,如果愿意的话,我们可以在一个语句中使用这三个变量。假设我们在程序的第12行希望使用名为Count的局部变量,即在第10行声明的变量。根据Ada的定义,最里面的变量将优先,通过简单地使用Count,我们就使用了所需的变量。但是,如果我们想使用第4行中声明的符号,我们可以使用第13行中说明的“点”符号。我们给编译器一个在哪里找到变量的完整映射。点表示法可以理解为“转到外部级别,Scope2,dot和名为Count的变量”。因此,第13行是指第4行中声明的变量。使用符号Scope2.Level1.Count将引用第7行中声明的变量。

第21行到第30行给出了使用点符号来使用其他不可见变量的其他示例。这也称为变量的扩展名或扩展命名约定。

 

重命名变量

为了减少使用的击键次数并提高某些程序的清晰度,Ada提供了重命名功能。第18行通过将三分量组合重命名为更简单的名称Outer_Index来说明这一点。在过程中允许使用较长名称的任何地方,使用新的较短名称也是合法的,因为它们只是同一实际变量的同义词。这是一个很容易在程序中被滥用的构造,使程序变得不必要的复杂,因此应该谨慎使用。

编译并运行这个程序,即使它没有输出,以确保它确实会编译。点符号将在Ada的许多其他地方使用,因此您应该熟悉它。

 

ADA块

Example program ------> e_c09_p3.ada

-- Chapter 9 - Program 3
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Blocks is

   Index, Count : INTEGER;

begin
   Index := 27;
   Count := 33;
   Put("In the main block      - values are");
   Put(Index, 5);                             -- Blocks.Index
   Put(Count, 5);                             -- Blocks.Count
   New_Line;

   declare
      Index, Stuff : INTEGER := -345;
   begin
      Index := 157;
      Put("In the embedded block  - values are");
      Put(Blocks.Index, 5);                   -- Blocks.Index
      Put(Index, 5);                          -- local Index
      Put(Stuff, 5);                          -- local Stuff
      Put(Count, 5);                          -- Blocks.Count
      New_Line;
   end;

   Put("Back to the main block - values are");
   Put(Index, 5);                             -- Blocks.Index
   Put(Count, 5);                             -- Blocks.Count
   New_Line;

   Who:                                       -- Block name
   declare
      Index, Stuff : INTEGER := -345;
   begin
      Index := 157;
      Put("In the block named Who - values are");
      Put(Blocks.Index, 5);                   -- Blocks.Index
      Put(Index, 5);                          -- Who.Index
      Put(Who.Index, 5);                      -- Who.Index
      Put(Stuff, 5);                          -- Who.Stuff
      Put(Who.Stuff, 5);                      -- Who.Stuff
      Put(Count, 5);                          -- Blocks.Count
      New_Line;
   end Who;

   Put("Back to the main block - values are");
   Put(Index, 5);                             -- Blocks.Index
   Put(Count, 5);                             -- Blocks.Count
   New_Line;

end Blocks;




-- Result of execution

-- In the main block      - values are   27   33
-- In the embedded block  - values are   27  157 -345   33
-- Back to the main block - values are   27   33
-- In the block named Who - values are   27  157  157 -345 -345   33
-- Back to the main block - values are   27   33

 

检查名为e\u c09\u p3.ada的程序,以获取使用ada块的示例。正如您可以定义一个过程并跳转到它一样,通过调用它,Ada允许您定义过程的等价物并将其作为内联代码执行。这样一段代码称为块,由declare、begin和end三个保留字构成,declare和begin之间有声明,begin和end之间有可执行语句。任何新类型、子类型、变量、常量甚至子程序都可以在块的声明部分声明,并在可执行部分使用。声明的范围从声明它们的位置开始,并在块的末尾结束。

 

块是一条语句

一个块是一个单独的语句,因为它是,它可以放在任何合法的地方放任何其他可执行语句。它可以在循环中使用,也可以在if语句的一个分支中使用,甚至可以作为case语句的一个case。示例程序包含两个这样的块,第一个在第17行到第27行,第二个在第34行到第47行。唯一真正的区别是,第二个块是一个命名块,名为Who,其用法将很快定义。

研究这个程序,你会发现,即使在块中定义了几个变量,并且至少有一个是一个全局变量的重复,所有变量实际上都可以通过使用我们在研究上一个程序时定义的点符号来获得。在第一个块中,当有重复的名称时,局部变量是默认变量,但是在第二个块中,可以使用点表示法来具体地命名变量。这是可能的,因为块是命名的,在这个特殊情况下,名称是Who。名称紧跟在块的前面,后面跟一个冒号,名称在块的末尾重复。在本例中,名称对您没有任何帮助,但是如果有两个嵌套块,则可以命名其中一个或两个,并且您可以选择感兴趣的变量。可以嵌套的块数没有限制。

请注意,用于块的名称不是一个可以跳转以执行goto语句的标签。该名称仅用于命名块。

如果不需要声明,则可以声明不带保留字declare的块,只使用执行块分隔符begin和end。如果没有声明部分,就没有理由声明块,除非我们谈到异常处理的主题,在这里拥有这个功能将非常有用。编译并执行此程序并研究结果。确保您了解每个显示值的来源。

查看Ada 95参考手册(ARM)的第5.6节,以获得更多使用ARM的经验。你可能会对这一节关于块的简短内容感到惊讶。

 

什么是自动变量?

Example program ------> e_c09_p4.ada

 -- Chapter 9 - Program 4
with Ada.Text_IO, Ada.Integer_Text_IO;
use Ada.Text_IO, Ada.Integer_Text_IO;

procedure Automatc is

   Dog, Cat : INTEGER;
   Pig, Cow : FLOAT;

begin

   for Index in 1..10 loop
      Put("The value of Index is");
      Put(Index, 3);

      declare
         START : constant INTEGER := Index;
         STOP  : constant INTEGER := START + 5;
         Count_Stuff : INTEGER;
      begin
         Count_Stuff := START + STOP + Index + 222;
         Put(" --->");
         for Index in START..STOP loop
            Put(Index, 5);
         end loop;
      end;

      New_Line;
   end loop;

end Automatc;




-- Result of execution

-- The value of Index is  1 --->    1    2    3    4    5    6
-- The value of Index is  2 --->    2    3    4    5    6    7
-- The value of Index is  3 --->    3    4    5    6    7    8
-- The value of Index is  4 --->    4    5    6    7    8    9
-- The value of Index is  5 --->    5    6    7    8    9   10
-- The value of Index is  6 --->    6    7    8    9   10   11
-- The value of Index is  7 --->    7    8    9   10   11   12
-- The value of Index is  8 --->    8    9   10   11   12   13
-- The value of Index is  9 --->    9   10   11   12   13   14
-- The value of Index is 10 --->   10   11   12   13   14   15

现在是讨论一个非常重要的话题的好时机,这个话题对你将来如何编写一些程序有着重要的影响。主题是自动变量,它们是什么,它们为您做了什么。定义它们的最好方法是检查另一个程序,而编写名为e_c09_p4.ada的程序就是为了说明这一点。

这个程序实际上非常简单,因为它只是一个大循环,其中变量index覆盖范围从1到10。每次通过循环时,第16行到第26行中的块都会被执行,并包含另一个循环以输出一些整数类型的数据。仔细注意这些常量以及它们的使用方式,你会看到一些有点奇怪的东西。每次进入块时,我们都会为循环变量输入一个较大的值,在本例中称为Index,因此每次通过块时常量都是不同的。但是,在每次传递过程中,它们都是恒定的,因此将被视为恒定的。这种行为是完全正常的,当我们定义一个自动变量时就会清楚地理解。

在进入块之前,两个常量和名为Count_Stuff的变量不存在。输入块时,系统将生成常量,并将其初始化为常量值,以便在块中使用。由于每次输入块时都会生成常量,因此每次都可以将它们初始化为不同的值,这正是这里要做的。将常数赋值的过程称为精化。

该变量也被生成,并可在块中使用,在块中为其分配了一个无意义的值,用于说明目的,然后从不使用。当程序控制离开块时,在这种情况下从底部掉出,两个常量和变量就完全不存在了,在程序中的任何地方都不再可用。据说它们的寿命是有限的,它们的寿命是从它们被详细阐述到我们离开声明它们的区块为止。因此,变量或常量的作用域和生存期对于理解非常重要。

您应该清楚,除了第23行到第25行之外,从第12行到第29行都可以看到外部循环变量Index。在第23行到第25行的区域内,不能使用外循环变量,即使使用扩展命名约定(即点符号),因为外循环没有名称。命名外循环将使外循环变量可用。

 

这还用在哪里?

自动变量的这个概念非常重要,因为在Ada程序中有很多地方都使用它。在本示例程序中,它被用在四个不同的地方,一次在块中,正如我们刚才提到的,一次在主程序本身中,四个带有动物名称的变量被自动生成,两次在for循环中。每个for循环中名为Index的变量都是自动变量,在进入循环时生成,在循环完成时丢弃。正如您所看到的,有一个很好的理由可以解释为什么在离开循环后循环控制变量不可用。

每次调用过程或函数时,都会生成形式参数,定义的变量和常量也是如此。变量生成和常量初始化的过程称为精化。然后在子程序中使用它们,当过程或函数完成并将控制返回给调用程序时,它们将被丢弃。

因为主程序本身就是一个过程,所以其变量的处理方式是相同的。

 

堆栈存储自动实体

生成的常量和变量存储在内部堆栈中,其定义超出了本教程的范围。如果您了解什么是堆栈,那么您应该清楚系统如何生成项、将它们放置在堆栈上、使用它们以及丢弃它们。另外,由于堆栈的性质,当调用嵌套更深的过程和函数时,您应该清楚如何在堆栈上放置其他变量。最后,只有使用自动变量,才能使用递归子程序。Ada要求所有子程序都是可重入的,而堆栈的使用使得这成为可能。

如果最后一段对你来说太技术化了,不要担心,因为提到它只是为了一般信息,不需要理解Ada编程。

编译并运行e_c09_p4.ada并研究输出。一定要理解自动变量的概念,因为Ada中的一些高级编程技术需要这些知识。

 

编程练习

1.修改e_c09_p3.ada,在第一个块的声明部分包含一个过程。当从块的可执行部分调用时,该过程应该向监视器输出一行文本。为什么不能从块外调用此过程?(Solution)

2.在e_c09_p4.ada中命名块,并使用点表示法输出循环中外部索引的值。它将输出相同的数字六次,因为外索引在内环中是不变的(Solution)

 

---------------------------------------------------------------------------------------------------------------------------

原英文版出处:https://perso.telecom-paristech.fr/pautet/Ada95/a95list.htm

翻译(百度):博客园  一个默默的 *** 的人

 

posted @ 2021-04-01 21:07  yangjianfeng  阅读(128)  评论(0编辑  收藏  举报