Iterative recursion

Published on: 2025-07-18

A systematic way to translate recursive algorithms to iterative ones.

By Tom Hulton-Harrop

Topic

When it comes to recursion, there’s an oft quoted phrase that goes…

Every recursive algorithm can be written iteratively.”

That’s a true statement, but not a particularly helpful one. It’s not often that sentence is followed by examples, it’s usually followed by trust me. This had been bothering me lately, so I decided to try and understand how to reliably and repeatedly take a recursive algorithm, and convert it to an iterative one. What follows is one such approach I’ve now applied successfully in a number of cases.

TLDR: See the recursion-experiments repo for a ton of examples of the following technique in action.

Aside: This post owes much to Al Sweigart, author of the excellent book The Recursive Book of Recursion. Reading it really helped me clarify my thinking about recursion (and dust off some of the cobwebs). It also contained some helpful early examples of converting recursive algorithms to iterative ones in Python and JavaScript.

All the examples given below are in C++, but should be transferable to any other procedural language of your choosing.

Motivation

Recursive algorithms abound in computer science literature and on platforms like LeetCode, as well as puzzles such as Advent of Code (where the initial inspiration for this investigation came from). They are often incredibly succinct, and can be quite beautiful at times, but the problem is (at least in my experience) they aren’t something you’d want to use in production code due to the inherent risk of running out of stack space.

With that being said, it would be really nice if we could take a recursive algorithm more or less as-is, and without too much effort, convert it to an iterative one.

I feel like I’ve found one approach to achieve this. It’s definitely not perfect, and it’s almost certainly possible to create a more efficient bespoke iterative alternative, but from a pragmatic standpoint this could be useful to use when you want to take an off the shelf recursive algorithm and convert it to an iterative one.

From recursive to iterative

We’re going to start with one of the best known recursive algorithms, and that’s how to calculate the factorial of a number (e.g. 5! = 5 x 4 x 3 x 2 x 1 = 120). We’re starting with this one because it’s short, simple, and there’s only one recursive call we need to worry about.

Before we look at the recursive version and how to directly translate it to an iterative one, it’s worth quickly seeing the regular iterative version 1.

int factorial_iterative(int n) {
int fact = 1;
for (; n > 0; n--) {
fact *= n;
}
return fact;
}

The above is just a toy example, but it’s a good place to start from. The recursive version is as follows.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
return n * factorial_recursive(n - 1);
}

This version nicely demonstrates the two key pieces of a recursive algorithm. First, we have the base case.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
return n * factorial_recursive(n - 1);
}

And second, the recursive case.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
return n * factorial_recursive(n - 1);
}

In addition to only having one recursive call, the only variable we need to worry about is the function parameter, n.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
return n * factorial_recursive(n - 1);
}

There are four key pieces of information we need to keep track of in what we’ll call our ‘iterative recursive’ function.

  1. Callstack
  2. Function parameters
  3. Return value
  4. Return address

Let’s build up the function piece by piece, adding code to track the items from the above list.

We’ll start with a function declaration, with a signature that matches the recursive version we saw earlier.

int factorial_iterative_recursive(int n) {
// todo
}

We know we’ll need an explicit callstack, and a structure to hold the function parameters and any local variables, so we’ll add those first.

int factorial_iterative_recursive(int n) {
struct frame_t {
int n;
};
std::stack<frame_t> call_stack;
// todo
}

This (mostly) takes care of the callstack and function parameters from our list. Next we need a return value, as well as a way to process the stack.

int factorial_iterative_recursive(int n) {
struct frame_t {
int n;
};
std::stack<frame_t> call_stack;
int return_value;
while (!call_stack.empty()) {
// todo
}
return return_value;
}

We’ve now accounted for the return value, as well as a way to process elements in our stack. All that remains is to deal with the return address. This is where things get interesting…

