Chapter 9. Indefinite Loops

You’ve seen in previous chapters how FP replaces for and foreach loops with LINQ functions like Select() or Aggregate(). That’s absolutely terrific, provided you are working with a fixed-length array, or an enumerable that will determine for itself when it’s time to finish iterating.

But what do you do when you aren’t at all sure how long you’ll want to iterate for? What if you’re iterating indefinitely until a condition is met?

I’ll start in this chapter by giving you a nonfunctional implementation of the ancient Indian board game Snakes and Ladders.1 Here are the rules for anyone who had a childhood tragically bereft of this board-game classic:

  • The board consists of 100 squares. The players start on square 1 and aim to reach square 100.

  • Each player takes a turn to roll a single die and to move their token forward the number of spaces indicated.

  • Landing on the bottom of a ladder means the playing piece should “climb” it to the top, advancing closer to square 100.

  • Landing on the head of a snake means the playing piece should “slide” down it to the end of the tail, moving farther away from square 100.

  • A player who rolls a 6 may take another turn.

  • The first player to reach square 100 wins.

This game has all sort of variations, but I’m going with these relatively simple, basic rules. Every edition of the game positions the snakes and ladders in all sorts of ways. The game we’ll be making, based on a version published in the early 20th century, looks like Figure 9-1.

A Snakes and Ladders board
Figure 9-1. A Snakes and Ladders board (source: Auckland Museum; available under CC BY 4.0 license)

Now, there’s no special reason we have to contain the logic for Snakes and Ladders separately— both do the same job. If a player lands on square x, move them instantly to square y. The only difference between the two is whether the player moves their piece up or down. Logically then, we can treat them as the same thing.

To start off the code, here’s a Dictionary of all Snakes and Ladders, which square they start on, and which square they end on:

private static readonly Dictionary<int, int> SnakesAndLadders =
    new Dictionary<int, int>
{
    { 5, 15 },
    { 8, 34 },
    { 17, 4 },
    { 19, 60 },
    { 25, 67 },
    { 27, 15 },
    { 36, 16 },
    { 48, 70 },
    { 53, 30 },
    { 63, 99 },
    { 64, 22 },
    { 71, 88 },
    { 75, 93 },
    { 80, 44 },
    { 86, 96 },
    { 91, 69 },
    { 95, 74 },
    { 98. 78 }
};

This is what the imperative solution looks like:

public int PlaySnakesAndLaddersImperative(int noPlayers, IDieRoll die)
{
    var currentPlayer = 1;
    var playerPositions = new Dictionary<int, int>();
    for (var i = 1; i <= noPlayers; i++)
    {
        playerPositions.Add(i, 1);
    }

    while (!playerPositions.Any(x => x.Value >= 100))
    {
        var dieRoll = die.Roll();
        playerPositions[currentPlayer] += dieRoll;
        if (SnakesAndLadders.ContainsKey(playerPositions[currentPlayer]))
            playerPositions[currentPlayer] =
                SnakesAndLadders[playerPositions[currentPlayer]];
        // another turn for this player if they roll a 6
        if (dieRoll == 6) continue;
            currentPlayer += 1;
        if (currentPlayer > noPlayers)
            currentPlayer = 1;
    }

    return currentPlayer;

}

You can’t possibly implement this functionality with a Select() statement. We can’t say when the criteria will be met, and we’ll continue to iterate around the while loop until one of them is.

So how can we make this functional? A while loop is a statement (specifically, a control flow statement), and as such it’s not preferred by FP languages.

We have a few options, and I’ll describe each, but this is one of those areas requiring some sort of trade-off. Each choice has consequences, and I’ll do my best to explain their respective pros and cons.

Buckle up your seat belts, because here we go.

Recursion

The classic FP method for handling indefinite loops is to use recursion. In brief, for those of you unfamiliar with it, recursion is the use of a function that calls itself. A condition of some sort determines whether there should be another iteration or whether to actually return data.

If the decision is made at the end of the recursive function, this is known as tail recursion.

A functional and recursive implementation of Snakes and Ladders might look like this:

public record Player
{
    public int Position { get; set; }
    public int Number { get; set; }
}

public record GameState
{
    public IEnumerable<Player> Players { get; set; }
    public int CurrentPlayer { get; set; }
    public int NumberOfPlayers { get; set; }
}

