Expressions

Conditional Expressions

As we've seen before, we can write simple expressions such as I = 0 or D.Valid. A conditional expression, as the name implies, is an expression that contains a condition. This might be an "if-expression" (in the if ... then ... else form) or a "case-expression" (in the case ... is when => form).

The Max function in the following code example is an expression function implemented with a conditional expression — an if-expression, to be more precise:

    
    
        
package Expr_Func is function Max (A, B : Integer) return Integer is (if A >= B then A else B); end Expr_Func;

Let's say we have a system with four states Off, On, Waiting, and Invalid. For this system, we want to implement a function named Toggled that returns the toggled value of a state S. If the current value of S is either Off or On, the function toggles from Off to On (or from On to Off). For other values, the state remains unchanged — i.e. the returned value is the same as the input value. This is the implementation using a conditional expression:

    
    
        
package Expr_Func is type State is (Off, On, Waiting, Invalid); function Toggled (S : State) return State is (if S = Off then On elsif S = On then Off else S); end Expr_Func;

As you can see, if-expressions may contain an elsif branch (and therefore be more complicated).

The code above corresponds to this more verbose version:

    
    
        
package Expr_Func is type State is (Off, On, Waiting, Invalid); function Toggled (S : State) return State; end Expr_Func;
package body Expr_Func is function Toggled (S : State) return State is begin if S = Off then return On; elsif S = On then return Off; else return S; end if; end Toggled; end Expr_Func;

If we compare the if-block of this code example to the if-expression of the previous example, we notice that the if-expression is just a simplified version without the return keyword and the end if;. In fact, converting an if-block to an if-expression is quite straightforward.

We could also replace the if-expression used in the Toggled function above with a case-expression. For example:

    
    
        
package Expr_Func is type State is (Off, On, Waiting, Invalid); function Toggled (S : State) return State is (case S is when Off => On, when On => Off, when others => S); end Expr_Func;

Note that we use commas in case-expressions to separate the alternatives (the when expressions). The code above corresponds to this more verbose version:

    
    
        
package Expr_Func is type State is (Off, On, Waiting, Invalid); function Toggled (S : State) return State; end Expr_Func;
package body Expr_Func is function Toggled (S : State) return State is begin case S is when Off => return On; when On => return Off; when others => return S; end case; end Toggled; end Expr_Func;

If we compare the case block of this code example to the case-expression of the previous example, we notice that the case-expression is just a simplified version of the case block without the return keyword and the end case;, and with alternatives separated by commas instead of semicolons.

In the Ada Reference Manual

Quantified Expressions

Quantified expressions are for expressions using a quantifier — which can be either all or some — and a predicate. This kind of expressions let us formalize statements such as:

  • "all values of array A must be zero" into for all I in A'Range => A (I) = 0, and

  • "at least one value of array A must be zero" into for some I in A'Range => A (I) = 0.

In the quantified expression for all I in A'Range => A (I) = 0, the quantifier is all and the predicate is A (I) = 0. In the second expression, the quantifier is some. The result of a quantified expression is always a Boolean value.

For example, we could use the quantified expressions above and implement these two functions:

  • Is_Zero, which checks whether all components of an array A are zero, and

  • Has_Zero, which checks whether array A has at least one component of the array A is zero.

This is the complete code:

    
    
        
package Int_Arrays is type Integer_Arr is array (Positive range <>) of Integer; function Is_Zero (A : Integer_Arr) return Boolean is (for all I in A'Range => A (I) = 0); function Has_Zero (A : Integer_Arr) return Boolean is (for some I in A'Range => A (I) = 0); procedure Display_Array (A : Integer_Arr; Name : String); end Int_Arrays;
with Ada.Text_IO; use Ada.Text_IO; package body Int_Arrays is procedure Display_Array (A : Integer_Arr; Name : String) is begin Put (Name & ": "); for E of A loop Put (E'Image & " "); end loop; New_Line; end Display_Array; end Int_Arrays;
with Ada.Text_IO; use Ada.Text_IO; with Int_Arrays; use Int_Arrays; procedure Test_Int_Arrays is A : Integer_Arr := (0, 0, 1); begin Display_Array (A, "A"); Put_Line ("Is_Zero: " & Boolean'Image (Is_Zero (A))); Put_Line ("Has_Zero: " & Boolean'Image (Has_Zero (A))); A := (0, 0, 0); Display_Array (A, "A"); Put_Line ("Is_Zero: " & Boolean'Image (Is_Zero (A))); Put_Line ("Has_Zero: " & Boolean'Image (Has_Zero (A))); end Test_Int_Arrays;

As you might have expected, we can rewrite a quantified expression as a loop in the for I in A'Range loop if ... return ... form. In the code below, we're implementing Is_Zero and Has_Zero using loops and conditions instead of quantified expressions:

    
    
        
package Int_Arrays is type Integer_Arr is array (Positive range <>) of Integer; function Is_Zero (A : Integer_Arr) return Boolean; function Has_Zero (A : Integer_Arr) return Boolean; procedure Display_Array (A : Integer_Arr; Name : String); end Int_Arrays;
with Ada.Text_IO; use Ada.Text_IO; package body Int_Arrays is function Is_Zero (A : Integer_Arr) return Boolean is begin for I in A'Range loop if A (I) /= 0 then return False; end if; end loop; return True; end Is_Zero; function Has_Zero (A : Integer_Arr) return Boolean is begin for I in A'Range loop if A (I) = 0 then return True; end if; end loop; return False; end Has_Zero; procedure Display_Array (A : Integer_Arr; Name : String) is begin Put (Name & ": "); for E of A loop Put (E'Image & " "); end loop; New_Line; end Display_Array; end Int_Arrays;
with Ada.Text_IO; use Ada.Text_IO; with Int_Arrays; use Int_Arrays; procedure Test_Int_Arrays is A : Integer_Arr := (0, 0, 1); begin Display_Array (A, "A"); Put_Line ("Is_Zero: " & Boolean'Image (Is_Zero (A))); Put_Line ("Has_Zero: " & Boolean'Image (Has_Zero (A))); A := (0, 0, 0); Display_Array (A, "A"); Put_Line ("Is_Zero: " & Boolean'Image (Is_Zero (A))); Put_Line ("Has_Zero: " & Boolean'Image (Has_Zero (A))); end Test_Int_Arrays;

So far, we've seen quantified expressions using indices — e.g. for all I in A'Range => .... We could avoid indices in quantified expressions by simply using the E of A form. In this case, we can just write for all E of A => .... Let's adapt the implementation of Is_Zero and Has_Zero using this form:

    
    
        
package Int_Arrays is type Integer_Arr is array (Positive range <>) of Integer; function Is_Zero (A : Integer_Arr) return Boolean is (for all E of A => E = 0); function Has_Zero (A : Integer_Arr) return Boolean is (for some E of A => E = 0); end Int_Arrays;

Here, we're checking the components E of the array A and comparing them against zero.

In the Ada Reference Manual

Declare Expressions

Relevant topics

Todo

Complete section!

Reduction Expressions

Relevant topics

Todo

Complete section!