Whenever a regular function is called, behind the scenes the return address of the function is pushed onto the callstack. This is so the program knows where to continue from when the called function returns. If we return to the recursive implementation of factorial for a moment, when we call the function, execution will start at the entry point of the function on line 3.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
return n * factorial_recursive(n - 1);
}

However, when factorial_recursive(n - 1) returns, execution begins (or more accurately continues), at a later point in the function. If we introduce a local variable for the return value of factorial_recursive(n - 1), it’s a little clearer to see where this is happening (line 5 below). The return value of factorial_recursive(n - 1) is being written to the local variable, return_value.

int factorial_recursive(int n) {
if (n <= 1) {
return 1;
}
int return_value = factorial_recursive(n - 1);
return n * return_value;
}

By switching to do things iteratively we have to handle this ourselves, which is why we must explicitly track the conceptual return address. Each time a fresh invocation of the factorial function is called, it hasn’t yet recorded a return address, it’s only when the function calls itself, the return address is recorded and pushed onto the stack.

To simulate a function call in our ‘iterative recursive’ case, we push a new frame onto the stack, setting its function parameters to be the same value as they’d be in the normal recursive case (n - 1).

// factorial_recursive(n - 1)
call_stack.push(frame_t{.n = top.n - 1});

The problem is we need a way to represent a return address which may not yet exist (later we’ll also need additional states for each recursive call). In the case of C++, std::optional is a very useful type for this, and we’ll use an enum to represent the separate recursive cases 2.

To help keep things consistent for later, we’ll introduce a new enum called return_address_e, wrapped by a std::optional, default constructed to std::nullopt (meaning empty or unset).

Our function now looks like this.

int factorial_iterative_recursive(int n) {
enum class return_address_e { recursive };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
};
std::stack<frame_t> call_stack;
int return_value;
while (!call_stack.empty()) {
// todo
}
return return_value;
}

When we simulate a function call by pushing a new frame onto the stack, before we push the new value we need to record the return address, so when the ‘function’ returns (the frame is popped off the stack), we know where to continue from.

We can implement this like so.

call_stack.top().return_address = return_address_e::recursive;
call_stack.push(frame_t{.n = top.n - 1});

The optional will now contain a value, allowing us to take a separate code path the next time this frame is encountered.

We’ve added the new enum value, and we’re setting it when we call the function, we now just need to use it in our implementation. The key insight is that we need to divide our iterative version across the recursive call boundary, which means splitting the code from before the first recursive call into one branch (when no return address has been set), and the rest into another branch (when we know we have a valid return address).

Adding these branches we get the following.

int factorial_iterative_recursive(int n) {
enum class return_address_e { recursive };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
};
std::stack<frame_t> call_stack;
int return_value;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
// todo - before recursive call
} else if (top.return_address == return_address_e::recursive) {
// todo - after recursive call
}
}
return return_value;
}

We’re super close now, the last bit is just implementing the logic from our recursive call (everything else has mostly just been boilerplate). Let’s start with the code before the recursive call (before the return address has been set), and then further divide that into the base case and the recursive case.

If we look at the base case on its own to start with, it looks like this.

if (top.n <= 1) {
return_value = 1;
call_stack.pop();
continue;
}

This is equivalent to the recursive implementation below.

if (n <= 1) {
return 1;
}

We set the return value to 1 (you can think of return_value as the space we’ve reserved to store the return value from our recursive function), we then pop the current stack frame off the stack (this simulates the function returning), and then the continue statement mimics the return by skipping all remaining code in the loop (we could use an else statement instead, but this keeps things a bit closer to the recursive version in style/appearance).

We’ve already seen the recursive case, as a quick reminder it looks like this.

auto& top = call_stack.top();
...
top.return_address = return_address_e::recursive;
call_stack.push(frame_t{.n = top.n - 1});

If we drop these new lines into place, we have the first half of our implementation (the code that runs the first time this function is called, or put another way, this frame is visited).