private static Player UpdatePlayer(Player player, int roll)
{
    var afterDieRoll = player with { Position = player.Position + roll };
    var afterSnakeOrLadder = afterDieRoll with
    {
        Position = SnakesAndLadders.ContainsKey(afterDieRoll.Position)
            ? SnakesAndLadders[afterDieRoll.Position]
            : afterDieRoll.Position
    };
    return afterSnakeOrLadder;
}

private static GameState PlaySnakesAndLaddersRecursive(
    GameState state,
    IDieRoll die)
{
    var roll = die.Roll();

    var newState = state with
    {
        CurrentPlayer = roll == 6
            ? state.CurrentPlayer
            : state.CurrentPlayer == state.NumberOfPlayers
                ? 1
                : state.CurrentPlayer + 1,
        Players = state.Players.Select(x =>
                x.Number == state.CurrentPlayer
                    ? UpdatePlayer(x, roll)
                    : x
            ).ToArray()
    };

    return newState.Players.Any(x => x.Position >= 100)
        ? newState
        : PlaySnakesAndLaddersRecursive(newState, die);
}

public int PlaySnakesAndLaddersRecursive(int noPlayers, IDieRoll die)
{
    var state = new GameState
    {
        CurrentPlayer = 1,
        Players = Enumerable.Range(1, noPlayers)
            .Select(x => (x, 1))
            .Select(x => new Player
            {
                Number = x.Item1,
                Position = x.Item2
            }),
        NumberOfPlayers = noPlayers
    };

    var finalState = PlaySnakesAndLaddersRecursive(state, die);
    return finalState.Players.First(x => x.Position >= 100).Number;
}

Job done, right? Well, not really, and I would think very carefully before using a function like this one. The issue is that every nested function call adds a new item onto the stack in the .NET runtime, and if the code has a lot of recursive calls, that can either negatively affect performance or kill the application with a stack overflow exception.

If there are guaranteed to be only a handful of iterations, nothing is fundamentally wrong with the recursive approach. You’d also have to be sure that this is revisited if the code’s usage is ever significantly changed following an enhancement. This rarely used function with a few iterations one day could turn into something heavily used with hundreds of iterations. If that ever happens, the business might wonder why its wonderful application suddenly becomes near unresponsive almost overnight.

So, as I said, think very carefully before using a recursive algorithm in C#. This has the advantage of being relatively simple and not requiring you to write any boilerplate to make it happen.

Note

F# and many other more strongly functional languages have a feature called tail call optimization, which means it’s possible to write recursive functions without them exploding the stack. This isn’t available in C#, however, and there are no plans to make it available in the future. Depending on the situation, the F# optimization will either create IL code with a while(true) loop, or use an IL command called goto to physically move the execution environment’s pointer back to the beginning of the loop.

I did investigate the possibility of referencing a generic tail optimized recursion call from F# and exposing it via a compiled DLL to C#, but that has its own performance issues that makes it a waste of effort.

I’ve seen another possibility discussed online, and that’s to add a post-build event that directly manipulates the .NET IL that C# compiles to so that C# makes retrospective use of the F# tail call optimization feature. That’s clever but sounds too much like hard work to me. It would be a potential extra maintenance task too.

In the next section, I’ll present a technique to simulate tail call optimization in C#.

Trampolining

I’m not entirely sure where the term trampolining came from, but it predates .NET. The earliest references I could find were academic papers from the ’90s, looking at implementing some of the features of LISP in C. I’d guess it’s even a little older than that, though.

The basic idea of trampolining is that we have a function that takes a thunk as a parameter, a thunk being a block of code stored in a variable. In C#, these are implemented as Func or Action. Having got the thunk, we create an indefinite loop with while(true) and some way of assessing a condition that determines whether the loop should terminate. This might be done with an additional Func that returns a bool or some sort of wrapper object that needs to be updated with each iteration by the thunk.

But at the end of the day, what we’re looking at is basically hiding a while loop at the back of our codebase. It’s true that while isn’t purely functional, but this is one of those places where we might need to compromise. Fundamentally, C# is a hybrid language, supporting both OOP and FP paradigms. There are always going to be places where it’s not going to be possible for C# to behave in exactly the way F# does. This is one of them.

We could implement trampolining in numerous ways, but this is the one I’d tend to go for:

