Concepts / Function Objects and Lambda Expressions

Function Objects and Lambda Expressions

Together, they explain why function objects are especially useful as arguments to algorithms, which may call the argument many times over a sequence of elements.

  • Programming
Interactive lab

Try it: Functions as Values: Lambda

That a lambda (like def) evaluates to a function object that a name can refer to and that can be passed to other functions — apply(f, x), twice(f, x), map(f, xs), sorted(xs, key=f) — and that each call is traced by binding the argument, substituting it into the single expression and evaluating in operator order.

How it works

  1. lambda x: expression evaluates to a function object (named <lambda>); nothing inside it is computed yet. f = ... gives it a name, exactly as def f(x): return expression would.
  2. Writing f without parentheses passes the function object itself; inside apply or twice the parameter func refers to the same object.
  3. A call binds the argument to the parameter x, substitutes it into the expression, and evaluates by operator precedence (* and % before + and -, parentheses first).
  4. The value of the expression is the call's return value; there is no other body.
  5. map(f, xs) calls f once per item and keeps the results in order; sorted(xs, key=f) calls f once per item to get keys, orders by the keys (ties keep their order) and returns the items.

Default run (9 steps): The program is ready: a lambda written inline, then f(x). … print shows 7.

Simplified: Four one-parameter lambdas with editable integer constants, small int arguments (at most 6 items). Each substituted expression the lab shows was evaluated by CPython 3.12 and has the same value as the real call; printed results and ZeroDivisionError messages match CPython.

Educational simulation

Loading the simulation…

A Counting Problem That Needs a Rule

Suppose you have a list of words and want to count how many come before the word 'm' in alphabetical order. A general counting algorithm can walk through the elements of the list, but it does not know your rule. It asks a question about each element, and the question itself has to come from somewhere. The question you need is not 'is this element equal to some value?' It is 'is this element less than a particular string?' This article looks at how a function object can supply that question to a general algorithm, and why that approach is useful.

What do you think happens?

Given the words apple, kiwi, and mango, how many come before 'm' in alphabetical order?

  • 1

  • 2

  • 3

Reveal answer
Answer:

2

apple and kiwi come before 'm'. mango does not, because 'm' is a prefix of 'mango' and therefore sorts before it. The same rule will be built into a function object in the sections that follow.

Tracing the Word List Element by Element

Before looking at any code, follow what a counting algorithm does with the three words. It visits each element in turn, asks the same question about it, and adds one to the count whenever the answer is yes. The question stays the same for every element. Only the element being examined changes.

Worked example

Walking the list with the question 'is this word less than m?'

Count the words in the list apple, kiwi, mango that are less than the string 'm'.

  1. 1
    Visit apple

    Ask whether apple is less than 'm'. It is, so the count goes from 0 to 1.

  2. 2
    Visit kiwi

    Ask whether kiwi is less than 'm'. It is, so the count goes from 1 to 2.

  3. 3
    Visit mango

    Ask whether mango is less than 'm'. It is not, because 'm' is a prefix of 'mango' and sorts before it. The count stays at 2.

Result

The count is 2.

Function Objects Carry Their Value

Definition

A function object is an object that can be used as if it were a function. The source describes Less_than<string>{s} as an object that compares to the string called s. The object holds that string inside itself. When you create it, the value it compares against travels with it.

This is the main reason function objects are useful. Without them, a separate ordinary function would be needed for each value you might want to compare against, and each value type might need its own function as well. The alternative would be to keep the value in a global variable that the function reads. Function objects avoid both problems. You do not write a separate function for each value and type, and you do not introduce global variables to hold the values.

The diagram below contrasts the two approaches. In the first column, each comparison value needs its own function, and the value is kept in a global variable that the function reads. In the second column, one object holds both the value and the operation, so nothing outside the object needs to remember what the comparison value was.

Function for mone per valueLess_than objectone objectFunction for zone per valueCarried value mheld insideGlobal valuestored outsidereadsreadscontains
What the function object contains that a separate function per value cannot, and how one object replaces many functions plus global variables.

Why Simple Function Objects Are Efficient

Carrying data is only half of the story. The source also states that, for a simple function object such as Less_than, inlining is straightforward. Inlining means the body of the operation can be placed directly where it is used, instead of being reached through a separate function call at run time. Because of this, a call of Less_than is far more efficient than an indirect function call.