int factorial_iterative_recursive(int n) {
enum class return_address_e { recursive };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
};
std::stack<frame_t> call_stack;
int return_value;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (top.n <= 1) {
return_value = 1;
call_stack.pop();
continue;
}
top.return_address = return_address_e::recursive;
call_stack.push(frame_t{.n = top.n - 1});
} else if (top.return_address == return_address_e::recursive) {
// todo - after recursive call
}
}
return return_value;
}

The final piece of the puzzle is to handle the logic after our recursive call returns, it’s essentially this part from the recursive version.

return n * return_value;

To update the return value of the current function call, we take the previous return value that was written to, multiply it by top.n, and then write that back to return_value so it’s updated. We then finally have to pop the current frame from the stack to conclude the return.

This looks as follows.

return_value = top.n * return_value;
call_stack.pop();

We can now slot everything into place and see the full ‘iterative recursive’ version of our factorial function.

int factorial_iterative_recursive(int n) {
enum class return_address_e { recursive };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.return_address = std::nullopt, .n = n});
int return_value; // uninitialised
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (top.n <= 1) {
return_value = 1;
call_stack.pop();
continue;
}
top.return_address = return_address_e::recursive;
call_stack.push(frame_t{.n = top.n - 1});
} else if (top.return_address == return_address_e::recursive) {
return_value = top.n * return_value;
call_stack.pop();
}
}
return return_value;
}

It’s probably worth having a quick read through this to see all the individual pieces and how they fit together. It’s definitely a lot longer and busier than the terse recursive version we saw at the beginning, but the one thing it does have going for it is a good chunk of the implicit logic (that can at times feel a bit magic) is now visible. This makes understanding and debugging the code much easier, and in my mind helps better illuminate the recursive version as well.

The full code listing is also available on Compiler Explorer.

Now even though the above looks like a lot, the good news is this basic pattern maps to any recursive function you want to translate to iterative, with only minor adjustments needed depending on the recursive function you’re converting. Before we look at the general case, let’s review another classic recursive function and see how it maps to the ‘iterative recursive’ case.

The classic recursive function I’m referring to is of course Fibonacci, possibly the best known recursive function, while also being the slowest way to calculate the Fibonacci sequence (that being with the normal recursive version with no memoization).

First of all, before we look at the canonical recursive version of Fibonacci, let’s get the ‘proper’ iterative Fibonacci implementation out of the way.

int fibonacci_iterative(int n) {
if (n <= 1) {
return n;
}
int prev = 0;
int curr = 1;
for (int i = 1; i < n; i++) {
int next = prev + curr;
prev = curr;
curr = next;
}
return curr;
}

On an unrelated note, you can replace lines 8-10 with std::exchange (quite possibly my favourite C++ utility function).

int next = prev + curr;
prev = curr;
curr = next;
prev = std::exchange(curr, prev + curr);

If for some bizarre reason you do need to calculate Fibonacci in your codebase, this is the way you’d do it. It’s fast, simple, and iterative.

Here is the classic recursive version of Fibonacci.

int fibonacci_recursive(int n) {
if (n <= 1) {
return n;
}
return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2);
}

This is slightly more interesting than the first recursive function we looked at to calculate factorial, because there are now two recursive calls happening instead of just one. Converting this to an iterative alternative right off the bat is a bit tricky, because as before, there’s quite a lot that’s hidden from us right now that we need to make explicit in the iterative version. Let’s make a couple of small changes to help with this.

int fibonacci_recursive(int n) {
if (n <= 1) {
return n;
}
int n_minus_1 = fibonacci_recursive(n - 1);
int n_minus_2 = fibonacci_recursive(n - 2);
return n_minus_1 + n_minus_2;
}

We’ve extracted the return values of each recursive call which will make converting things to the iterative version a bit easier. We’ll start with the rough outline of the ‘iterative recursive’ function we had before, and then make the necessary updates to have it handle calculating Fibonacci.

