Recursive lambda expressions in C++
Last Updated :
17 Aug, 2022
A recursive lambda expression is the process in which a function calls itself directly or indirectly is called recursion and the corresponding function is called a recursive function. Using a recursive algorithm, certain problems can be solved quite easily. Examples of such problems are Towers of Hanoi (TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc.
A recursive function is a kind of loop structure only. The difference is it maintains a memory stack on its own. Obviously, it must have a break condition like for and while loop. Hence, the recursive function has the following structure-
function name(arguments)
{
a base case (a breaking condition)
recursive code (the actual logic)
}
int fact(int n)
{
// Base Case
if (n < = 1)
return 1;
else
return n * fact(n - 1);
}
C++ 11 introduced lambda expressions to allow us to write an inline function that can be used for short snippets of code that are not going to be reused and not worth naming. In its simplest form, the lambda expression can be defined as follows:
[ capture clause ] (parameters) -> return-type
{
definition of method
}
Program 1:
Below is the program for the lambda expressions in the sort() method in C++:
C++14
// C++ program to implement
// the above approach
#include <bits/stdc++.h>
using namespace std;
// Driver code
int main()
{
int arr[] = { 5, 2, 1, 4, 3 };
sort(arr, arr + 5, [](int& a,
int& b) {
// Instant lambda function
return a > b;
});
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
return 0;
}
Time complexity: O(nlogn)
Auxiliary Space: O(1)
Explanation: Here, the instant lambda function is used, which cannot be used in another sorting method, i.e., the same code needs to be written again. This lambda expression exists only during the execution of the sort() method. But it is possible to store the lambda expression in a variable (better to call it a function) as well, like below. By storing it, it can be used further, and of course, perform recursion also. Â
Program 2:
C++14
// C++ program to implement
// the above approach
#include <bits/stdc++.h>
using namespace std;
// Stored lambda expression
auto cmp = [](int& a, int& b) {
return a > b;
};
// Driver code
int main()
{
int arr[] = { 5, 2, 3, 1, 4 };
sort(arr, arr + 5, cmp);
for (int i = 0; i < 5; i++)
cout << arr[i] << " ";
return 0;
}
Time complexity: O(nlogn)
Auxiliary Space: O(1)
Explanation: Here, auto cmp is the lambda stored function that can be used in as many sorting methods.
Recursion Using Lambda: Let's discuss the concept of recursion and lambda expressions together by considering the recursive function below:
Program 3:
C++14
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Recursive function to print
// the digits of a number
void printReverse(int n)
{
if (n == 0)
return;
cout << n % 10 << " ";
printReverse(n / 10);
}
// Driver code
int main()
{
int n = 12345;
printReverse(n);
return 0;
}
Time complexity: O(logn)
Auxiliary Space: O(logn)
Explanation: Above is the function which prints the digits of a number in reverse order, using recursion. The same can be done using the lambda expression.
Program 4:
Below is the C++ program to implement the above code using lambda expressions:Â
C++14
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Driver code
int main()
{
int n = 12345;
// Recursive lambda function to
// print the digits of a number
auto printReverse = [&]() {
if (n == 0)
return;
cout << n % 10;
n = n / 10;
printReverse();
// As it is a part of main body,
// semicolon is must
};
printReverse();
return 0;
}
Output:Â
Oops, the function, defined using auto return type, must be deducted first (Compiler must be able to determine whether the return type auto can be converted into void or int or something else)Â
Explanation: Here, how the function is going to recognize the variable n, even if it is not passed as an argument. A lambda with an empty capture clause [ ] can access only those variables which are local to it. Here, the capture close [&] is used, which allows the function to access the variable n ( The actual value of variable n is being changed). There are two ways to resolve the error above:
1. By passing the function itself, to the function argument:
Below is the C++ program to implement the above concept:
Program 5:Â
C++14
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Driver code
int main()
{
int n = 12345;
// Function itself as a parameter
auto printReverse = [&](auto&& printReverse) {
if (n == 0)
return;
cout << n % 10 << " ";
n = n / 10;
printReverse(printReverse);
};
// Function as an argument
printReverse(printReverse);
return 0;
}
Time complexity: O(logn)
Auxiliary Space: O(logn)
2. By declaring the function first:
Declaring a function means declaring its name and parameters type in order to inform a compiler that there is a function named xyz, which will write its body later.
Program 6:
Below is the C++ program to implement the above concept:Â
C++
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Declaring of a function
void declaringFunction(string s);
// Driver code
int main()
{
declaringFunction("Hello I am learning how to declare a function");
return 0;
}
// Body of a function
void declaringFunction(string s)
{
cout << s;
}
OutputHello I am learning how to declare a function
Time complexity: O(1)
Auxiliary Space: O(1)
Program 7:
Since it is a lambda function, there must be some unique way of declaring the function. Below is the C++ program for the same:Â
C++14
// C++ program to implement
// the above approach
#include <functional>
#include <iostream>
using namespace std;
// Driver code
int main()
{
int n = 12345;
// Function < return type (parameter
// types) > functionName
// Don't forget to include functional
// header
// Declaration
function<void()> printReverse;
printReverse = [&]() {
if (n == 0)
return;
// Definition
cout << n % 10 << " ";
n /= 10;
printReverse();
};
printReverse();
}
Time complexity: O(logn)
Auxiliary Space: O(logn)
Explanation: In the above code, first, the function printReverse is declared, then we have defined its body. Instead of that, we can directly declare the function and its body along with, which is known as defining a function. Below is the C++ program to implement the above approach:
Program 8:
C++14
// C++ program to implement
// the above approach
#include <functional>
#include <iostream>
using namespace std;
// Driver code
int main()
{
int n = 12345;
// Function < return type (parameter
// types) > functionName
function<void()> printReverse = [&]() {
if (n == 0)
return;
// Declaration + Body
cout << n % 10 << " ";
n /= 10;
printReverse();
};
printReverse();
}
Time complexity: O(logn)
Auxiliary Space: O(logn)
Examples:Â Below are some examples of recursive functions using lambda expressions.
Program 9:Â
C++14
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Driver code
int main()
{
int n = 6;
// Recursive Lambda function to
// find the factorial of a number
auto factorial = [&](auto&& factorial) {
if (n == 1)
return n;
return n-- * factorial(factorial);
};
auto factorial2 = [](int n, auto&& factorial2) {
if (n == 1)
return n;
return n * factorial2(n - 1, factorial2);
};
// Given n = 6
cout << factorial(factorial) << endl;
// Given n = 5
cout << factorial2(5, factorial2);
}
Time complexity: O(2n)
Auxiliary Space: O(n)
Explanation: In the factorial function, n is directly accessed using the [&] capture clause. In the factorial2 function, n is passed as an argument. Hence, there is no need for the [&] clause.
Program 10:
C++14
// C++ program to implement
// the above approach
#include <iostream>
using namespace std;
// Driver code
int main()
{
// Sorted array
int arr[] = { 1, 2, 5, 7, 10, 12, 15 };
int size = 7;
// Item to be searched
int key = 10;
auto binarySearch = [&](int startIndex,
int endIndex,
auto&& binarySearch) {
if (startIndex > endIndex)
return -1;
int midIndex = (startIndex + endIndex) / 2;
if (arr[midIndex] == key)
return midIndex;
if (arr[midIndex] > key)
return binarySearch(startIndex,
midIndex - 1,
binarySearch);
if (arr[midIndex] < key)
return binarySearch(midIndex + 1,
endIndex,
binarySearch);
// Not found
return -1;
};
int index = binarySearch(0, size - 1,
binarySearch);
if (index == -1)
cout << "Not found";
else
cout << "Found on index " << index;
return 0;
}
Time complexity: O(logn)
Auxiliary Space: O(1)
Similar Reads
Lambda Expressions vs Function Pointers
Function Pointer: A function pointer, or a subroutine pointer, or a procedure pointer, is a pointer that points to a function. In simple words, it is a pointer to the location inside the text section. It stores the address of a function and is used for passing a behavior as a parameter to another fu
7 min read
When to Prefer Lambda Expressions over Functors in C++?
In C++, lambda expressions and functors (function objects) both are used for defining anonymous or named functions in C++. In this article, we will learn when to use a lambda expression instead of a functor in C++. When to Prefer Lambda instead of Functors?Prefer to use a lambda expression instead o
4 min read
How to Capture Pairs in Lambda Expression in C++?
In C++, lambda expressions provide an easy way to define anonymous functions inline. They also allow these inline functions to capture some variables from the surrounding scope. In this article, we will learn how to capture pairs in lambda functions in C++ Capture Pairs in Lambda Expressions in C++W
2 min read
When to Use Lambda Expressions Instead of Functions in C++?
In C++, both lambda expressions and functions are used to define operations that can be invoked somewhere else in the code. However, there are some cases where using lambda expressions can be more beneficial than using functions. In this article, we will learn when to use lambda expressions instead
4 min read
How to Parse Mathematical Expressions in a C++ String?
In C++, strings are sequences of characters stored in a char array. Strings are used to store words and text. We can also store mathematical expressions in this string. In this article, we will explore how to parse mathematical expressions in a C++ String. Example: Input:expression = (3 + 4) * 2 / (
5 min read
How to Capture std::vector in Lambda Function?
In C++, lambda functions allow users to define an inline function anywhere in the program. They are also able to capture the objects from outside its definition. In this article, we will look at how to capture a std::vector object in lambda functions in C++. Capture std::vector in Lambda FunctionTo
2 min read
How to use C++ Lambdas with Capture Clauses?
In C++, lambda expressions provide a convenient way to create one-liner functions. One of the key features of lambdas is their ability to capture variables from their surrounding scope by using capture clauses. This allows lambdas to access and modify variables from the outer scope, even after the l
3 min read
Overloads of the Different References in C++
This article focuses on function/method overloads by references, as well as the types of arguments that can be passed. Prerequisites: l-value references.r-value references.Move semantics - std::move(). Overview:l-value refers to a memory location that identifies an object. r-value refers to the data
12 min read
std::unary_negate() in C++ with Examples
The std::unary_negate() is a wrapper function object returning the complement of the unary predicate it holds. A wrapper function is a subroutine in a software library or a computer program whose main purpose is to call a second subroutine or a system call with little or no additional computation. A
3 min read
Useful Inbuilt Functions in C++
In-built functions in C++ are those functions that are part of C++ standard libraries. The purpose of inbuilt functions is to provide common and essential functionality that is frequently required in the programming. In this article, we will look at some of the commonly used inbuilt functions in C++
7 min read