寻找特定的Enumerable运算符序列:TakeWhile(!)+ Concat Single

sco*_*obi 6 c# linq

给定一个Enumerable,我希望Take()所有元素都包括一个终结符(如果找不到终结符则抛出异常).就像是:

list.TakeWhile(v => !condition(v)).Concat(list.Single(condition))
Run Code Online (Sandbox Code Playgroud)

..除了不蹩脚.只想走一次.

这对于.NET 4和Rx中的当前运算符来说是否简洁,或者我是否需要编写新的运算符?

编写运算符会比写这个问题花费更少的时间(尽管我认为一半的时间会弄清楚这个函数的名称),但我只是不想复制那些已经存在的东西.

更新

好的,这是运营商.非常令人兴奋,我知道.无论如何,可以从内置运营商构建它吗?

    public static IEnumerable<T> TakeThroughTerminator<T>([NotNull] this IEnumerable<T> @this, Func<T, bool> isTerminatorTester)
    {
        foreach (var item in @this)
        {
            yield return item;
            if (isTerminatorTester(item))
            {
                yield break;
            }
        }

        throw new InvalidOperationException("Terminator not found in list");
    }
Run Code Online (Sandbox Code Playgroud)

Jef*_*ado 2

没有内置函数可以有效地执行此类操作。人们并不经常需要同时获得满足某个条件的物品和另一件不满足条件的物品。你必须自己写。

然而,您可以使用现有方法来构建它,但它不会那么高效,因为您需要以某种方式保持状态只会使代码复杂化。我不会容忍这种查询,因为它违背了 LINQ 的哲学,并且我会自己编写它。但既然你问了:

var list = Enumerable.Range(0, 10);
Func<int, bool> condition = i => i != 5;
int needed = 1;
var query = list.Where(item => condition(item)
                                   ? needed > 0
                                   : needed-- > 0)
                .ToList(); // this might cause problems
if (needed != 0)
    throw new InvalidOperationException("Sequence is not properly terminated");
Run Code Online (Sandbox Code Playgroud)

然而,这有其自身的问题,无法真正得到很好的解决。处理这个问题的正确方法是手动编码(不使用 LINQ)。这会给你完全相同的行为。

public static IEnumerable<TSource> TakeWhileSingleTerminated<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, bool> predicate)
{
    var hasTerminator = false;
    var terminator = default(TSource);
    foreach (var item in source)
    {
        if (!hasFailed)
        {
            if (predicate(item))
                yield return item;
            else
            {
                hasTerminator = true;
                terminator = item;
            }
        }
        else if (!predicate(item))
            throw new InvalidOperationException("Sequence contains more than one terminator");
    }
    if (!hasTerminator)
        throw new InvalidOperationException("Sequence is not terminated");
    yield return terminator;
}
Run Code Online (Sandbox Code Playgroud)

经过深思熟虑后,我想说很难获得原始查询的最有效实现,因为它具有冲突的需求。你正在混合TakeWhile()哪个提前终止,Single()哪个不能终止。可以复制最终结果(正如我们都在这里尝试过的那样),但如果不对代码进行重大更改,则行为无法复制。如果目标是仅获取第一个失败的项目,那么这将是完全可能且可复制的,但是由于事实并非如此,因此您只需处理此查询所存在的问题。

ps,我认为仅通过我对这个答案所做的编辑次数就可以看出这是多么不平凡。希望这是我最后一次编辑。