Линк к объектам: TakeWhileOrFirst - PullRequest
4 голосов
/ 09 февраля 2011

Какой самый понятный способ применить следующее к последовательности с использованием linq:

TakeWhile elements are valid but always at least the first element

РЕДАКТИРОВАТЬ: Я уточнил название, чтобы быть более точным.Я прошу прощения за любую путаницу, ответы ниже определенно научили меня чему-то!

Ожидаемое поведение таково: принять, пока элемент действителен.Если результат - пустая последовательность, все равно возьмите первый элемент.

Ответы [ 4 ]

5 голосов
/ 09 февраля 2011

Я думаю, что это делает намерение совершенно ясным:

things.TakeWhile(x => x.Whatever).DefaultIfEmpty(things.First());

Мое ранее, более подробное решение:

var query = things.TakeWhile(x => x.Whatever);
if (!query.Any()) { query = things.Take(1); }
4 голосов
/ 09 февраля 2011

Следующее работает * и кажется мне вполне читабельным:

seq.Take(1).Concat(seq.TakeWhile(condition).Skip(1));

Возможно, есть лучший способ, не уверен.

* с благодарностью @Jeff M за исправление

1 голос
/ 09 февраля 2011

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Это вариант приятного ответа Джеффа Мс, и поэтому он предназначен только для показа кода с использованием do-while.Он предоставляется только в качестве дополнения к ответу Джеффса.

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            yield break;
        var current = enumerator.Current; 
        do{
            yield return current
        } while (predicate(current) && 
                 enumerator.MoveNext() && 
                 predicate(current = enumerator.Current) );
    }
}

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

1 голос
/ 09 февраля 2011

Это будет наиболее эффективно реализовано вручную, насколько я могу судить, чтобы гарантировать, что оно не будет перечислено более, чем необходимо.

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            yield break;
        TSource current = enumerator.Current;
        yield return current;
        if (predicate(current))
        {
            while (enumerator.MoveNext() && predicate(current = enumerator.Current))
                yield return current;
        }
    }
}

И для завершения, перегрузка, которая включает в себяиндекс:

public static IEnumerable<TSource> TakeWhileOrFirst<TSource>(this IEnumerable<TSource> source, Func<TSource, int, bool> predicate)
{
    using (var enumerator = source.GetEnumerator())
    {
        if (!enumerator.MoveNext())
            yield break;
        TSource current = enumerator.Current;
        int index = 0;
        yield return current;
        if (predicate(current, index++))
        {
            while (enumerator.MoveNext() && predicate(current = enumerator.Current, index++))
                yield return current;
        }
    }
}
...