为什么在ada语言中if表达式和if语句与case一起使用?

4
来源:Introduction to Ada—If expressions

Ada的if表达式类似于if语句。但是,有一些区别源于它是一个表达式:

所有分支的表达式必须是相同的类型

如果周围的表达式没有括号,则必须用括号括起来

else分支是强制性的,除非then后面的表达式具有布尔值。在这种情况下,else分支是可选的,如果不存在,则默认为else True

我不明白为什么需要使用两种不同的方法来使用if关键字。背后的理由是什么?

此外,还有case表达式和case语句。这是为什么呢?

4个回答

6
我认为最好的回答是引用Ada 2012 Rationale 第3.1章中的话:
WG9指导文件[1]确定需要关注的关键领域之一是提高编写和执行合同的能力。这些在前一章节中进行了详细讨论。 在定义前置条件、后置条件、类型不变式和子类型谓词的新方面时,很明显,如果没有更灵活的表达形式,许多函数都需要被引入,因为在所有情况下,方面都是由一个表达式给出的。 然而,声明一个函数并在函数体中给出条件、不变量或谓词的详细信息,使得合同的详细信息对于人类读者来说相当遥远。信息隐藏通常是一件好事,但在这种情况下,它只会引入模糊性。 引入了四种形式,即if表达式、case表达式、量化表达式和表达式函数。它们一起赋予Ada某些函数式语言的灵活感。
此外,if语句和case语句通常在所有分支中为同一变量分配不同的值,除此之外没有其他操作:
if Foo > 10 then
   Bar := 1;
else
   Bar := 2;
end if;

在这种情况下,使用一个if表达式可以增加可读性,并更清楚地说明代码中正在发生的事情:
Bar := (if Foo > 10 then 1 else 2);

我们现在可以看到,代码维护者不再需要阅读整个if语句才能看到只有一个变量被更新。
同样适用于case表达式,它们也可以减少嵌套if表达式的需求。
此外,我可以将问题反问回给你:为什么基于C的语言除了if语句之外还有三元运算符“?”:“”?

强调一下,由于Ada经常用于关键软件中可能需要进行代码覆盖证明的情况(https://en.m.wikipedia.org/wiki/Modified_condition/decision_coverage),这种构造可能被禁止(因为它会隐藏源代码覆盖的决策节点)。除非您能够提供目标代码覆盖和正式证明您已经到达了每个相应的指令(在这两种情况下,您可能需要工具资格来提供这些证明)。 - LoneWanderer

4
Egilhh已经涵盖了主要原因,但有时实现表达式还有其他有用的原因。有时候你会制作一些仅需要一个或两个方法的程序包,他们是制作程序包本体的唯一原因。您可以使用表达式来创建表达式函数,从而允许您在规范文件中定义操作。
此外,如果您最终遇到一些复杂的变体记录组合,有时可以使用表达式为它们设置默认值,在通常情况下您无法如此清晰地进行设置。请考虑以下示例:
with Ada.Text_IO; use Ada.Text_IO;

procedure Hello is

    type Binary_Type is (On, Off);

    type Inner(Binary : Binary_Type := Off) is  record
        case Binary is
            when On =>
                Value : Integer := 0;
            when Off =>
                null;
        end case;
    end record;

    type Outer(Some_Flag : Boolean) is record
        Other : Integer := 32;
        Thing : Inner   := (if Some_Flag then 
                              (Binary => Off) 
                            else 
                              (Binary => On, Value => 23));
    end record;

begin
  Put_Line("Hello, world!");
end Hello;

我遇到了一个更复杂的设置,旨在映射到硬件级别的复杂消息接口。如果有默认值是很好的。现在我可以在Outer中使用一个case,但这样我将不得不为每个case想出两个不同命名的消息字段版本,这在你希望代码映射到ICD时并不理想。同样,我也可以使用函数来初始化它,但如其他回答者所指出的那样,这并不总是一个好方法。


4
另一个阐述在Ada中添加条件表达式动机的地方可以在ARG文档中找到,链接为 AI05-0147-1,该文档解释了动机并且给出了一些使用实例。
我发现在处理命令行参数时它们非常有用,特别是当默认值将在未在命令行上指定参数时使用。通常,您需要在程序中将这些值声明为常量。条件表达式使其更容易实现。
with Ada.Command_Line; use Ada;

procedure Main
is
   N : constant Positive :=
     (if Command_Line.Argument_Count = 0 then 2_000_000
      else Positive'Value (Command_Line.Argument (1)));
   ...

否则,如果没有条件表达式,要实现相同的效果,您需要声明一个函数,我认为这更难以阅读;
with Ada.Command_Line; use Ada;

procedure Main
is

   function Get_N return Positive is
   begin
      if Command_Line.Argument_Count = 0 then
         return 2_000_000;
      else
         return Positive'Value (Command_Line.Argument (1));
      end if;
   end Get_N;

   N : constant Positive := Get_N;
   ...

0

在Ada中,if表达式的感觉和使用C语言系列中三元运算符的语句非常相似。我借鉴了learn.adacore.com上的一些代码,介绍了if表达式:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;

procedure Check_Positive is
   N : Integer;
begin
   Put ("Enter an integer value: ");
   Get (N);
   Put (N,0);

   declare
      S : constant String :=
        (if N > 0 then " is a positive number"
         else " is not a positive number");
   begin
      Put_Line (S);
   end;
end Check_Positive;

我将其翻译成了基于C的语言 - 在这种情况下是Java。 我相信要注意的主要点是,尽管语法不同,但两种语言都有效地执行相同的操作:测试条件并在一个语句中将其中一个值分配给变量。 尽管我意识到对于大多数在stackoverflow上的人来说,这是一种过度简化的表述。 我的目标是通过引入示例来帮助初学者理解基本概念。 乾杯。
import java.util.Scanner;

public class IfExpression {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        
        System.out.print("Enter an integer value: ");
        var N = in.nextInt();
        System.out.print(N);
        var S = N > 0 ? " is a positive number" : " is not a positive number";
        System.out.println(S);  
        in.close();
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接