The two properties support each other. Carrying data makes the object convenient, and its efficiency makes it practical. Together, they explain why function objects are especially useful as arguments to algorithms, which may call the argument many times over a sequence of elements. A small cost per call adds up across a long sequence, so avoiding an indirect call on every step matters.

Policy Objects and General Algorithms

Definition

A policy object is a function object that defines the meaning of a key operation in a general algorithm. The word policy describes the role of the object. The algorithm is general: it knows how to walk a sequence and count matches. The policy object decides what counts as a match. Changing the policy changes the meaning of the operation without changing the algorithm.

count_ifgeneral walk and countSequence elementsapple, kiwi, mangoLess_than policydefines the matchCarried string mheld by the policyvisits eachasks match questioncompares against
How count_if calls into the Less_than policy for its key operation, and which part of the behavior belongs to the algorithm versus the policy.

Read the diagram from top to bottom. The algorithm owns the walk through the sequence and the bookkeeping of the count. The policy owns the answer to the question of whether an element matches. The algorithm never needs to be rewritten to answer a different question; only the policy passed to it changes.

Code: Passing a Policy to count_if

The following example is an original illustration built from the source's idea. It defines a function object that carries a string and compares against it, then passes that object to the count_if algorithm. The class is a template, so the same pattern works for other types.

cpp
Output
n is 2
Notes on the code
  • Less_than stores the string s inside itself when it is created.
  • operator() is the call that count_if makes for each element.
  • The comparison rule lives entirely in the Less_than object passed to count_if.

The output is 2 because apple and kiwi come before 'm', while mango does not. The count_if algorithm is general. The comparison rule lives entirely in the Less_than policy object, so the same algorithm could be given a different policy for a different question, such as a different string to compare against.

Implicit Notation with Lambda Expressions

The example above has a drawback. You have to define Less_than separately from its use, and that separation can feel inconvenient when the object is needed for only one call. The notation for implicitly generating function objects addresses this. It lets you describe the object at the point where it is needed instead of writing a separate definition first. In C++ this notation is called a lambda expression.

cpp
Output
n is 2
Notes on the code
  • The lambda plays the role of the Less_than object, written in place.
  • The result is the same as the separately defined Less_than version.
[ ]capture list (empty here)(const string& x)parameter: each element{ return x < "m"; }body: the comparison rulethenthen
The parts of the lambda that replaces the separate Less_than definition: the capture list, the parameter, and the body that compares against m.

Common Mistakes With Function Objects

  • Common mistake

    Writing a separate function for every comparison value

    What it looks like

    One function for less than m, another for less than z, each reading its own global variable.

    Why it fails

    This is the approach function objects are meant to avoid. It multiplies functions and introduces global state for every value.

    Do this instead

    Create a function object that holds its value, such as Less_than<string>{"m"}, and pass that object to the algorithm.

  • Common mistake

    Assuming the algorithm decides what counts as a match

    What it looks like

    Expecting count_if to know that the words should be compared alphabetically against 'm'.

    Why it fails

    The algorithm is general. It only walks the sequence and counts. The rule comes from the policy object.

    Do this instead

    Pass a policy object whose operation defines the match you want.

  • Common mistake

    Expecting the lambda to behave differently from the named object

    What it looks like

    Thinking the inline lambda is a different kind of thing that works by another mechanism.

    Why it fails

    The source describes the implicit notation as a way to generate the same kind of function object at the point of use. It changes where the definition is written, not how the object behaves.

    Do this instead

    Treat the lambda as a shorter way to write the same function object, and reason about it the same way.

Trace It Yourself

EASY

A list contains the words zebra, lemon, and banana. Using the same counting pattern with a function object that compares against 'm', how many words does count_if count, and which words are counted?

Hints
  • Ask the question 'is this word less than m?' for each word in order.
  • Remember that a word is counted only when the answer is yes.
  • banana and lemon sort before 'm'; zebra does not.

Key Takeaways

  • A function object carries its comparison value inside itself, so you do not need a separate function per value or global variables to hold the values.
  • A simple function object such as Less_than can be inlined, which makes calling it far more efficient than an indirect function call, especially when an algorithm calls it many times.
  • A policy object is a function object that defines the meaning of a key operation in a general algorithm such as count_if; changing the policy changes the operation without changing the algorithm.
  • The implicit notation, called a lambda expression in C++, lets you generate a function object at the point of use instead of defining it separately first, without changing how the object behaves.

Related concepts

More concepts starting with F