public static class FunctionalExtensions
{
    public static T IterateUntil<T>(
        this T @this,
        Func<T, T> updateFunction,
        Func<T, bool> endCondition)
    {
        var currentThis = @this;

        while (!endCondition(currentThis))
        {
            currentThis = updateFunction(currentThis);
        }

        return currentThis;
    }
}

By attaching to type T, which is a generic, this extension method therefore attaches to everything in the C# codebase. The first parameter is a Func delegate that updates the type that T represents to a new form based on whatever rules the outside world defines. The second is another Func that returns the condition that will cause the loop to terminate.

Since this is a simple while loop, there aren’t any issues with the size of the stack. The code is not pure FP, though. It’s a compromise. At the very least, it’s a single instance of a while loop that’s hidden somewhere, deep in the codebase. It may also be that one day Microsoft will release a new feature that enables proper tail call optimization to be implemented somehow, in which case this function can be reimplemented and the code should continue to work as it did, but with one instance fewer of imperative code features:

public int PlaySnakesAndLaddersTrampolining(int noPlayers, IDieRoll die)
{
    var state = new GameState
    {
        CurrentPlayer = 1,
        Players = Enumerable.Range(1, noPlayers)
            .Select(x => (x, 1))
            .Select(x => new Player
            {
                Number = x.Item1,
                Position = x.Item2
            }),
        NumberOfPlayers = noPlayers
    };

    var finalState = state.IterateUntil(x =>
    {
        var roll = die.Roll();

        var newState = state with
        {
            CurrentPlayer = roll == 6
                ? state.CurrentPlayer
                : state.CurrentPlayer == state.NumberOfPlayers
                    ? 1
                    : state.CurrentPlayer + 1,
            Players = state.Players.Select(x =>
                x.Number == state.CurrentPlayer
                    ? UpdatePlayer(x, roll)
                    : x
            ).ToArray()
        };

        return newState;
    }, x => x.Players.Any(y => y.Position >= 100));

    return finalState.Players.First(x => x.Position >= 100).Number;
}

There is another way you can implement trampolining. Functionally, it behaves the same as a hidden while loop and it is pretty much the same in terms of performance as well.

I’m not necessarily convinced that this approach has any additional benefit, and I personally feel it looks a little less friendly than a while loop. But it is probably ever so slightly more in accordance with the functional paradigm in that it dispenses with the while statement. Use this alternative version of trampolining if you prefer, but it’s a matter of personal preference, in my view.

This version is implemented using a C# command that under just about any other circumstance I’d implore you not to use. It has existed in coding since at least the days of BASIC and is still around in some form today: the goto command.

In BASIC, you could move to any arbitrary line of code you wanted by calling goto and specifying the line number. That’s often how loops were implemented in BASIC. In C#, however, you need to create tags, and it’s only to these tags that goto will move.

Here’s a reimplementation of IterateUntil() using two tags. One is called Loop​Begin⁠ning, which is the equivalent of the { character at the beginning of a while loop. The second tag is called LoopEnding, which represents the } character at the end of a while loop:

public static T IterateUntil<T>(
 this T @this,
 Func<T, T> updateFunction,
 Func<T, bool> endCondition)
{
    var currentThis = @this;

    LoopBeginning:

        currentThis = updateFunction(currentThis);
        if(endCondition(currentThis))
            goto LoopEnding;
        goto LoopBeginning;

    LoopEnding:

    return currentThis;
}

I’ll leave it to you to decide which version you prefer. They’re pretty much equivalent. Whatever you do, don’t go using goto anywhere else in your code unless you absolutely, positively, completely, and utterly know what you’re doing and why there’s no better option. Like a certain snake-loving, noseless, evil wizard, the goto command is both great and yet also terrible if used unwisely.

It’s powerful in that you can create effects and improve efficiency (in some cases) in ways that are impossible via any other means. It’s also dangerous in that you no longer have a predictable order of operations—during execution, the pointer can jump to an arbitrary point in the codebase, regardless of whether it makes any sense at all to do so. Used improperly, you could end up with inexplicable, hard-to-debug issues in your codebase.

Use the goto statement with great caution.

A third option, presented in the next section, requires quite a bit more boilerplate but ultimately looks a little friendlier than the previous versions. Have a look, and see what you think.

Custom Iterator

