Home C++ Introduction Decisions Loops Input/Output Functions Stack and Heap References Arrays Searching and Sorting Recursion Pointers Character and Strings Structures Classes Inheritance Exceptions Templatess STL Modern C++ Misc Books ----

Lambdas


Contents

STL

We shall examine how lambdas are used in STL.

File: lambdas2.cpp
 #include <algorithm>
 #include <array>
 #include <string>
 #include <functional>
 #include <iostream>

 using namespace std ;

 class customSort1
 {
   public:
    bool operator()(const int& s1, const int& s2)
     {

        return s1 < s2 ;
        }


 };

class Add {
  public:
     int operator()(int a, int b) const
     {
       cout << "Inside Add operator." << endl ;
       return a + b ;
     }
 };



 int main()
 {
     //Using lambda in stl sort algorithm
     array<int, 10> arr1{5, 7, 4, 2, 8, 6, 1, 9, 0, 3};

     auto print1 = [&arr1](const string& rem)
     {
         for (auto element : arr1)
             cout << element << ' ';
         cout << ": " << rem << '\n';
     };

     sort(arr1.begin(), arr1.end());
     print1("sorted with the default operator<");

     auto customSort = [] (int const& s1, int const& s2) -> bool
       {
        return s1 > s2 ;
       };
                               //Pass a lambda object
     sort(arr1.begin(), arr1.end(), customSort);
     print1("sorted with the customSort ");


     //Inline lambda
     sort(arr1.begin(), arr1.end(), [] (int const& s1, int const& s2) -> bool
       {
        return s1 > s2 ;
       });
     print1("sorted with the inline lambds ");


     //Using the built in functor "greater"
     //Signature of the greater is
     //template <class T> struct greater {
     //  bool operator()(const T& lhs, const T& rhs) const;
     //};
     sort(arr1.begin(), arr1.end(), greater<int>() );



     //Use a custom functor object
     customSort1 customSort1Object ;
     sort(arr1.begin(), arr1.end(), customSort1Object );
     print1("Sorted with the functor object.");


     //Return statements can be implicit or
     //explicit.
     //Example of implicit return
     auto add = [] (int a, int b) {
       // always returns an 'int'
       return a + b;
         };
      cout <<  "add called:" << add(4, 5)  << endl ;

     //Explicit return statement
     auto add1 = []  (int a, int b ) -> double {
       return a + b  ;

     };
     cout << "add1 called:" <<  add1(4, 6)  << endl ;


     //inline example
      // initialize vector of integers
       vector<int> nums = {1, 2, 3, 4, 5, 8, 10, 12};

       int even_count = count_if(nums.begin(), nums.end(),
           [](int num) {
             return num % 2 == 0;
           }
       );

     cout << "There are " << even_count << " even numbers."
          << endl ;



    //Without using auto
    //A lambda is a functor object.
    //Add  add3 = [] (int a, int b) {
    function<void(int, int)> add2 = [] (int a, int b) {
     cout << "Sum: " << a + b << endl ;
     };


    add2(1, 2);

    Add add3Object  ;

    //Assign a class object to a function object
    function< int(int, int) > add3 = add3Object ;
    add3( 10, 20 ) ;

/*
    Add add4Object = [] (int a, int b) {
       // always returns an 'int'
       return a + b;
         };
     Compiler Error
    lambdas2.cpp:143:10: error: conversion from ‘main()::<lambda(int, int)>’ to non-scalar type ‘Add’ requested
    143 |
    Expression on the left is of class "Add". Expression on the right is
    a different class type and due to strong static type checking
    we cannnot assign a class of one type to a different type.

         */


}

//https://stackoverflow.com/questions/65186151/conversion-from-lambda-to-non-scalar-type-requested
We have the concept of a function that can be defined with it's body.
Then came along "functors". This is a class with an operator "()" such
as:

    bool operator()(int const& s1, int const& s2)
     {
        return s1 < s2 ;
     }

that can then be called directly by creating an object of the class and
then just doing something like:

obj1( 4, 5 ) ;

The next language enhancement to this approach is lambdas. We have an
expression that is like a function but is actually a functor.


     auto print1 = [&arr1](const string& messg)
     {
         for (auto element : arr1)
             std::cout << element << ' ';
         std::cout << ": " << messg << '\n';
     };
