Как переписать Single(OrDefault) с помощью LINQ Aggregate Catamorpshim?

Я читал статью, написанную Бартом де Сметом довольно давно: http://community.bartdesmet.net/blogs/bart/archive/2009/11/08/jumping-the-trampoline-in-c-stack-friendly-recursion.aspx

Читателю оставлено в качестве упражнения определение всех других катаморфических операторов в LINQ в терминах оператора Aggregate:
- Простой: (длинный) счетчик, сумма, средняя, ​​минимальная, максимальная
- немного сложнее: все, любое, содержит
- Больше размышлений: первый(OrDefault), последний(OrDefault), одиночный(OrDefault)

Мне удалось использовать совокупный катаморфизм почти для всего, кроме метода расширения Single(). Кроме того, в нескольких случаях, таких как Contains() или First(), нет должного переписывания, потому что я не вижу, как можно остановить Совокупный катаморфизм (всякий раз, когда был найден элемент, соответствующий некоторым условиям) вместо того, чтобы перебирать остальную часть всей данной последовательности.

Подводить итоги:
Переписать метод расширения Single() с помощью Aggregate?
- Как остановить агрегатный катаклизм LINQ, чтобы вернуть элемент, когда элемент был найден, без продолжения итерации остальной части данной последовательности (например, AggregateContains() должен вернуть true для первого элемента, соответствующего значению, и прекратить итерацию).

public static class Program
{
    private static void Main()
    {
        var n = 42;
        var numbers = Enumerable.Range(1, n).ToArray();

        Console.WriteLine(numbers.Count() == numbers.AggregateCount());
        Console.WriteLine(numbers.Count(IsEven) == numbers.AggregateCount(IsEven));

        Console.WriteLine(numbers.LongCount() == numbers.AggregateLongCount());
        Console.WriteLine(numbers.LongCount(IsEven) == numbers.AggregateLongCount(IsEven));

        Console.WriteLine(numbers.Sum() == numbers.AggregateSum());
        Console.WriteLine(numbers.Sum(i => i.IsEven() ? i : 0) == numbers.AggregateSum(i => i.IsEven() ? i : 0));

        Console.WriteLine(numbers.Average() == numbers.AggregateAverage());
        Console.WriteLine(numbers.Average(i => i * 2) == numbers.AggregateAverage(i => i * 2));

        Console.WriteLine(numbers.Min() == numbers.AggregateMin());
        Console.WriteLine(numbers.Max() == numbers.AggregateMax());

        Console.WriteLine(numbers.All(i => i < n + 1) == numbers.AggregateAll(i => i < n + 1));
        Console.WriteLine(numbers.Any(IsEven) == numbers.AggregateAny(IsEven));

        Console.WriteLine(numbers.Last() == numbers.AggregateLast());
        Console.WriteLine(numbers.LastOrDefault() == numbers.AggregateLastOrDefault());
        Console.WriteLine(numbers.Last(IsEven) == numbers.AggregateLast(IsEven));
        Console.WriteLine(numbers.LastOrDefault(IsEven) == numbers.AggregateLastOrDefault(IsEven));

        Console.WriteLine(numbers.First() == numbers.AggregateFirst());
        Console.WriteLine(numbers.FirstOrDefault() == numbers.AggregateFirstOrDefault());
        Console.WriteLine(numbers.First(IsEven) == numbers.AggregateFirst(IsEven));
        Console.WriteLine(numbers.FirstOrDefault(IsEven) == numbers.AggregateFirstOrDefault(IsEven));

        Console.ReadKey();
    }

    private static bool IsEven(this int number)
    {
        return number % 2 == 0;
    }
}


public static class EnumerableAggregateExtensions
{
    public static int AggregateCount<T>(this IEnumerable<T> source)
    {
        return source.Aggregate(0, (r, i) => r + 1);
    }