The third option is to hack around with the IEnumerable and IEnumerator interfaces. The thing about IEnumerable is that it isn’t actually an array; it’s just a pointer to the current item of data, and an instruction on how to go and fetch the next item. That being the case, we can create our own implementation of the IEnumerable interface but with our own behavior.

For our Snakes and Ladders example, we want an IEnumerable that’s going to iterate until a player has reached square 100 and therefore won the game. We start by creating an implementation of IEnumerable itself, which has only a single function that has to be implemented: GetEnumerator(). The IEnumerator class sits behind the scenes and does the work of enumerating. That’s what we’ll move on to next.

Understanding the Anatomy of an Enumerator

This is what the IEnumerator interface effectively looks like (it inherits a few functions from other interfaces, so a couple of these functions are here to satisfy the inheritance requirements):

public interface IEnumerator<T>
{
    object Current { get; }
    object IEnumerator.Current { get; }
    void Dispose();
    bool MoveNext();
    void Reset();
}

Each of these functions has a specific job to do, as described in Table 9-1.

Table 9-1. Component functions of IEnumerator
FunctionBehaviorReturns

Current

Get the current data item.

The current item, or null if iteration hasn’t started yet

IEnumera⁠tor​.Current

Get the current data item. The same as Current, but included here from IEnumerator.

Same as Current

Dispose()

Break down everything in the IEnumerable to implement IEnumerator.

void

MoveNext()

Move to the next item.

true if another item is found, false if the enumeration process is complete

Reset()

Move back to the beginning of the dataset.

void

Most of the time, IEnumerable is simply enumerating over an array, in which case I’d imagine the implementation probably should be something vaguely like this:

public class ArrayEnumerable<T> : IEnumerator<T>
{
    public readonly T[] _data;
    public int pos = -1;
    public ArrayEnumerable(T[] data)
    {
        this._data = data;
    }

    private T GetCurrent() => this.pos > -1 ? _data[this.pos] : default;

    T IEnumerator<T>.Current => GetCurrent();

    object IEnumerator.Current => GetCurrent();

    public void Dispose()
    {
        //  Run!  Run for your life!
        // Run before the GC gets us all!
    }
    public bool MoveNext()
    {
        this.pos++;
        return this.pos < this._data.Length;
    }
    public void Reset()
    {
        this.pos = -1;
    }
}

I expect the real code by Microsoft is likely far more complicated—you’d hope there would be a lot more error handling and parameter checking. But this simple implementation gives you an idea of the sort of job the IEnumerator does.

Implementing Custom Enumerators

Knowing how the enumeration process works under the surface, you can see how it’s possible to implement any behavior whatsoever that you’d like in an IEnumerable. I’ll show you a few examples of how powerful this technique can be by creating an IEnumerable implementation that iterates through only every other item in an array by putting the following code in MoveNext():

public bool MoveNext()
{
    pos += 2;
    return this.pos < this._data.Length;
}

// This turns { 1, 2, 3, 4 }
// into { 2, 4 }

How about an IEnumerable that loops over every item twice, effectively creating a duplicate of each item when enumerating:

public bool IsCopy = false;
public bool MoveNext()
{
 if(this.IsCopy)
 {
  this.pos = this.pos + 1;
 }
 this.IsCopy = !this.IsCopy;

 return this.pos < this._data.Length
}

// This turns { 1, 2, 3 }
// into { 1, 1, 2, 2, 3, 3 }

Or an entire implementation that goes backward, starting with an enumerator outer wrapper:

public class BackwardsEnumerator<T> : IEnumerable<T>
{
    private readonly T[] data;

    public BackwardsEnumerator(IEnumerable<T> data)
    {
        this.data = data.ToArray();
    }

