Насколько интеллектуален Linq с IOrderedEnumerable и Where
Так скажем, у меня есть IEnumerable даты, и я хочу получить даты в пределах диапазона.
Являются ли Linq и IOrderedEnumerable достаточно умными, чтобы реализовать возможность выбирать более быстрые алгоритмы, учитывая, что даты теперь упорядочены.
Итак, рассмотрим это:
IOrderedEnumerable<ObjectWith2DateTimes> orderedDates = dates.OrderBy(x => new Tuple(x.datetime1,x.datetime2));
...
this is called a bunch
...
DateTime afterDateTime = (some datetime)
DateTime beforeDateTime = (other datetime)
yield return orderedDates.Where(x => x.datetime1 >= afterDateTime && x.datetime2 <= beforeDateTime)
Если не умный с этой реализацией, есть ли какая-то другая реализация, которая сделала бы это умным?
4 ответа
Нет, это не достаточно умно, чтобы сделать это. Enumerable.Where
Тем не менее, что-то вроде этого должно сделать:
orderedDates
.SkipWhile(x => x.datetime1 < afterDateTime)
.TakeWhile(x => x.datetime2 > beforeDateTime)
Нет; Where
оператор не знает, что делает ваш предикат; он просто знает, как вызвать делегата, который сообщает ему, включать ли элемент в набор результатов. Для провайдера запросов было бы возможно сделать это с IOrderedQueryable
, поскольку поставщик запросов сможет анализировать дерево выражений. Я не знаю, действительно ли какие-либо поставщики запросов проводят такой анализ.
LINQ-to-objects работает с делегатами и рассматривает их как черные ящики. Поэтому он не знает, что ваше условие where имеет какое-либо отношение к более ранней операции заказа. Так что он не выполняет эту оптимизацию.
Это даже хуже, сортировка будет выполняться каждый раз, когда вы оцениваете запрос, даже если он является общим. Вы должны материализовать это с ToArray
или же ToList
, Затем вы можете использовать встроенную функцию двоичного поиска, чтобы ускорить это.
Нет. Where
Метод extension выполняет простой линейный поиск по всем элементам в вашей коллекции и возвращает элемент, если он следует вашему предикату. Вы можете думать об этом как:
foreach(var item in source)
if(predicate(item))
yield return item;