    public static int AggregateCount<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        return source.Aggregate(0, (r, i) => predicate(i) ? r + 1 : r);
    }

    public static long AggregateLongCount<T>(this IEnumerable<T> source)
    {
        return source.Aggregate(0L, (r, i) => r + 1);
    }

    public static long AggregateLongCount<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        return source.Aggregate(0L, (r, i) => predicate(i) ? r + 1 : r);
    }

    public static int AggregateSum(this IEnumerable<int> source)
    {
        return source.Aggregate(0, (r, i) => r + i);
    }

    public static int AggregateSum<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
        return source.Aggregate(0, (r, i) => r + selector(i));
    }

    public static double AggregateAverage(this IEnumerable<int> source)
    {
        return source.Aggregate(new { Sum = 0, Count = 0 }, (r, i) => new { Sum = r.Sum + i, Count = r.Count + 1 }, r => r.Sum / (double)r.Count);
    }

    // Could use a nominable mutable type definition to avoid the readonly anonymous type re-allocation:
    //public class Int32SumCountHolder
    //{
    //    public int Sum { get; private set; }
    //    public int Count { get; private set; }
    //    public double? Average => Count > 0 ? Sum / (double)Count : new double?();
    //    public Int32SumCountHolder(int sum = 0, int count = 0)
    //    {
    //        Sum = sum;
    //        Count = count;
    //    }
    //    public Int32SumCountHolder Increase(int value)
    //    {
    //        Sum += value;
    //        Count ++;
    //        return this;
    //    }
    //}
    public static double AggregateAverage<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
    {
        return source.Aggregate(new { Sum = 0, Count = 0 }, (r, i) => new { Sum = r.Sum + selector(i), Count = r.Count + 1 }, r => r.Sum / (double)r.Count);
    }

    public static TSource AggregateMin<TSource>(this IEnumerable<TSource> source)
    {
        var comparer = Comparer<TSource>.Default;
        return source.Aggregate((r, i) => comparer.Compare(i, r) < 0 ? i : r);
    }

    public static TResult AggregateMin<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        var comparer = Comparer<TResult>.Default;
        return source.Select(selector).Aggregate((r, i) => comparer.Compare(i, r) < 0 ? i : r);
    }

    public static TSource AggregateMax<TSource>(this IEnumerable<TSource> source)
    {
        var comparer = Comparer<TSource>.Default;
        return source.Aggregate((r, i) => comparer.Compare(i, r) > 0 ? i : r);
    }

    public static TResult AggregateMax<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
    {
        var comparer = Comparer<TResult>.Default;
        return source.Select(selector).Aggregate((r, i) => comparer.Compare(i, r) > 0 ? i : r);
    }

    public static bool AggregateAll<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source.Aggregate(true, (r, i) => r && predicate(i));
    }

    public static bool AggregateAny<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        // How to make the Aggregate call stop whenever the predicate is okay rather than looping over the whole source?
        return source.Aggregate(false, (r, i) => r || predicate(i));
    }

    public static bool AggregateContains<TSource>(this IEnumerable<TSource> source, TSource value)
    {
        return source.AggregateContains(value, EqualityComparer<TSource>.Default);
    }

    public static bool AggregateContains<TSource>(this IEnumerable<TSource> source, TSource value, IEqualityComparer<TSource> comparer)
    {
        // Same as for AggregateAny... how to make it stop earlier?
        return source.Aggregate(false, (r, i) => r || comparer.Equals(i, value));
    }

    public static TSource AggregateFirst<TSource>(this IEnumerable<TSource> source)
    {
        // Same as for AggregateContains... how to make it stop earlier?
        return source.Aggregate(default(Tuple<TSource>), (r, i) => r ?? new Tuple<TSource>(i)).Item1;
    }

    public static TSource AggregateFirstOrDefault<TSource>(this IEnumerable<TSource> source)
    {
        var result = source.Aggregate(default(Tuple<TSource>), (r, i) => r ?? new Tuple<TSource>(i));

        return result == null ? default(TSource) : result.Item1;
    }

    public static TSource AggregateFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        // Same as for above...
        return source.Aggregate(default(Tuple<TSource>), (r, i) => predicate(i) && r == null ? new Tuple<TSource>(i) : r).Item1;
    }

    public static TSource AggregateFirstOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        // Same as for above...
        var result = source.Aggregate(default(Tuple<TSource>), (r, i) => predicate(i) && r == null ? new Tuple<TSource>(i) : r);

        return result == null ? default(TSource) : result.Item1;
    }

    public static TSource AggregateLast<TSource>(this IEnumerable<TSource> source)
    {
        return source.Aggregate((r, i) => i);
    }

    public static TSource AggregateLast<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source.Aggregate(default(Tuple<TSource>), (r, i) => predicate(i) ? new Tuple<TSource>(i) : r).Item1;
    }

    public static TSource AggregateLastOrDefault<TSource>(this IEnumerable<TSource> source)
    {
        return source.Aggregate(default(TSource), (r, i) => i);
    }

    public static TSource AggregateLastOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        return source.Aggregate(default(TSource), (r, i) => predicate(i) ? i : r);
    }

}

0 ответов

Другие вопросы по тегам