Book Image

Learning C++ Functional Programming

By : Wisnu Anggoro
5 (1)
Book Image

Learning C++ Functional Programming

5 (1)
By: Wisnu Anggoro

Overview of this book

Functional programming allows developers to divide programs into smaller, reusable components that ease the creation, testing, and maintenance of software as a whole. Combined with the power of C++, you can develop robust and scalable applications that fulfill modern day software requirements. This book will help you discover all the C++ 17 features that can be applied to build software in a functional way. The book is divided into three modules—the first introduces the fundamentals of functional programming and how it is supported by modern C++. The second module explains how to efficiently implement C++ features such as pure functions and immutable states to build robust applications. The last module describes how to achieve concurrency and apply design patterns to enhance your application’s performance. Here, you will also learn to optimize code using metaprogramming in a functional way. By the end of the book, you will be familiar with the functional approach of programming and will be able to use these techniques on a daily basis.
Table of Contents (15 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface

Simplifying the function notation using a Lambda expression


The Lambda expression is an anonymous notation that represents something that performs an operation or calculation. In functional programming, the Lambda expression is useful to produce the first class and pure function, which we will discuss in separate chapters in this book. For now, let's familiarize ourselves with this new feature introduced in C++11 by investigating three basic parts of the Lambda expression:

  • capturing list: []
  • parameter list: ()
  • body: {}

The order of these three basic parts is as follows:

    [](){} 

The capturing list part is also used as a mark to identify the Lambda expression. It is a placeholder to value to be involved in the expression. The only capture defaults are the ampersand symbol (&), which will implicitly capture the automatic variables by reference, and the equal sign (=), which will implicitly capture the automatic variables by copy (we will discuss it further in the upcoming section). The parameter list is similar to the capturing list in every function where we can pass the value to it. The body is the implementation of the function itself.

Using the Lambda expression for a tiny function

Imagine we have a tiny one-line function that we invoke only once. It's better if we write the operation of that function directly when we need it. We actually had this function in our previous example when discussing the C++ Standard Library. Just go back to the for_each.cpp file and we will find the PrintOut() function that is only invoked once by for_each(). We can make this for_each loop more readable if we use Lambda. Let's take a look at the following code snippet to examine how we refactor the for_each.cpp file:

    /* lambda_tiny_func.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>
    #include "../vehicle/vehicle.h"

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_tiny_func.cpp]" << endl;

      // Initializing several Vehicle instances
      Vehicle car("car", 4);
      Vehicle motorcycle("motorcycle", 2);
      Vehicle bicycle("bicycle", 2);
      Vehicle bus("bus", 6);

      // Assigning the preceding Vehicle instances to a vector
      vector<Vehicle> vehicles = { car, motorcycle, bicycle, bus };

      // Displaying the elements of the vector
      // using Lambda expression
      cout << "All vehicles:" << endl;
      for_each(
             begin(vehicles),
             end(vehicles),
             [](const Vehicle &vehicle){
                 cout << vehicle.GetType() << endl;
            });

      return 0;
    }

As we can see, we have transformed the PrintOut() function that we used in the for_each.cpp file into a Lambda expression and passed it to the for_each loop. It will indeed give the same output as the for_each.cpp file does. However, now our code becomes more concise and readable.

Using the Lambda expression for multiline functions

The Lambda expression can also be used for multiline functions, so we can put the body of the function on it. This will make our code more readable as well. Let's make a new code. In that code, we will have an integer collection and an intent to inspect whether the selected element is the prime number or not. We can make a separate function, for instance, PrintPrime(), then invoke it. However, since the prime number checking operation is called only once, it's more readable if we transform it into a Lambda expression. The code should look like this:

    /* lambda_multiline_func.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_multiline_func.cpp]" << endl;

      // Initializing a vector containing integer element
      vector<int> vect;
      for (int i = 0; i < 10; ++i)
        vect.push_back(i);

      // Displaying whether or not the element is prime number
      for_each(
             begin(vect),
             end(vect),
             [](int n) {
                cout << n << " is";
                if(n < 2)
                {
                  if(n == 0)
                  cout << " not";
                }
                else
                {
                  for (int j = 2; j < n; ++j)
                    {
                       if (n % j == 0)
                       {
                         cout << " not";
                         break;
                       }
                   }
                 }

                cout << " prime number" << endl;
            });

        return 0;
     }

The output we should see on the screen is as follows:

As we can see in the preceding screenshot, we have successfully identified the prime number by using the Lambda expression.

Returning a value from the Lambda expression

Our two preceding samples of the Lambda expression are just for the purpose to print on console. It means the function does not need to return any value. However, we can ask the Lambda expression to return a value for an instance if we do the calculation inside the function and return the calculation result. Let's take a look at the following code to examine the use of this Lambda:

    /* lambda_returning_value.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_returning_value.cpp]" << endl;

      // Initializing a vector containing integer element
      vector<int> vect;
      for (int i = 0; i < 10; ++i)
        vect.push_back(i);

      // Displaying the elements of vect
      cout << "Original Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n){
                cout << n << " ";
            });
      cout << endl;

      // Creating another vect2 vector
      vector<int> vect2;
      // Resize the size of vect2 exactly same with vect
      vect2.resize(vect.size());
      // Doubling the elements of vect and store to vect2
      transform(
              begin(vect),
              end(vect),
              begin(vect2),
              [](int n) {
                return n * n;
            });

      // Displaying the elements of vect2
      cout << "Squared Data:" << endl;
      for_each(
             begin(vect2),
             end(vect2),
             [](int n) {
                cout << n << " ";
            });
      cout << endl;

      // Creating another vect3 vector
      vector<double> vect3;
      // Resize the size of vect3 exactly same with vect
      vect3.resize(vect.size());
      // Finding the average of the elements of vect
      // and store to vect2
      transform(
              begin(vect2),
              end(vect2),
              begin(vect3),
              [](int n) -> double {
                return n / 2.0;
            });

      // Displaying the elements of vect3
      cout << "Average Data:" << endl;
      for_each(
             begin(vect3),
             end(vect3),
             [](double d) {
                cout << d << " ";
            });
      cout << endl;

      return 0;
     }

When we use the transform() method in the preceding code, we have a Lambda expression that returns a value from the calculation of n * n. However, there's no return type stated in the expression. This is because we can omit the statement of the return type since the compiler has understood that the expression will return an integer value. So, after we have another vector, vect2, which has the same size as vect, we can invoke the transform() method along with the Lambda expression, and the value of vect will be doubled and stored in vect2.

We can, if we want to, specify the return type to the Lambda expression. As we can see in the preceding code, we transformed the vect3 vector based on all values of the vect vector, but now we specify the return type to double using the arrow symbol (->). The result of the preceding code should be like the following screenshot:

As we can see from the preceding screenshot, we have successfully found the doubled and average result using the Lambda expression.

Capturing a value to the Lambda expression

In our previous Lambda expression examples, we keep the capturing part and the square bracket ([]) empty since the Lambda doesn't capture anything and doesn't have any extra member variable in the anonymous object generated by the compiler. We can also specify the object we want to capture in the Lambda expression by specifying it in this square bracket. Let's take a look at the following piece of code to go through the discussion:

    /* lambda_capturing_by_value.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_capturing_by_value.cpp]" << endl;

      // Initializing a vector containing integer element
      vector<int> vect;
      for (int i = 0; i < 10; ++i)
      vect.push_back(i);

      // Displaying the elements of vect
      cout << "Original Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n){
                cout << n << " ";
             });
      cout << endl;

      // Initializing two variables
      int a = 2;
      int b = 8;

      // Capturing value explicitly from the two variables
      cout << "Printing elements between " << a;
      cout << " and " << b << " explicitly [a,b]:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [a,b](int n){
                if (n >= a && n <= b)
                cout << n << " ";
             });
      cout << endl;

      // Modifying variable a and b
      a = 3;
      b = 7;

      // Capturing value implicitly from the two variables
      cout << "printing elements between " << a;
      cout << " and " << b << " implicitly[=]:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [=](int n){
                if (n >= a && n <= b)
                cout << n << " ";
            });
      cout << endl;

      return 0;
    }

In the preceding code, we will try to capture the value in the Lambda expression, explicitly and implicitly. Let's suppose we have two variables, a and b, and we want to explicitly capture the values, we can specify them in the Lambda expression using the [a,b] statement, and then using the values inside the function body. Moreover, if we wish to capture the value implicitly, just use [=] for the capturing part and then the expression will know which variable we intend to use when we specify them in the function body. If we run the preceding code, we will get the following output on the screen:

We can also mutate the state of the values we capture without modifying the value outside the Lambda expression function body. For this purpose, we can use the same techniques as used previously, and add the mutable keyword as shown in the following block of code:

    /* lambda_capturing_by_value_mutable.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_capturing_by_value_mutable.cpp]" << endl;

      // Initializing a vector containing integer element
      vector<int> vect;
      for (int i = 0; i < 10; ++i)
        vect.push_back(i);

      // Displaying the elements of vect
      cout << "Original Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n){
                 cout << n << " ";
            });
      cout << endl;

      // Initializing two variables
      int a = 1;
      int b = 1;

      // Capturing value from the two variables
      // without mutate them
      for_each(
             begin(vect),
             end(vect),
             [=](int& x) mutable {
                 const int old = x;
                 x *= 2;
                 a = b;
                 b = old;
             });

      // Displaying the elements of vect
      cout << "Squared Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n) {
                  cout << n << " ";
            });
      cout << endl << endl;

      // Displaying value of variable a and b
      cout << "a = " << a << endl;
      cout << "b = " << b << endl;

      return 0;
    }

The preceding code will double the element of the vect vector. It uses capturing by value in the Lambda expression and also the mutable keyword. As we can see, we passed the vector element by reference (int& x) and multiplied it by two, then changed the value of a and b. However, since we use the mutable keyword, the final result of a and b will remain the same, although, we have passed the vector by reference. The output on the console looks like the following screenshot:

If we want to change the value of the a and b variables, we have to use the Lambda expression to capture by reference. We can do this by passing the reference to the angle bracket in the Lambda expression, for instance, [&a, &b]. For more detail, let's take a look at the following piece of code:

    /* lambda_capturing_by_reference.cpp */
    #include <vector>
    #include <algorithm>
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_capturing_by_reference.cpp]" << endl;

      // Initializing a vector containing integer element
      vector<int> vect;
      for (int i = 0; i < 10; ++i)
        vect.push_back(i);

      // Displaying the elements of vect
      cout << "Original Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n){
                 cout << n << " ";
            });
      cout << endl;

      // Initializing two variables
      int a = 1;
      int b = 1;

      // Capturing value from the two variables
      // and mutate them
      for_each(
             begin(vect),
             end(vect),
             [&a, &b](int& x){
                 const int old = x;
                 x *= 2;
                 a = b;
                 b = old;
            });

      // Displaying the elements of vect
      cout << "Squared Data:" << endl;
      for_each(
             begin(vect),
             end(vect),
             [](int n) {
                 cout << n << " ";
            });
      cout << endl << endl;

      // Displaying value of variable a and b
      cout << "a = " << a << endl;
      cout << "b = " << b << endl;

      return 0;
     }

