nest yields to return IEnumerable> with lazy evaluation

前端 未结 4 2065
忘掉有多难
忘掉有多难 2021-02-13 16:40

I wrote a LINQ extension method SplitBetween analogous to String.Split.

> new List(){3,4,2,21,3,2,17,16,1}
> .SplitBet         


        
4条回答
  •  死守一世寂寞
    2021-02-13 17:15

    This one won't use List<>, and won't go BOOM.

    public static IEnumerable> SplitBetween(this IEnumerable source,
                                                              Func separatorSelector, 
                                                              bool includeSeparators=false) 
    {
        if (source == null)
            throw new ArgumentNullException("source");
    
        return SplitBetweenImpl(source, separatorSelector, includeSeparators);
    }
    
    private static IEnumerable SplitBetweenInner(IEnumerator e,
                                                       Func separatorSelector)
    {
        var first = true;
    
        while(first || e.MoveNext())
        {
            if (separatorSelector((T)e.Current))
                yield break;    
    
            first = false;
            yield return e.Current;
        }
    }
    
    private static IEnumerable> SplitBetweenImpl(this IEnumerable source,
                                                                   Func separatorSelector, 
                                                                   bool includeSeparators) 
    {
        using (var e = source.GetEnumerator())
            while(e.MoveNext())
            {
                if (separatorSelector((T)e.Current) && includeSeparators)
                    yield return new T[] {(T)e.Current};
                else
                    {
                    yield return SplitBetweenInner(e, separatorSelector);
                    if (separatorSelector((T)e.Current) && includeSeparators)
                        yield return new T[] {(T)e.Current};
                    }
            }
    }
    

    Test:

    void Main()
    {
        var list = new List(){1, 2, 3, 10, 1};
        foreach(var col in list.Concat(Ext.ThrowingEnumerable())
                               .SplitBetween(x=>x>=10).Take(1))
        {
            Console.WriteLine("------");
            foreach(var i in col)
                Console.WriteLine(i);
        }
    }
    

    Output:

    ------
    1
    2
    3
    

    Test2

    var list = new List(){1, 2, 3, 10, 1}
    foreach(var col in list.Concat(Ext.ThrowingEnumerable())
                           .SplitBetween(x=>x>=10).Take(2))
    

    Output:

    ------
    1
    2
    3
    ------
    1
    *Exception*
    

    Here, the exception is caused because the first element of the ThrowingEnumerable-enumeration would go into the same group as the 1.


    Test3:

    var list = new List(){1, 2, 3, 10, 1, 17};
    foreach(var col in list.Concat(Ext.ThrowingEnumerable())
                           .SplitBetween(x=>x>=10, true).Take(4))
    

    Output:

    ------
    1
    2
    3
    ------
    10
    ------
    1
    ------
    17
    

    No problem here, because the Exception element would go into it's own group, and thus is not iterated over due to Take(4):

提交回复
热议问题