Просто наивное использование Seq.length
может быть недостаточно для взрыва бесконечных последовательностей.
Будет более интересно использовать что-то вроде ss |> Seq.truncate n |> Seq.length
, но за кулисами может потребоваться двойной обход фрагмента последовательности аргументов в MoveNext()
.
IEnumerator.
Лучший подход, который мне удалось найти, это:
let hasAtLeast n (ss: seq<_>) =
let mutable result = true
use e = ss.GetEnumerator()
for _ in 1 .. n do result <- e.MoveNext()
result
Это включает в себя только один обход последовательности (точнее, выполнение e.MoveNext()
n
раз) и правильно обрабатывает граничные случаи пустых и бесконечных последовательностей. Кроме того, я могу добавить несколько небольших улучшений, таких как явная обработка конкретных случаев для списков, массивов и ICollection
s, или некоторая обработка длины хода, но мне интересно, существует ли какой-либо более эффективный подход к проблеме, который я могу пропустить?
Спасибо за вашу помощь.
РЕДАКТИРОВАТЬ : Наличие под рукой 5 общих вариантов реализации функции hasAtLeast
(2 моих собственных, 2 предложенных Daniel и один предложенный Ankur ) Я устроил марафон между ними. Результаты, которые связаны для всех реализаций, доказывают, что Guvante верен: простейшая комбинация существующих алгоритмов была бы лучшей, здесь нет никакого смысла в перепроектировании.
Далее, добавив коэффициент читабельности, я бы использовал либо мой собственный чистый F # на основе
let hasAtLeast n (ss: seq<_>) =
Seq.length (Seq.truncate n ss) >= n
или предложенный Ankur полностью эквивалентный, основанный на Linq, который использует интеграцию .NET
let hasAtLeast n (ss: seq<_>) =
ss.Take(n).Count() >= n