Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementation of custom iterator does not change one of its parameters

Tags:

iterator

c#

I have this iterator and want it to stop on some condition, thus, there is a 3rd parameter called "condition".

public static IEnumerable<long> Dates(long start, int step, bool condition)
{
    var k = start + step;

    while (condition)
    {
        k += step;
        yield return k;
    }
}

I call it this way :

var i = 0;

foreach (var k in Iterator.Dates(0, 5, i++ < 100))
{
    // Here goes infinite loop because (i++ < 100) is always true inside iterator
}

Unfortunately, this parameter does not change inside loop so now it is always true because seems that it gets executed only on the first iteration.

Question : how to check or execute "condition" on each iteration?

like image 231
Anonymous Avatar asked Dec 03 '25 23:12

Anonymous


2 Answers

Argument is bool, but you need predicate function like this:

public static IEnumerable<long> Dates(long start, int step, Func<bool> condition)
{
    var k = start + step;

    while (condition())
    {
        k += step;
        yield return k;
    }
}

Usage:

var i = 0;

foreach (var k in Dates(0, 5, () => i++ < 100))
{
    // Here goes infinite loop because (i++ < 100) is always true inside iterator
}

Comments

() => i++ < 100 is lambda expression similar to boolean function without arguments that returns i++ < 100.

like image 186
General-Doomer Avatar answered Dec 06 '25 13:12

General-Doomer


What you want to provide is a piece of functionality, a rule. Instead you have provided a value, and this value is calculated in the expression before the method is called and thus inside the method it is constant, never changes, exactly like you observed.

Instead you need to pass in a delegate, you "delegate" the responsibility of providing the rule to the caller.

A simple delegate type appropriate for this example is Func<T> which is basically defined like this:

public delegate T Func<T>();

This is a delegate that wraps a method that returns a value, without taking any parameters.

Since you want to use the result of this function in a while statement, you need it to return bool.

Here's how you would declare the method:

public static IEnumerable<long> Dates(long start, int step, Func<bool> condition)
{
    var k = start + step;

    while (condition())
    {
        k += step;
        yield return k;
    }
}

Note that you need to change the while expression to call the delegate. Since it wraps a method, to obtain the value from the method you have to call it.

Here's how to call the new method:

var i = 0;

foreach (var k in Iterator.Dates(0, 5, () => i++ < 100))
{
    // No longer infinite loop because (i++ < 100) is now evaluated on every iteration
}

This new expression:

() => i++ < 100

is basically the same as writing this:

int i;
bool Func()
{
    return i++ < 100;
}

but wrapped in a smaller syntax.

Now, every time the loop runs one iteration, it will call this func, which will increase the value and compare it to 100.

like image 40
Lasse V. Karlsen Avatar answered Dec 06 '25 13:12

Lasse V. Karlsen