The preceding code has the same behavior with the lambda_capturing_by_value_mutable.cpp file that will double the element of the vect vector. However, by capturing by reference, it now also modifies the value of a and b when they are processed in the for_each loop. The a and b values will be changed at the end of the code, as we can see in the following screenshot:

Preparing the value using initialization captures

Another great feature of the Lambda expression coming up in C++14 is its initialization captures. The expression can capture a value of the variable and assign it to the expression's variable. Let's take a look at the following piece of code implementing the initialization captures:

    /* lambda_initialization_captures.cpp */
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_initialization_captures.cpp]" << endl;

      // Initializing a variable
      int a = 5;
      cout << "Initial a = " << a << endl;

      // Initializing value to lambda using the variable
      auto myLambda = [&x = a]() { x += 2; };

      // Executing the Lambda
      myLambda();

      // Displaying a new value of the variable
      cout << "New a = " << a << endl;

      return 0;
     }

As we can see in the preceding code, we have an int variable named a with the value 5. The Lambda expression, myLambda, then captures the a value and executes it in the code. The result is that now the a value will be 7 since it is added by 2. The following output screenshot should appear in our console window when we run the preceding code:

From the preceding snapshot, we see that we can prepare the value to be included in the calculation inside the Lambda expression.

Writing a generic Lambda expression to be used many times with many different data types

