【问题标题】:yield return in the lambda expression?在 lambda 表达式中产生返回?
【发布时间】:2016-05-30 03:29:22
【问题描述】:

以下代码会将列表分成以“[”开头并以“]”结尾的子列表。如何将其转换为使用yield return,以便它可以懒惰地处理非常大的流输入? --或者如何在 F# 中通过惰性枚举来实现它?--(没关系,我认为 f#implementation 应该是微不足道的)

var list = new List<string> { "[", "1", "2", "3", "]", "[", "2", "2", "]", "[", "3", "]" };
IEnumerable<IEnumerable<string>> result = Split(list);

static IEnumerable<IEnumerable<string>> Split(List<string> list)
{
    return list.Aggregate(new List<List<string>>(), // yield return?
    (sum, current) =>
    {
        if (current == "[")
            sum.Add(new List<string>());
        else if (current == "]")
            return sum; // Convert to yield return?
        else
            sum.Last().Add(current);
        return sum; // Convert to yield return?
    });
}

【问题讨论】:

  • 感觉聚合在这里使用不太合适。编写你自己的版本,然后将逻辑叠加在上面。
  • @leppie Aggregate 的问题在于他实际上并没有聚合任何东西。 lambda 仅用于它的副作用,因此它注意到的不仅仅是一个更难阅读的foreach 循环。

标签: c# linq


【解决方案1】:

您的任务最简单的解决方案是这种方法:

IEnumerable<IEnumerable<string>> SplitList(IEnumerable<string> list, string open = @"[", string close = @"]") {
    Dictionary<int, List<string>> splitedList = new Dictionary<int, List<string>>();
    int i = 0;            
    foreach (var s in list) {
        if (open.Equals(s))
            splitedList.Add(i, new List<string>());
        else if (close.Equals(s))
            i++;
        else
            splitedList[i].Add(s);
    }
    return splitedList.Values;
}

【讨论】:

  • 你没有理解实际的问题。
【解决方案2】:

虽然 C# 不允许在 lambda 中使用 yield,但我认为我们可以这样解决

static IEnumerable<IEnumerable<string>> Split(List<string> list)
{
    var tmp = list as IEnumerable<string>;
    while(tmp.FirstOrDefault() == "[")
    {
        yield return tmp.TakeWhile((current) => current != "]");
        tmp = tmp.SkipWhile((current) => current != "]");
    }
}

【讨论】:

    【解决方案3】:

    我找到了另一种方法,使用Linq。不确定效率,但它有效。

    int index=0;
    list.Select(item=> new { item=item, index= item=="]"? ++index : index })
                      .Where(c => !(c.item == "[" || c.item =="]"))
                      .GroupBy(g=>g.index)
                      .Select(e=> e.Select(c=>c.item));
    

    工作Demo

    【讨论】:

    • 这不是懒惰地生成序列。 Groupby 将需要处理 整个 序列,然后才能产生任何结果,OP 表示他们不想这样做。
    【解决方案4】:

    C# 不支持匿名迭代器块,因此您只需使用命名方法而不是匿名方法。

    public static IEnumerable<IEnumerable<string>> Split(IEnumerable<string> tokens)
    {
        using(var iterator = tokens.GetEnumerator())
            while(iterator.MoveNext())
                if(iterator.Current == "[")
                    yield return SplitGroup(iterator);
    }
    
    public static IEnumerable<string> SplitGroup(
        IEnumerator<string> iterator)
    {
        while(iterator.MoveNext() && iterator.Current != "]")
            yield return iterator.Current;
    }
    
    猜你喜欢
    • 2019-09-19
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多