Implementing a custom iterator does not change one of its parameters

I have this iterator and want it to stop on some condition, so there is a third 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 like this:

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 the inner loop, so now it is always right, because it seems like it only executes on the first iteration.

The question is : how to check or fulfill the "condition" at each iteration?

+3


source to share


2 answers


The argument is a bool, but you need a predicate function:

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

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

      

Using:



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 a lambda expression, similar to a boolean function that returns i++ < 100

.

+8


source


What you want to provide is a piece of functionality, a rule of thumb. Instead, you specified a value, and this value is evaluated in the expression before the method call, and therefore, within the method, it is constant, never changes, just as you observed.

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

A simple delegation type suitable for this example is Func<T>

, which is basically defined like this:

public delegate T Func<T>();

      

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

Since you want to use the result of this function in a statement while

, you need to return bool

.

You can declare a method here:

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 expression while

to call the delegate. Since it wraps the method, in order to get the value from the method, you must 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 is a new expression:

() => i++ < 100

      

is basically the same as writing:

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

      

but wrapped up in less syntax.

Now, every time the loop starts one iteration, it calls this func, which increments the value and compares it to 100.

+2


source







All Articles