int fibonacci_iterative_recursive(int n)
enum class return_address_e { /*return addresses*/ };
struct frame_t {
std::optional<return_address_e> return_address;
int n; // function parameter
/* any shared local variables */
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.n = n});
int return_value = 0;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
// initial function call
} else if (top.return_address == /* return address */) {
// after first recursive call
} else if (/* any other return addresses */) {
// after second recursive call
}
}
return return_value;
}

If we start with the return_address_e enum, because we have 2 recursive function calls, we know there are going to be two points as which we need to ‘resume’ our function, and two branches in addition to the initial call. We need a simple way to distinguish these so let’s just name the recursive_1 and recursive_2. While we’re at it we can fill in the branches we know we’ll need in our loop.

int fibonacci_iterative_recursive(int n)
enum class return_address_e { recursive_1, recursive_2 };
struct frame_t {
std::optional<return_address_e> return_address;
int n; // function parameter
/* any shared local variables */
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.n = n});
int return_value = 0;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
// initial function call
} else if (top.return_address == return_address_e::recursive_1) {
// after first recursive call
} else if (top.return_address == return_address_e::recursive_2) {
// after second recursive call
}
}
return return_value;
}

In the recursive version we introduced two local variables, n_minus_1 and n_minus_2, to capture the return value of the recursive calls. When the lifetime of these variables extends across a recursive call (appearing before and after it), these variables need to be persisted so they retain the value they had when the function ‘resumes’. To accomplish this we simply need to add these variables to our frame_t structure.

struct frame_t {
std::optional<return_address_e> return_address;
int n;
int n_minus_1;
int n_minus_2;
};

Note: In this example, n_minus_2 is technically redundant. We could write the result of the function call directly to return_value, and then write return_value = top.n_minus_1 + return_value. It is included here as an attempt to help with readability.

Our partially implemented function now looks like this.

int fibonacci_iterative_recursive(int n)
enum class return_address_e { recursive_1, recursive_2 };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
int n_minus_1;
int n_minus_2;
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.n = n});
int return_value = 0;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
// initial function call
} else if (top.return_address == return_address_e::recursive_1) {
// after first recursive call
} else if (top.return_address == return_address_e::recursive_2) {
// after second recursive call
}
}
return return_value;
}

We don’t need to worry about setting the new local variables yet. You could default initialise them if you wanted to (for peace of mind), but they only need to be set where they appear in the recursive call (that’s when the local variables will become ‘alive’ or in scope). We’re really just reserving space for them at the moment.

Now we just need to implement each branch of our function, corresponding to where in the recursive function we are. We’ll start with the first branch, when a new instance of the function is invoked. This includes the base case and first recursive case.

int fibonacci_iterative_recursive(int n)
enum class return_address_e { recursive_1, recursive_2 };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
int n_minus_1;
int n_minus_2;
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.n = n});
int return_value = 0;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (top.n <= 1) {
return_value = top.n;
call_stack.pop();
continue;
}
top.return_address = return_address_e::recursive_1;
call_stack.push(frame_t{.n = top.n - 1});
} else if (top.return_address == return_address_e::recursive_1) {
// after first recursive call
} else if (top.return_address == return_address_e::recursive_2) {
// after second recursive call
}
}
return return_value;
}

This should all look strikingly familiar to the factorial version, the logic is nearly identical. The only difference is we’re setting return_value to n (instead of 1 in the factorial calculation), and the return address to return_address_e::recursive_1 instead of just return_address_e::recursive (as there’s more than one recursive call).

if (top.n <= 1) {
return_value = top.n;
call_stack.pop();
continue;
}
top.return_address = return_address_e::recursive_1;
call_stack.push(frame_t{.n = top.n - 1});

The following branches are then just storing the return address before making the second recursive call, and finally updating the return value with n_minus_1 and n_minus_2.

The branch after the first recursive call is as follows.

top.return_address = return_address_e::recursive_2;
top.n_minus_1 = return_value;
call_stack.push(frame_t{.n = top.n - 2});

We update the return address, write the return value to n_minus_1, and then simulate calling the next function with n - 2.

