跳至内容

Ada 编程/控制

来自维基教科书,开放世界中的开放书籍

Ada. Time-tested, safe and secure.
Ada. 经时间考验,安全可靠。
这篇文章关于计算机编程,提供伪代码Adaooc版本。

条件语句

[编辑源代码]

条件语句是只有当特定表达式(条件)为时才会执行的代码块。

if-else 语句是最简单的条件语句。它们也被称为分支,因为当程序在执行过程中到达一个 if 语句时,控制会“分支”到两个或多个“方向”中的一个。if-else 语句通常具有以下形式


if condition then
    statement;
else
    other statement;
end if;

如果原始条件满足,则第一个语句中的所有代码都将被执行。可选的 else 部分指定一个备用语句,该语句将在条件为假时执行。不同编程语言的语法会有所不同,但大多数编程语言(特别是过程式结构化 语言)都内置了某种形式的 if-else 条件语句。if-else 语句通常可以扩展为以下形式


if condition then
    statement;
elsif condition then
    other statement;
elsif condition then
    other statement;
...
else
    another statement;
end if; 

整个代码块中只执行一个语句。这个语句将是第一个其条件计算结果为真的语句。使用示例可以更容易理解 if-else-if 结构的概念


with Ada.Text_IO;
use  Ada.Text_IO;
...
type Degrees is new Float range -273.15 .. Float'Last;
...
Temperature : Degrees;
...
if Temperature >= 40.0 then
    Put_Line ("Wow!");
    Put_Line ("It's extremely hot");
elsif Temperature >= 30.0 then
    Put_Line ("It's hot");
elsif Temperature >= 20.0 then
    Put_Line ("It's warm");
elsif Temperature >= 10.0 then
    Put_Line ("It's cool");
elsif Temperature >= 0.0 then
    Put_Line ("It's cold");
else
    Put_Line ("It's freezing");
end if; 

优化提示

[编辑源代码]

当此程序执行时,计算机将按顺序检查所有条件,直到其中一个条件与其对真的概念匹配。一旦发生这种情况,程序将执行紧随条件之后的语句并继续,而不会检查任何其他条件。出于这个原因,当您尝试优化 程序时,最好按降序概率对 if-else 条件进行排序。这将确保在最常见的情况下,计算机需要做更少的工作,因为它很可能只需要检查一两个“分支”就能找到它应该执行的语句。但是,在第一次编写程序时,尽量不要过多地考虑这个问题,以免您发现自己进行了过早优化

话虽如此,您应该知道,一个优化编译器可能会随意重新排列您的 if 语句,前提是该语句没有副作用。在其他技术中,优化编译器甚至可能应用跳转表二分搜索


在 Ada 中,默认情况下,具有多个条件的条件语句不使用短路求值。为了模拟 C/C++ 的短路求值,在条件之间使用and thenor else

通常需要将一个特定变量与几个常量表达式进行比较。对于这种条件表达式,存在 case 语句。例如


case X is
   when 1 =>

      Walk_The_Dog;

   when 5 =>

      Launch_Nuke;

   when 8 | 10 =>

      Sell_All_Stock;

   when others =>

      Self_Destruct;

end case;

X 的子类型必须是离散类型,即枚举类型或整数类型。

在 Ada 中,case 语句的一个优点是编译器将检查选择的覆盖率,即变量 X 的子类型的所有值都必须存在,或者存在一个默认分支when others 必须指定在其余情况下该怎么做。

无条件语句

[编辑源代码]

无条件语句允许您更改程序的流程,而无需条件。使用无条件语句时要小心。它们通常会使程序难以理解。阅读goto 有害吗? 获取更多信息。

结束一个函数并返回到调用过程或函数。


对于过程

return;

对于函数

return Value;

Goto 将控制权转移到标签之后的语句。


   goto Label;

   Dont_Do_Something;

<<Label>>
   ...

goto 有害吗?

[编辑源代码]

自从迪克斯特拉教授发表了Go To Statement Considered Harmfulgoto 在结构化编程语言中被认为是不好的做法,实际上,许多编程风格指南禁止使用 goto 语句。但人们经常忽略的是,任何不是过程或函数中最后一个语句的 return 也是一个无条件语句——一个伪装的 goto。不过,有一个重要的区别:return 是一种仅向前使用的 goto。异常也是一种 goto 语句;请注意,它们不需要指定将要跳转到的位置。

因此,如果您有包含多个 return 语句的函数和过程,那么您就像使用 goto 一样破坏了程序的结构。实际上,几乎每个程序员都熟悉 'return' 语句及其关联的行为;因此,在可读性方面,以下两个示例几乎相同


还要注意,Ada 中的 goto 语句比其他语言中的更安全,因为它不允许您将控制权转移

  • 到代码块之外;
  • case 语句、if 语句或 select 语句的备选方案之间;
  • 在不同的异常处理程序之间;或者从处理的语句序列的异常处理程序返回到其语句序列。