Before C++14, we have to specifically state the type of the parameter list. Fortunately, now in C++14, Lambda expressions accept auto as a valid parameter type. Therefore, we can now build a generic Lambda expression as demonstrated in the following code. In that code, we have only one Lambda expression to find out which is the greatest value between two numbers passed to the expression. We will use the auto keyword in parameter declaration so it can be passed by any data type. Therefore, the findMax() function parameters can be passed by both the int and float data type. The code should be as follows:

    /* lambda_expression_generic.cpp */
    #include <iostream>

    using namespace std;

    auto main() -> int
    {
      cout << "[lambda_expression_generic.cpp]" << endl;

      // Creating a generic lambda expression
      auto findMax = [](auto &x, auto &y){
        return x > y ? x : y; };

      // Initializing various variables
      int i1 = 5, i2 = 3;
      float f1 = 2.5f, f2 = 2.05f;

      // Consuming generic lambda expression
      // using integer data type
      cout << "i1 = 5, i2 = 3" << endl;
      cout << "Max: " << findMax(i1, i2) << endl << endl;

      // Consuming generic lambda expression
      // using double data type
      cout << "f1 = 2.5f, f2 = 2.05f" << endl;
      cout << "Max: " << findMax(f1, f2) << endl << endl;

      return 0;
     }

The output we will see on the console should be as follows:

Note

The C++17 language plans to introduce two new features for the Lambda expression--they are capturing *this, which allows the expression to capture the enclosing object by copy, and the constexpr Lambda expressions, which allows us to use the result of the Lambda expressions and generate constexpr objects at compile time. However, since C++17 has not been released yet, we cannot try it for now.