Question

I'm hoping there's a nicer way to write this method & overloads with less code duplication. I want to return a sequence of deltas between items in a list. this method:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence)
    {
        decimal prev = default(decimal);
        foreach (var item in sequence)
        {
            var current = item;
            decimal diff = current - prev;
            prev = item;
            yield return diff;
        }
    }

works just fine.

I then thought about an overload which would allow an absolute delta, but would call the original method if absolute wasn't required:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,bool absolute)
    {
        if (absolute)
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = Math.Abs(current - prev);
                prev = item;
                yield return diff;
            }
        }
        else
        {
            return CalculateDeltas(sequence);
        }
    }

but this doesn't compile because of Error

"Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration."

I've had a look at this post and it seems like I won't be able to do anything other than repeating the code from the original method:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,bool absolute)
    {
        if (absolute)
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = Math.Abs(current - prev);
                prev = item;
                yield return diff;
            }
        }
        else
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = current - prev;
                prev = item;
                yield return diff;
            }
        }
    }

Can anyone suggest a better way of doing this?

Was it helpful?

Solution

A single method cannot both yield return and return. You must choose one or the other.

You can either do a foreach to yield return the list:

else
{
    foreach (var item in CalculateDeltas(sequence))
        yield return item;
}

Or separate your code into two methods:

if (absolute)
    return CalculateAbsoluteDeltas(sequence);
else
    return CalculateDeltas(sequence);

OTHER TIPS

The simplest approach is probably to split the method into two, one of which is implemented via an iterator block and one of which isn't:

public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,
                                                   bool absolute)
{
    return absolute ? CalculateAbsoluteDeltas(sequence) 
                    : CalculateDeltas(sequence);
}

private static IEnumerable<decimal> CalculateAbsoluteDeltas
    (IEnumerable<decimal> sequence)
{
    decimal prev = default(decimal);
    foreach (var item in sequence)
    {
        var current = item;
        decimal diff = Math.Abs(current - prev);
        prev = item;
        yield return diff;
    }
}

This split approach also allows you to eagerly validate sequence, e.g.

if (sequence == null) {
    throw new ArgumentNullException("sequence");
}

... in the non-iterator block method.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top