什么是适用以下使用LINQ序列中的最可读的方式:LINQ到对象:TakeWhileOrFirst
TakeWhile elements are valid but always at least the first element
编辑:我已经更新了标题,更精确。我很抱歉有什么困惑,下面的答案肯定会教给我一些东西!
预期的行为是这样的:Take while元素有效。如果结果是一个空序列,无论如何要采取第一个元素。
什么是适用以下使用LINQ序列中的最可读的方式:LINQ到对象:TakeWhileOrFirst
TakeWhile elements are valid but always at least the first element
编辑:我已经更新了标题,更精确。我很抱歉有什么困惑,下面的答案肯定会教给我一些东西!
预期的行为是这样的:Take 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;
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;
}
}
}
以下工作*,似乎相当不错可读的对我说:
seq.Take(1).Concat(seq.TakeWhile(condition).Skip(1));
有可能是一个更好的办法,不知道。
*与感谢@Jeff米为校正
你需要一个`IEnumerable <>`来指定`Concat()`。我会用`Take(1)`而不是`First()`。 – 2011-02-08 23:41:24
@Rune:好点! – asgerhallas 2011-02-09 09:25:21
@Rune:请注意,我跳过采集集合中的第一个值,而不是跳过集合中的第一个值,然后在匹配条件时进行。如果第一个是不匹配的并且以下是,则只返回第一个。 – 2011-02-09 20:01:10
我想,这使得意图非常明确:
things.TakeWhile(x => x.Whatever).DefaultIfEmpty(things.First());
我更早,更详细的解决方案:
var query = things.TakeWhile(x => x.Whatever);
if (!query.Any()) { query = things.Take(1); }
免责声明: 这是杰夫女士好的答案的变化,因此只是为了展示代码使用do-while代替。它仅作为Jeffs答案的扩展提供。
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));
}
}
当然
这是一个风格问题,我个人喜欢有我的条件逻辑是可能的,但双用谓词可能很难把握,可以是一个轻微的性能猪(视的尽可能低的嵌套级别在优化和分支预测)
我已经更新了这个问题。查看修改。对困惑感到抱歉。 – asgerhallas 2011-02-09 09:52:02