    public IEnumerator<T> GetEnumerator()
    {
        return new BackwardsArrayEnumerable<T>(this.data);
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

And afterward, the actual IEnumerable that drives the backward motion:

public class BackwardsArrayEnumerable<T> : IEnumerator<T>
{
     public readonly T[] _data;

    public int pos;

    public BackwardsArrayEnumerable(T[] data)
    {
        this._data = data ?? new T[0];
        this.pos = this._data.Length;
    }

    T Current => (this._data != null && this._data.Length > 0 &&
        this.pos >= 0 && this.pos < this._data.Length)
            ? _data[pos] : default;

    object IEnumerator.Current => this.Current;

    T IEnumerator<T>.Current => this.Current;

    public void Dispose()
    {
        // Nothing to dispose
    }

    public bool MoveNext()
    {

        this.pos = this.pos - 1;
        return this.pos >= 0;
    }

    public void Reset()
    {
        this.pos = this._data.Length;
    }
}

The usage of this backward enumerable is pretty much exactly the same as a standard enumerable:

var data = new[] { 1, 2, 3, 4, 5, 6, 7, 8 };
var backwardsEnumerator = new BackwardsEnumerator<int>(data);
var list = new List<int>();
foreach(var d in backwardsEnumerator)
{
    list.Add(d);
}

// list = { 8, 7, 6, 5, 4, 3, 2, 1 }

Now that you’ve seen how easy it is to create your own IEnumerable with whatever custom behavior you want, conjuring up an IEnumerable that iterates indefinitely should be easy enough.

Indefinitely Looping Enumerables

Try saying this section title 10 times fast!

As you saw in the previous section, there’s no special reason that an IEnumerable has to start at the beginning and loop to the end. We can make it behave in absolutely any way we care to.

Instead of an array, for this example we’ll pass in a single state object of some kind, along with a bundle of code (i.e., a thunk, or Func delegate) for determining whether the loop should continue.

Working backward, the first thing we’ll make is the IEnumerator. This is an entirely bespoke enumeration process, so we’re not going to make any effort to make it generic in any way. The logic wouldn’t make sense outside of a game state object:

public class SnakesAndLaddersEnumerator : IEnumerator<GameState>
{
    // I need this in case of a restart
    private GameState StartState;

    // old game state -> new game state
    private readonly Func<GameState, GameState> iterator;
    // some tricky logic required to ensure the final
    // game state is iterated.  Normal logic is that if
    // the MoveNext function returns false, then there isn't
    // anything pulled from Current, the loop simply terminates
    private bool stopIterating = false;

    public SnakesAndLaddersEnumerator(
        Func<GameState,
        GameState> iterator,
        GameState state)
    {
        this.StartState = state;
        this.Current = state;
        this.iterator = iterator;
    }

    public GameState Current { get; private set; }

    object IEnumerator.Current => Current;

    public void Dispose()
    {
        // Nothing to dispose
    }

    public bool MoveNext()
    {
        var newState = this.iterator(this.Current);
        // Not strictly functional here, but as always with
        // this topic, a compromise is needed
        this.Current = newState;

        // Have we completed the final iteration?  That's done after
        // reaching the end condition
        if (stopIterating)
            return false;

        var endConditionMet = this.Current.Players.Any(x => x.Position >= 100);
        var lastIteration = !this.stopIterating && endConditionMet;
        this.stopIterating = endConditionMet;
        return !this.stopIterating || lastIteration;
    }

    public void Reset()
    {
        // restore the initial state
        this.Current = this.StartState;
    }
}

That’s the hard bit done! We have an engine under the surface that’ll allow us to iterate through successive states until we’re finished—whatever we decide “finished” means.

The next item required is the IEnumerable to run the IEnumerator. That’s pretty straightforward:

public class SnakesAndLaddersIterator : IEnumerable<GameState>
{
    private readonly GameState _startState;
    private readonly Func<GameState, GameState> _iterator;

    public SnakesAndLaddersIterator(
        GameState startState,
        Func<GameState,
        GameState> iterator)
    {
        this._startState = startState;
        this._iterator = iterator;
    }

    public IEnumerator<GameState> GetEnumerator() =>
        new SnakesAndLaddersEnumerator(this._iterator, this._startState);

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

Everything is now in place to carry out a custom iteration. We just need to define the custom logic and set up the iterator:

var state = new GameState
{
    CurrentPlayer = 1,
    Players = Enumerable.Range(1, noPlayers)
        .Select(x => (x, 1))
        .Select(x => new Player
        {
            Number = x.Item1,
            Position = x.Item2
        }),
    NumberOfPlayers = noPlayers
};

var update = (GameState g) =>
{
    var roll = die.Roll();

    var newState = g with
    {
        CurrentPlayer = roll == 6
            ? g.CurrentPlayer
            : g.CurrentPlayer == g.NumberOfPlayers
                ? 1
                : g.CurrentPlayer + 1,
        Players = g.Players.Select(x =>
            x.Number == g.CurrentPlayer
                ? UpdatePlayer(x, roll)
                : x
        ).ToArray()
    };

    return newState;
};

var salIterator = new SnakesAndLaddersIterator(state, update);

We have a couple of options for handling the iteration itself, and I’d like to take a little time out to discuss each of those options in more detail.

Using Indefinite Iterators

Strictly speaking, as a fully fledged iterator, any LINQ operation can be applied, as well as a standard foreach iteration.

Using foreach would probably be the simplest way to handle this iteration, but it wouldn’t be strictly functional. It’s up to you whether you want to compromise by adding a statement in a limited capacity, or want to seek out a more purely functional alternative. Implemented with a foreach, the code might look like this:

foreach(var g in salIterator)
{
// store the updated state outside of the loop.
 playerState = g;

 // Here you can do whatever logic you'd like to do
 // to message back to the player.  Write a message onto screen
 // or whatever is useful for them to be prompted to do another action
}

// At the end of the loop here, the player is now out of jail, and
// the game can continue with the updated version of playerState;

That code wouldn’t give me too much cause for concern in production, honestly. But what we’ve done is negated all the work we’ve put into attempting to get rid of nonfunctional code from our codebase.

The other options use LINQ. As a fully fledged IEnumerable, our salIterator can have any LINQ operations applied to it. Which ones would be the best, though?

Select() would be an obvious starting place but might not behave entirely as you’d expect. Its usage is pretty much the same as any standard Select() list operation you’ve ever done before:

var gameStates = salIterator.Select(x => x);

The trick here is that we’re treating gameIterator as an array, so Selecting from it will result in an array of game states. We’ll basically have an array of every intermediate step the user has gone through, finishing with the final state in the last element.

The easy way to reduce this to simply the final state is to substitute Select() for Last():

var endState = var gameStates = salIterator.Last();

This assumes, of course, that you aren’t interested in the intermediate steps. It might be that you want to compose a message to the user for each state update, in which case you might want to select and then provide a transformation:

var messages = salIterator.Select(x =>
    "Player " + x.CurrentPlayer + "'s turn." +
    "The current winner is: " +
    x.Players.Single(y => y.Number == x.Players.Max(z => z.Position))
);

That eradicates the actual game state, though, so Aggregate() might be a better option:

var stateAndMessages = (
    Messages: Enumerable.Empty<string>(),
    State: state
);

var result = salIterator.Aggregate(stateAndMessages, (agg, x) =>
{
    return (
        agg.Messages.Append("Player " + x.CurrentPlayer + "'s turn." +
                            "The current winner is: " +
                            x.Players.First(y =>
                                y.Position == x.Players.Max(z =>
                                    z.Position)).Number),
        x
    );
});

The x in each iteration of the Aggregate() process is an updated version of the game state, and the code will carry on aggregating until the declared end condition is met. Each pass appends a message to the list, so what you finally get at the end is a tuple containing an array of strings, which are messages to pass to the player, and the final version of the game state.

Bear in mind that any use of LINQ statements that will terminate the iteration early in some manner—such as First or Take—will also prematurely end this iteration process, possibly in our instance with the player still in jail. Of course, this might be a behavior you want! For example, maybe you’re restricting the player to just a couple of actions before moving on to another part of the game or to another player’s turn. You could come up with all sorts of possibilities for the logic by playing with this technique.

Summary

You’ve learned how to implement indefinite iteration in C# using the foreach statement, which results in cleaner code with fewer possible side effects of execution.

It’s not strictly possible to do this purely functionally. Several options are available, all of which carry some level of compromise to the functional paradigm. This is the nature of working in C#. Which option, if any, you wish to use is entirely a matter of personal choice and whatever of any constraints that apply to your project.

More often than not, I go with the trampolining option. It isn’t as dangerous as recursion, nor as much work to implement as a custom iterator. As always with these things, your choice depends what you’re trying to do. The requirements and constraints of your project will determine the best option.

Please be cautious with the use of recursion. It’s a fast method of iteration that’s purely functional, but if you aren’t careful, it can lead to significant performance issues when it comes to memory usage.

In the next chapter, I’ll show you a nice way to take advantage of pure functions to improve performance in your algorithms.

1 Also known as Chutes and Ladders in the US.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.105.239