Объединение в единое целое LINQ-подобных запросов - PullRequest
0 голосов
/ 29 июня 2019

Я хотел создать свободный API для итерации в массиве, где я фильтрую значения и продолжаю обрабатывать оставшиеся (не отфильтрованные) значения. Примерно такой псевдокод:

int[] input = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
from a in Take(3) // a = {5,4,1}
from b in Skip(4) // b = null
from c in TakeWhile(x=> x != 0) // c = {7, 2}
select new Stuff(a, b, c)

Я не знаю, с чего начать, на чем основано подобное. Поэтому я хотел попросить о помощи.

Система не должна быть ограничена целыми числами .. другой пример:

string[] input = { "how", "are", "you", "doing", "?" };
from a in OneOf("how", "what", "where") // a = "how"
from b in Match("are") // b = "are"
from c in TakeWhile(x=> x != "?") // c = { "you", "doing" }
select new Stuff(a, b, c)

1 Ответ

1 голос
/ 29 июня 2019

Следующий код позволит вам сделать input.FirstTake(3).ThenSkip(4).ThenTakeWhile(x => x != 0);, чтобы получить последовательность 5, 4, 1, 7, 2. Основная идея заключается в том, что вам нужно следить за дублями и пропусками, которые вы хотите сделать, чтобы они моглиприменяться, когда вы повторяете.Это похоже на работу OrderBy и ThenBy.Обратите внимание, что вы не можете выполнять другие операции Linq между ними.Это создает одно перечисление последовательных пропусков и дублей, после чего эта последовательность будет передаваться через любые операции Linq, которые вы выполняете.

public interface ITakeAndSkip<out T> : IEnumerable<T>
{
    ITakeAndSkip<T> ThenSkip(int number);
    ITakeAndSkip<T> ThenTake(int number);
    ITakeAndSkip<T> ThenTakeWhile(Func<T, bool> predicate);
    ITakeAndSkip<T> ThenSkipWhile(Func<T, bool> predicate);
}

public class TakeAndSkip<T> : ITakeAndSkip<T>
{
    private readonly IEnumerable<T> _source;

    private class TakeOrSkipOperation
    {
        public bool IsSkip { get; private set; }
        public Func<T, bool> Predicate { get; private set; }
        public int Number { get; private set; }

        private TakeOrSkipOperation()
        {
        }

        public static TakeOrSkipOperation Skip(int number)
        {
            return new TakeOrSkipOperation
            {
                IsSkip = true,
                Number = number
            };
        }

        public static TakeOrSkipOperation Take(int number)
        {
            return new TakeOrSkipOperation
            {
                Number = number
            };
        }


        public static TakeOrSkipOperation SkipWhile(Func<T, bool> predicate)
        {
            return new TakeOrSkipOperation
            {
                IsSkip = true,
                Predicate = predicate
            };
        }

        public static TakeOrSkipOperation TakeWhile(Func<T, bool> predicate)
        {
            return new TakeOrSkipOperation
            {
                Predicate = predicate
            };
        }
    }

    private readonly List<TakeOrSkipOperation> _operations = new List<TakeOrSkipOperation>();

    public TakeAndSkip(IEnumerable<T> source)
    {
        _source = source;
    }

    public IEnumerator<T> GetEnumerator()
    {
        using (var enumerator = _source.GetEnumerator())
        {
            // move to the first item and if there are none just return
            if (!enumerator.MoveNext()) yield break;

            // Then apply all the skip and take operations
            foreach (var operation in _operations)
            {
                int n = operation.Number;
                // If we are not dealing with a while then make the predicate count
                // down the number to zero.
                var predicate = operation.Predicate ?? (x => n-- > 0);

                // Iterate the items until there are no more or the predicate is false
                bool more = true;
                while (more && predicate(enumerator.Current))
                {
                    // If this is a Take then yield the current item.
                    if (!operation.IsSkip) yield return enumerator.Current;
                    more = enumerator.MoveNext();
                }

                // If there are no more items return
                if (!more) yield break;
            }

            // Now we need to decide what to do with the rest of the items. 
            // If there are no operations or the last one was a skip then
            // return the remaining items
            if (_operations.Count == 0 || _operations.Last().IsSkip)
            {
                do
                {
                    yield return enumerator.Current;
                } while (enumerator.MoveNext());
            }

            // Otherwise the last operation was a take and we're done.
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public ITakeAndSkip<T> ThenSkip(int number)
    {
        _operations.Add(TakeOrSkipOperation.Skip(number));
        return this;
    }

    public ITakeAndSkip<T> ThenSkipWhile(Func<T, bool> predicate)
    {
        _operations.Add(TakeOrSkipOperation.SkipWhile(predicate));
        return this;
    }

    public ITakeAndSkip<T> ThenTake(int number)
    {
        _operations.Add(TakeOrSkipOperation.Take(number));
        return this;
    }

    public ITakeAndSkip<T> ThenTakeWhile(Func<T, bool> predicate)
    {
        _operations.Add(TakeOrSkipOperation.TakeWhile(predicate));
        return this;
    }
}

public static class TakeAndSkipExtensions
{
    public static ITakeAndSkip<T> FirstTake<T>(this IEnumerable<T> source, int number)
    {
        return new TakeAndSkip<T>(source).ThenTake(number);
    }

    public static ITakeAndSkip<T> FirstSkip<T>(this IEnumerable<T> source, int number)
    {
        return new TakeAndSkip<T>(source).ThenSkip(number);
    }

    public static ITakeAndSkip<T> FirstTakeWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        return new TakeAndSkip<T>(source).ThenTakeWhile(predicate);
    }

    public static ITakeAndSkip<T> FirstSkipWhile<T>(this IEnumerable<T> source, Func<T, bool> predicate)
    {
        return new TakeAndSkip<T>(source).ThenSkipWhile(predicate);
    }
}
...