The final branch is shown below.

top.n_minus_2 = return_value;
return_value = top.n_minus_1 + top.n_minus_2;
call_stack.pop();

We write the return value to n_minus_2 now (instead of the previous assignment to n_minus_1), we then update the return value with the result of n_minus_1 and n_minus_2, and finally we ‘return’ from the function by popping the current frame off the stack.

Our final function now looks like this.

int fibonacci_iterative_recursive(const int n) {
enum class return_address_e { recursive_1, recursive_2 };
struct frame_t {
std::optional<return_address_e> return_address;
int n;
int n_minus_1;
int n_minus_2;
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{.n = n});
int return_value = 0;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (top.n <= 1) {
return_value = top.n;
call_stack.pop();
continue;
}
top.return_address = return_address_e::recursive_1;
call_stack.push(frame_t{.n = top.n - 1});
} else if (top.return_address == return_address_e::recursive_1) {
top.return_address = return_address_e::recursive_2;
top.n_minus_1 = return_value;
call_stack.push(frame_t{.n = top.n - 2});
} else if (top.return_address == return_address_e::recursive_2) {
top.n_minus_2 = return_value;
return_value = top.n_minus_1 + top.n_minus_2;
call_stack.pop();
}
}
return return_value;
}

And we’re done! It’s incredibly verbose, but it’s also weirdly satisfying to see a recursive algorithm exploded/unwrapped, with all the gory details in plain view (instead of elegantly/frustratingly hidden, depending on your perspective).

The full code listing is also available on Compiler Explorer.

Now we’ve been through two transformations from a recursive function to an iterative one, we can start to see how this applies more generally to all recursive functions. Below is a walkthrough of a generic ‘iterative recursive’ function.

/* return type */ iterative_recursive_placeholder(/*recursive function parameters*/) {
enum class return_address_e { recursive[_n] };
struct frame_t {
// return address
std::optional<return_address_e> return_address;
// function parameters
// shared local variables
};
std::stack<frame_t> call_stack;
call_stack.push(frame_t{/* function parameters */});
/* return type */ return_value /* = uninitialised */;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (/* base case condition */) {
return_value = /* parameter, local variable or constant */;
// pop and continue (equivalent to early return)
call_stack.pop();
continue;
}
// set return address (where to return to after the recursive call)
top.return_address = return_address_e::recursive[_n];
// set any local variables needed across recursive calls
top./* local variables */ = /* state */
// 'call' recursive function by pushing a new frame onto the stack
// parameters will be set from current stack top variables
call_stack.push(frame_t{/* recursive parameters */});
} else if (top.return_address == return_address_e::recursive[_n]) {
// update local variable
top./* local variables */ = /* state */
// write to return_value when the function returns
return_value = top./* local variables */;
// pop the stack to simulate the function returning
call_stack.pop();
}
}
return return_value;
}

This basic shape repeats itself no matter the recursive function. The act of transforming a recursive function becomes mechanical once you have these building blocks to hand.

Below is one more example of a recursive function and its equivalent ‘iterative recursive’ variant. The function will generate all possible variations of opening and closing parentheses. For example if 3 is passed to the function, you’ll receive the following output.

// ((())), (()()), (())(), ()(()), ()()()

The recursive implementation is as follows.

std::vector<std::string> balanced_parentheses_internal(
int opening, int closing, std::string current) {
if (opening == 0 && closing == 0) {
return std::vector<std::string>(1, current);
}
std::vector<std::string> results;
if (opening > 0) {
auto r = balanced_parentheses_internal(opening - 1, closing, current + "(");
results.insert(results.end(), r.begin(), r.end());
}
if (closing > opening) {
auto r = balanced_parentheses_internal(opening, closing - 1, current + ")");
results.insert(results.end(), r.begin(), r.end());
}
return results;
}
std::vector<std::string> balanced_parentheses_recursive(int parentheses_count) {
return balanced_parentheses_internal(parentheses_count, parentheses_count, "");
}

