Creating Unnamed Functions with the Lambda Library

Creating Unnamed Functions with the Lambda Library

A lambda expression enables you to define an unnamed function?directly on the call site of an algorithm. Such unnamed functions are chiefly useful in functional programming, generic libraries, and scientific applications. However, even mainstream C++ applications can benefit from the higher-order programming concepts of lambda abstractions. The following sections show how TR1’s lambda library can simplify the usage of STL algorithms.


How can you create small, unnamed functions directly on the call site of an STL algorithm?


Use the lambda library for implementing lambda functors.

Presenting the Problem
The term a “lambda expression” originates of the mathematical field of lambda calculus. To see what kind of problems a lambda expression can solve, consider the following invocation of the transform() algorithm originally presented in a previous 10-Minute Solution:

string s="hello";transform(s.begin(), s.end(), s.begin(), toupper);

transform‘s fourth argument can be any callable entity, say a functor, a bound member function or a freestanding function. Now, suppose you want to apply a different type of transformation to the string?namely mask every character with an asterisk. You want to be able to write something like this:

transform(s.begin(),           s.end(),           s.begin(),           ='*');

Of course, this code won’t compile. Instead, you have to write a full-blown function first:

int toasterisk(int original_letter){ return '*';}And call transform() like this:transform(s.begin(), s.end(), s.begin(), toasterisk);

Using for_each() instead of transform() for such a task seems more natural:

for_each(s.begin(), s.end(), toasterisk);

But still, the third argument must be the name of a function defined elsewhere in the program. This coding style, though inevitable, is artificial and unwieldy. In some cases, it also compromises encapsulation. Take a look at how to define an unnamed function on the call site using the lambda library. Creating a Lambda Expression
The lambda library enables you to rewrite the previous for_each() call like this:

for_each(s.begin(),          s.end(),         _1 = '*'); 

The interesting part is the third argument:

_1 = '*'

This expression creates a lambda functor which writes * to every character in s. The syntax seems unusual, so let’s see how it works under the hood.

The identifier _1 (an underscore followed by the number one, not a lowercase “L”) is called a placeholder. It’s the crux of the lambda functor. Within each iteration of for_each(), the functor is called with a different element of s as its actual argument. This actual argument is substituted for the placeholder _1, and the body of the lambda functor is evaluated. Thus, within the first iteration _1 is translated to s[0], then s[1] and so on.

Delaying the Evaluation of Constants and Variables
The following line outputs the elements of a vector. Each element is followed by a space:

vector  vi;vi.push_back(10);vi.push_back(20);vi.push_back(30);for_each(vi.begin(),          vi.end(),          cout << _1 << ' ');

Remember to understand what a lambda functor such as:

std::cout << _1 << ' '

does on each iteration, simply replace the placeholder with an actual argument e.g., vi[0]. As expected, this for_each() call outputs:

10 20 30

Now suppose you want to insert a space before each element. You modify the for_each() call slightly:

for_each(vi.begin(),          vi.end(),          cout << ' ' <<_1); 

This however doesn't work as expected. It outputs a single space followed by the elements of viwith no separators. The problem is that none of the operands of:

cout << ' '

is a lambda expression. Therefore, this expression is evaluated immediately, causing a single space to be displayed. In such cases, you need to use the constant() function for delaying the evaluation of constants:

for_each(vi.begin(),          vi.end(),          cout << constant(' ') << _1);

This time, you get the desired output:

10 20 30

The expression constant(' ') produces a nullary lambda functor that stores the character constant ' ' and returns a reference to it when called.

A similar problem arises when you use variables in a lambda expression:

int idx = 0; for_each(vi.begin(),          vi.end(),          cout << ++idx << ':' << _1 << '
');

Here, idx is incremented and displayed only once while the rest of the elements are displayed sequentially. To get the desired results, wrap idx with the function var():

 for_each(vi.begin(),          vi.end(),          cout << ++var(index) << ':' << _1 << '
');

Now you get the desired output:

1:102:203:30

Lambda by Numbers
A unary expression uses one placeholder?the predefined variable _1. The lambda library defines two more placeholders: _2 and _3 for binary and ternary functors, respectively. These placeholders are useful for creating an unnamed function taking two and three operands, respectively. Let's look at an example.

Suppose you have a container of pointers that needs to be sorted. The default sort() implementation will sort the bare pointers, which isn't what you usually want. To override this behavior, provide a lambda functor as the third argument:

struct Foo{//..};vector  vp;//..populate the container sort(vp.begin(), vp.end(), *_1 < *_2);//ascending

Let's see how it works. vp[n] is a pointer. Hence, the placeholders _1 and _2 represent pointers. The complete lambda expression dereferences these pointers and returns a Boolean value indicating whether *_1 is less than *_2. This binary lambda functor ensures that the sorting operation produces the desired results.

Lambda Unlimited
The lambda library has many other constructs for defining if-then-else statements, exception handling, switch statements and the sizeof and typeid operators. Although you've only seen the basic features of the lambda library here, it's clear that it's a very powerful addition to the C++ Standard Library.

Share the Post:
XDR solutions

The Benefits of Using XDR Solutions

Cybercriminals constantly adapt their strategies, developing newer, more powerful, and intelligent ways to attack your network. Since security professionals must innovate as well, more conventional endpoint detection solutions have evolved

AI is revolutionizing fraud detection

How AI is Revolutionizing Fraud Detection

Artificial intelligence – commonly known as AI – means a form of technology with multiple uses. As a result, it has become extremely valuable to a number of businesses across

AI innovation

Companies Leading AI Innovation in 2023

Artificial intelligence (AI) has been transforming industries and revolutionizing business operations. AI’s potential to enhance efficiency and productivity has become crucial to many businesses. As we move into 2023, several

data fivetran pricing

Fivetran Pricing Explained

One of the biggest trends of the 21st century is the massive surge in analytics. Analytics is the process of utilizing data to drive future decision-making. With so much of

kubernetes logging

Kubernetes Logging: What You Need to Know

Kubernetes from Google is one of the most popular open-source and free container management solutions made to make managing and deploying applications easier. It has a solid architecture that makes

ransomware cyber attack

Why Is Ransomware Such a Major Threat?

One of the most significant cyber threats faced by modern organizations is a ransomware attack. Ransomware attacks have grown in both sophistication and frequency over the past few years, forcing

data dictionary

Tools You Need to Make a Data Dictionary

Data dictionaries are crucial for organizations of all sizes that deal with large amounts of data. they are centralized repositories of all the data in organizations, including metadata such as