In the above snippet we define a lambda "print1" that
passes the "arr1" object and takes a string as an argument.
We are basically printing out the elements of the "arr1"
object.

We first sort using the std::sort function:

sort(arr1.begin(), arr1.end());

without any custom comparators. This sorts the container
in an ascending order.

     auto customSort = [] (int const& s1, int const& s2) -> bool
       {
        return s1 > s2 ;
       };
     sort(arr1.begin(), arr1.end(), customSort);

In the above we define a lambda object "customSort" that takes
2 integer arguments and returns a boolean value. This sorts the
container in a descending order. The signature for "sort" specifies
the third argument to a functor that takes 2 arguments and returns
a boolean value. Notice that we have explicitly specified the return
type with the "-> bool" part of the expression.

     sort(arr1.begin(), arr1.end(), [] (int const& s1, int const& s2) -> bool
       {
        return s1 > s2 ;
       });
     print1("sorted with the inline lambds ");

We do not have to have a name for the lambda expression and can embed
the expression in the function call as shown above.

   sort(arr1.begin(), arr1.end(), greater() );

 In this call we are using the built in functor "greater" from the
 "" header file. The signature is as:

 template  struct greater {
   bool operator()(const T& lhs, const T& rhs) const;
   };

 As we can see we can use lambda expressions and functors
 interchangeably.

	 class customSort1
	 {
	   public:
		bool operator()(int const& s1, int const& s2)
		 {
			return s1 < s2 ;
		}


	 };

      //Use a custom functor object
      customSort1 customSort1Object ;
      sort(arr1.begin(), arr1.end(), customSort1Object );
      print1("Sorted with the functor object.");

  In the above we have our own functor custom class. We create
  an object of that and pass it to the sort function.

       //Explicit return statement
     	auto add1 = []  (int a, int b ) -> double {
    	  return a + b  ;

       };
       cout << "add1 called:" <<  add1(4, 6)  << endl ;

  The above snippet shows how to specify the type that the lambda
  expression will return. The type is specified just before the body
  of the lambda expression.

      //Without using auto
      //A lambda is a functor object.
      function<void(int, int)> add2 = [] (int a, int b) {
       cout << "Sum: " << a + b << endl ;
       };

      add2(1, 2);

  The above snippet shows an alternative to using "auto" to
  declare the type for lambda. Since a lambda object is a functor,
  we can use the stl function to specify that the functor takes 2
  integer arguments and returns null. We can then call the functor object
  just as we would call a lambda object.


Exercise

1) Complete the "TO DO" section in the below code. Discuss whether it's better to use a function as a lambda when the function is large ( in terms of the number of lines of code ).

File: lambdas_ex_3.cpp
#include <iostream>
#include <vector>
#include <algorithm> // Required for sort and for_each

using namespace std ;


bool isPalindrome(const string& str) {
    if (str.empty()) {
        return true; // An empty string can be considered a palindrome
    }

    int left = 0;
    int right = str.length() - 1;

    while (left < right)
    {
        if (str[left] != str[right])
        {
            return false; // Characters don't match, not a palindrome
        }
        left++;
        right--;
    }
    return true; // All characters matched, it's a palindrome
}


int main()
{
     vector<string> names = {"chair", "table", "civic", "kayak" };

    // Find the palindrome in the vector of strings
   // TO DO Use a lambda expression instead of the function
   //     auto it = find_if(names.begin(), names.end(), isPalindrome ) ;



    if (it != names.end())
      {
        cout << "Found: " << *it << endl ;

      }

    return 0;
}
2) Complete the "TO DO" section in the below code.

File: lambdas_ex_4.cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

using namespace std ;


bool isPerfectSquare( const int& num )
{
   if (num < 0) {
        return false; // Negative numbers cannot be perfect squares
    }
    int root = sqrt( (double)(num) );
    //cout << root << " " << num << endl ;
    return (root * root == num);

}


int main()
{
     vector<int> numbers = {1 , 4, 5, 6, 9, 10 };

      //TO DO
      //Replace  isPerfectSquare by a lambda expression
      //Put bool as an explicit type
      //Capture clause is empty
      //Pass a single const int& as an argument
       int count = count_if(numbers.begin(), numbers.end(),
                       isPerfectSquare  );
       cout << count << endl ;

    return 0;
}
















































Solutions



File: lambdas_ex_3s.cpp


File: lambdas_ex_4s.cpp