The ‘iterative recursive’ version is considerably longer due to the explicit nature of everything, but it will give an identical result.

std::vector<std::string> balanced_parentheses_iterative_recursive(
int parentheses_count) {
enum class return_address_e { recursive_1, recursive_2 };
struct frame_t {
std::optional<return_address_e> return_address;
// function parameters
int opening;
int closing;
std::string current;
// (shared) local variables
std::vector<std::string> results;
};
std::stack<frame_t> call_stack;
call_stack.push(
frame_t{
.closing = parentheses_count,
.opening = parentheses_count,
.current = ""});
std::vector<std::string> return_value;
while (!call_stack.empty()) {
auto& top = call_stack.top();
if (!top.return_address.has_value()) {
if (top.opening == 0 && top.closing == 0) {
return_value = std::vector<std::string>(1, top.current);
call_stack.pop();
continue;
}
if (top.opening > 0) {
top.return_address = return_address_e::recursive_1;
call_stack.push(
frame_t{
.opening = top.opening - 1,
.closing = top.closing,
.current = top.current + "("});
continue;
}
if (top.closing > top.opening) {
top.return_address = return_address_e::recursive_2;
call_stack.push(
frame_t{
.opening = top.opening,
.closing = top.closing - 1,
.current = top.current + ")"});
continue;
}
return_value = top.results;
call_stack.pop();
} else if (top.return_address == return_address_e::recursive_1) {
top.results.insert(
top.results.end(), return_value.begin(), return_value.end());
if (top.closing > top.opening) {
top.return_address = return_address_e::recursive_2;
call_stack.push(
frame_t{
.opening = top.opening,
.closing = top.closing - 1,
.current = top.current + ")"});
continue;
}
return_value = top.results;
call_stack.pop();
} else if (top.return_address == return_address_e::recursive_2) {
top.results.insert(
top.results.end(), return_value.begin(), return_value.end());
return_value = top.results;
call_stack.pop();
}
}
return return_value;
}

The above example highlights two code blocks that are duplicated. This is because in the recursive version, the recursive call balanced_parentheses_internal(opening, closing - 1, current + ")"); can either be called the first time through the function, or after having called balanced_parentheses_internal(opening - 1, closing, current + "(");. This could be refactored and split out to avoid the duplication, but remains here to aid with readability.

Summary

That more or less covers everything. If you want equivalent logging to a recursive function printing at the start, just add a print statement to the first branch of the ‘iterative recursive’ version (the one without a return address). If you don’t care about return values and just want to traverse a tree or graph, you can delete all the return value stuff. There’s a little more work needed to handle recursive functions called from inside a loop (the enumeration is only needed for each recursive call as it appears in the source code). What is needed, is to track the loop index as part of the frame object. Examples of this can be found here, and a more complex case here.

We also haven’t covered memoization, however this is added in more or less the exact same way as it would be to the recursive version. An example of adding memoization to the ‘iterative recursive’ version of Fibonacci can be found here. If you’d like to learn more about memoization, I can highly recommend the book mentioned at the outset of this post, The Recursive Book of Recursion. There’s also a great video from freeCodeCamp that covers memoization and dynamic programming which goes into a lot more detail.

I hope this has proved interesting, and possibly may be helpful in future. Be that with understanding recursive algorithms themselves, or ensuring a recursive algorithm can be applied without exhausting a program’s stack space. A ton of examples from mergesort and quicksort, to combinations and permutations, can be found in the repository mentioned at the top of the article, recursion experiments.

Footnotes

  1. This only works for relatively small numbers, if you need to output the factorial of larger numbers you’ll need something smarter.

  2. It would also be okay to add a separate enum value to represent the ‘start’ or ‘empty’ state of the return address, it’s just those aren’t actually return addresses themselves. The optional approach respects the distinction between elements and their absence, so we can represent an ‘empty’ set, without needing an ‘empty’ value added as part of the set.

Up arrow