Lesson #14. Lambda expressions

lambda expression Theory

  • A lambda expression is some unnamed expression representing a functional dependency.
  • Based on lambda expression, the compiler builds a function, identifies it in some way and replaces this lambda expression with this identifier.
  • A lambda expression can be stored in procedural variables, or passed as parameters of a procedural type.
  • Sample 1: An example of a lambda expression that returns, first, the square of the parameter, and then returns the parameter to the third power. The lambda expression is stored in the procedural variable x.
    1. Declaring of a procedural variable, and then binding it to a lambda expression:

    begin
     var x: integer-> integer;
     x:= t -> t * t;
     Print(x(7)); // 49
     x := t -> t * t * t;
     Print(x(7)); // 343
    end.

    2. Declaring and initialization of a procedural variable at the same time:

    begin
     var x: integer-> integer:= t -> t * t;
     Print(x(7)); // 49
     x := t -> t * t * t;
     Print(x(7)); // 343
    end.
    IDE PascalABC.NET allows you to use a beautiful arrow instead of -> (on the keyboard: with the Alt key pressed, we type 26 on the small numeric keypad).
    Sample 2: An example of a procedural variable with two parameters that returns the maximum of the parameters using a lambda expression.
    begin
      var f: (integer,integer)integer :=(x, y) -> if x > y then result:=x else result:=y;
      print(f(5,13))
    end.
    If a procedural variable is intended to store procedures (and not functions), then when declaring it, empty brackets must be specified after the arrow -> ().
    Sample 3: An example of a procedural variable with two parameters that outputs the maximum of the parameters using a lambda expression.
    begin
      var f: (integer,integer)() :=(x, y) -> if x > y then print(x) else print(y);
      f(5,13)
    end.
    Sample 4: An example of a lambda expression that outputs the squared parameter.
    begin
    var x: integer-> () := t -> (t * t).Print();
    x(5) // 25
    end.
    Sample 5: Example of a procedural variable with tuple assignment: the variable is associated with a lambda expression that takes two integers and returns their sum and product:
    1. A classic solution using the procedure:

    procedure Pr(a,b:integer; var sum,mult:integer);
    begin
      sum:=a+b;
      mult:=(a*b);
    end;
    begin
      var sum,mult:integer;
      Pr(2,4,sum,mult);
      print(sum,mult) // (6,8) 
    end.

    2. Solution using a procedural variable with tuple assignment and a lambda expression:

    begin
      var f: (integer,integer) -> (integer, integer);
      f := (a, b) -> (a + b, a * b);
      print(f(2, 4)) // (6,8) 
    end.
    Lambda function as a parameter
    Sample: Create a procedure that outputs a list of values of an arbitrary function of one argument on the range [a;b].

    The results when a=-5, b=-1 and function x -> x * x:

    -5 25 
    -4 16 
    -3 9 
    -2 4 
    -1 1 
    

    ✍ Solution:

      procedure Lamb(a, b: integer; f: real-> real);
      begin
        for var x := a to b do 
        begin
          Println(x, f(x));
        end
      end;
       
      begin
        Lamb(-5, 3, x -> x * x);
        Writeln;
      end.

    Labs and tasks

    {0.5 points} Task 1:

    To do: Create a procedural variable with one parameter — a two-digit number that returns a number in which the digits are swapped. Associate the variable with a lambda expression to perform the task. Do the task twice: the procedural variable returns the value and the procedural variable outputs the value. (see samples 2, 3, 4)

    expected output:

    enter two-digit number please: 
    >> 72
    27 
    

    [Program name: 14task-01.pas]

    {0.5 points} Task 2:

    To do: Calculate the value of the variable y by one of the two branches:

    To solve this, create a procedural variable with two parameters — a and b, and associate it with a lambda expression. Complete the task twice: the procedural variable returns the value and the procedural variable outputs the value. (see samples 2, 3, 4)

    expected output:

    enter two numbers 
    >>2
    >>5
    result: 3  
    

    [Program name: 14task-02.pas]

    {0.5 points} Task 3:

    To do: Create a Triangle(a,h) function that calculates the perimeter of an isosceles triangle using its base a and the height h drawn to the base. (see sample 5 solution 2)

    1. Declare the procedural variable TriangleP, defining its type as a function that takes two real parameters and returns a real result:

    var TriangleP: (real, real) -> real;

    2. Then, assign a lambda expression to this variable, which, using two parameters a and h — the base and height of an isosceles triangle — calculates the perimeter of this triangle (P = 2b+a; the side formula: b2 = (a/2)2 + h2)

    expected output:

    enter two numbers 
    >>4
    >>6
    result: 16.6491106406735  
    

    [Program name: 14task-03.pas]

    {0.5 points} Task 4:

    To do: Declase a TrianglePS(a, P, S) procedure that calculates an equilateral triangle’s perimeter P = 3·a and area S = a2·(3)1/2/4 (a — input triangle side, P and S — output parameters; all parameters are real). Using this procedure, output the perimeters and areas of three equilateral triangles with these sides. Solve this problem twice: a classical procedure and a procedural variable associated with a lambda expression. (see sample 5 solutions 1 and 2)

    expected output:

    >>2
    61.73205080756888
    >>5
    1510.8253175473055
    >>6
    1815.5884572681199
    

    [Program name: 14task-04.pas]


    Standard functions and procedures and Transformation of an array elements

    Lab 1:

    To do: Create the function to search x in the array. The function returns -1 if it is not found and index of the element otherwise.

    ✍ Algorithm:

      function IndexOfW<T>(a: array of T; x: T): integer;
      begin
        var n := a.Length;
        var i := 0;
        while (i < n) and (a[i] <> x) do
          i += 1;
        Result := i = n ? -1 : i;
      end;
       
      begin
        var a := new integer[10];
        a := arrRandomInteger(10, 0, 10);
        print(a);
        print(indexOfW(a, 2))
      end.
    Lab 2:

    To do: Print out how many odd elements are in the array.

    Expected output:

    [18,10,91,47,35] 
    3 
    

    ✍ Algorithm:

      begin
        var a := new integer[5];
        a := arrRandomInteger(5); // [18,10,91,47,35] 
        print(a);
        print(a.Count(a->odd(a))) // 3 
      end.
    Lab 3:

    To do: Transform the array elements using the rule: if element is even then substruct it by 1, if it is odd then add 1 to it.

    ✍ Algorithm:

        begin
          var a := new integer[5];
          a := arrRandomInteger(5); // [4,36,93,36,29] 
          a.Transform(a,a -> a mod 2 = 0 ? a-1 : a + 1);
          print(a) // [3,35,94,35,30] 
        end.
    {0.5 points} Task 5:

    To do: Create a randomly generated array. Use the standard functions and procedures to implement the following goals. Print out:

    1. reversed array (Reverse())
    2. the index of x number (x is entered)
    3. the last index of x number (x is entered) (LastIndexOf())
    4. true if x exists in the array, false if it doesn’t (Contains())
    5. index of the first even element (IndexOf())
    6. the number of positive elements in the array (Count()). You should create your own function that returns true if the checked number is positive.
    7. maximum element and it’s index (Max(), IndexMax()).
    8. transformed array with a rule: if element is positive then square it, if it is negative then set it to 0 (Transform()).

    Expected output:

    1. [1,6,6,4,9,9,1,3,3,10] result: 10 3 3 1 9 9 4 6 6 1
    2. [8,10,0,5,10,7,2,9,8,2]  enter x: >>10  the result: 1 
    3. [8,10,0,5,10,7,2,9,8,2]  enter x: >>10  the result: 4
    4. [8,10,0,5,10,7,2,9,8,2]  enter x: >>3  the result: false
    5. [7,1,0,3,2,4,2,10,9,4] result: 2 
    6. [7,-1,0,3,-2,4,2,-10,9,4] result: 7
    7. [7,1,0,3,2,4,2,10,9,4] result: max = 10 indexMax= 7
    8. [-8,5,1,-5,-8,10,-9,-7,-7,1] result: 0 25 1 0 0 100 0 0 0 1
    

    [Program name: 14task-05.pas]

    Extra tasks:

    1. Open taskBook of pascalAbc.net IDE:
    2. Choose ArrTransF group in the dropdown menu and click html-page button:
    3. Complete the tasks giving the files the proper names (ArrTransf1.pas etc.)
    Lab 4, Loop over some indices:

    To do: Create an array of integer elements. Pint out the array values in the interval [10,19] and increase by one it’s positive values with even indexes.

    Expected output:

    array: [41,90,100,12,16,7,69,23,59,37] 
    array values in the interval [10,19]: 12 16 
    

    ✍ Algorithm:

      begin
        var a:=arrRandomInteger(10);
        print(a);
        // values in the interval [10,19]
        foreach var i in a.Indices(x -> x.InRange(10,20)) do 
          print(a[i]);
       
        // positive values with even indexes
        foreach var i in a.Indices((x,i) -> (i mod 2 = 0) and (x > 0)) do 
          a[i] += 1;
      end.
    {0.4 points} Task 6:

    To do: Create a randomly generated array. Use indeces method to print out:

    • The values of the array with odd indexes.
    • The second half of the array elements.

    Expected output:

    array: [8,41,56,82,8,93,44,70,6,54] 
    The second half of the array elements: 8 41 56 82 8 
    The values of the array with odd indexes: 41 82 93 70 54 
    

    [Program name: 14task-06.pas]

    Lab 5:

    To do: Create the procedure to shift circularly the elements to the left

    ✍ Algorithm:

      procedure CircularShiftLeft<T>(a: array of T);
      begin
        var v := a[0];
        for var i:=0 to a.Length-2 do
          a[i] := a[i+1];
        a[a.Length-1] := v;
      end;
       
      begin
        var a := new integer[5];
        a := arrRandomInteger(5); // [56,28,33,57,25] 
        CircularShiftLeft(a);
        print(a)// [28,33,57,25,56] 
      end.
    Lab 6:

    To do: Create the procedure to shift circularly the elements to the right

    ✍ Algorithm:

      procedure CircularShiftRight<T>(a: array of T);
      begin
        var v := a[a.Length-1];
        for var i:=a.Length-1 downto 1 do
          a[i] := a[i-1];
        a[0] := v;  
      end;
       
      begin
        var a := new integer[5];
        a := arrRandomInteger(5); // [25,23,74,17,31] 
        CircularShiftRight(a);
        print(a)// [31,25,23,74,17] 
      end.
    {0.3 points} Task 7:

    To do: Create a randomly generated array. At first, transform its elements using the rule: a-1, and after, reverse it’s elements and after, make a circular shift it’s elements to the right and to the left.

    Expected output:

    array:  [93,25,52,74,76,40,54,13,29,65] 
    transformed:  [92,24,51,73,75,39,53,12,28,64] 
    Shift left:  [24,51,73,75,39,53,12,28,64,92] 
    Shift right:  [92,24,51,73,75,39,53,12,28,64] 
    

    [Program name: 14task-7.pas]