procedure Use_Return is
begin
   Do_Something;

   if Test then
      return;
   end if;

   Do_Something_Else;

   return;
end Use_Return;
procedure Use_Goto is
begin
   Do_Something;
  
   if Test then
      goto Exit_Use_Goto;
   end if;

   Do_Something_Else;

<<Exit_Use_Goto>>
   return;
end Use_Goto;

由于使用 goto 需要声明一个标签,因此 goto 实际上比使用 return 可读性高两倍。因此,如果您关注的是可读性,而不是严格的“不要使用 goto”编程规则,那么您应该使用 goto 而不是多个 return。当然,最好的方法是结构化方法,在这种方法中既不需要 goto 也不需要多个 return


procedure Use_If is
begin
   Do_Something;
  
   if not Test then

      Do_Something_Else;

   end if;

   return;
end Use_If;

循环允许您一遍又一遍地重复一组语句。

无限循环

[编辑源代码]

无限循环是一个永不结束的循环,循环内的语句会永远重复。术语“无限循环”是一个相对术语;如果正在运行的程序因程序控制范围之外的某些原因而强制终止,则无限循环确实会结束。


Endless_Loop :
   loop

      Do_Something;

   end loop Endless_Loop;

循环名称(在本例中为“Endless_Loop”)是 Ada 的一个可选功能。命名循环对于可读性很好,但不是严格必需的。但是,如果程序应该跳出内部循环,循环名称很有用,请参见下文。

循环条件在开头

[编辑源代码]

此循环在开头有一个条件。只要条件满足,语句就会重复执行。如果条件在一开始就不满足,则循环内的语句永远不会被执行。


While_Loop :
   while X <= 5 loop

      X := Calculate_Something;

   end loop While_Loop;

循环条件在结尾

[编辑源代码]

此循环在结尾有一个条件,语句会重复执行,直到条件满足。由于检查在结尾,因此语句至少执行一次。


Until_Loop :
   loop

      X := Calculate_Something;

      exit Until_Loop when X > 5;
   end loop Until_Loop;

循环条件在中间

[编辑源代码]

有时你需要先进行计算,并在满足特定条件时退出循环。但如果条件不满足,则需要执行其他操作。因此你需要一个退出条件位于中间的循环。


Exit_Loop :
   loop

      X := Calculate_Something;

      exit Exit_Loop when X > 5;

      Do_Something (X);

   end loop  Exit_Loop;

在 Ada 中,exit 条件可以与任何其他循环语句结合使用。你也可以拥有多个 exit 语句。你也可以在多个循环嵌套的情况下,退出命名为外层循环。

for 循环

[编辑源代码]

很多时候,你需要一个循环,其中一个特定变量从给定的起始值向上或向下计数到特定结束值。你可以在此处使用 while 循环——但由于这是一种非常常见的循环,因此提供了一种更简单的语法。


For_Loop :
   for I in Integer range 1 .. 10 loop

      Do_Something (I)

   end loop For_Loop;

你不必像示例中那样声明子类型和范围。如果你省略子类型,则编译器将根据上下文确定它;如果你省略范围,则循环将遍历给定子类型的每个值。

与 Ada 一样,当“根据上下文确定”给出两个或多个可能的选项时,将显示错误消息,然后你必须指定要使用的类型。Ada 仅在安全的情况下进行“猜测”。

循环计数器 I 是一个隐式声明的常量,在循环体结束后将不再存在。

数组上的 for 循环

[编辑源代码]

另一种非常常见的情况是需要一个循环来遍历数组的每个元素。以下示例代码展示了如何实现这一点。


Array_Loop :
   for I in X'Range loop

      X (I) := Get_Next_Element;

   end loop Array_Loop;

其中 X 是一个数组。注意:这种语法主要用于数组(因此得名),但也可以在需要完整迭代的其他类型上使用。

工作示例

[编辑 | 编辑源代码]

以下示例展示了如何遍历整数类型的每个元素。

文件: range_1.adb (查看, 纯文本, 下载页面, 浏览所有)
with Ada.Text_IO;

procedure Range_1 is
   type Range_Type is range -5 .. 10;

   package T_IO renames Ada.Text_IO;
   package I_IO is new  Ada.Text_IO.Integer_IO (Range_Type);

begin
   for A in Range_Type loop
      I_IO.Put (Item  => A,
                Width => 3,
                Base  => 10);

      if A < Range_Type'Last then
         T_IO.Put (",");
      else
         T_IO.New_Line;
      end if;
   end loop;
end Range_1;

另请参阅

[编辑 | 编辑源代码]

维基教科书

[编辑 | 编辑源代码]

Ada 参考手册

[编辑 | 编辑源代码]
华夏公益教科书