屈服,回报,大脑冻结

本文关键字:冻结 回报 屈服 | 更新日期: 2023-09-27 18:01:34

假设我有这样一个方法:

IEnumerable<record> GetSomeRecords()
{
  while(...)
  {
    yield return aRecord
  }
}

现在,假设我有一个调用者,它也返回一个相同类型的可枚举对象,就像这样

IEnumerable<record> ParentGetSomeRecords()
{
  // I want to do this, but for some reason, my brain locks right here
  foreach(item in someItems)
    yield return GetSomeRecords();
}

这段代码出现了语法错误因为yield return想要一个记录类型,而我返回的是记录的IEnumerable

我想要一个扁平化的IEnumerable来扁平化一个嵌套的enumerable循环。我快疯了,因为我知道我以前做过这种事,但我不记得是什么了。有什么提示吗?

屈服,回报,大脑冻结

这是你想要的吗?

IEnumerable<record> ParentGetSomeRecords()
{
    foreach(var item in someItems)
        foreach(var record in GetSomeRecords())
            yield return record;
}

如前所述,这只适用于单个级别的子级,但与示例代码最等效。

有些人似乎认为你想要扁平化层次结构的能力。下面是一个扩展方法,它执行宽度优先的平坦化(在子节点之前获得兄弟节点):

来自单个条目:

[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this T source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(!ReferenceEquals(source, null));
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
    var pendingChildren = new List<T> {source};
    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (results != null)
                pendingChildren.AddRange(results);
        }
    }
}

可以这样使用:

record rec = ...;
IEnumerable<record> flattened = rec.BreadthFirstFlatten(r => r.ChildRecords);

这将导致IEnumerable<record>包含rec,所有recs的子节点,所有子节点的子节点,等等。

如果您来自records的集合,请使用以下代码:

[Pure]
private static IEnumerable<T> BreadthFirstFlatten<T, TResult>(IEnumerable<T> source, Func<T, TResult> selector, Action<ICollection<T>, TResult> addMethod)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Requires(addMethod != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
    var pendingChildren = new List<T>(source);
    while (pendingChildren.Any())
    {
        var localPending = pendingChildren.ToList();
        pendingChildren.Clear();
        foreach (var child in localPending)
        {
            yield return child;
            var results = selector(child);
            if (!ReferenceEquals(results, null))
                addMethod(pendingChildren, results);
        }
    }
}
[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.AddRange(arg2));
}
[Pure]
public static IEnumerable<T> BreadthFirstFlatten<T>(this IEnumerable<T> source, Func<T, T> selector)
{
    Contract.Requires(source != null);
    Contract.Requires(selector != null);
    Contract.Ensures(Contract.Result<IEnumerable<T>>() != null);
    return BreadthFirstFlatten(source, selector, (collection, arg2) => collection.Add(arg2));
}

这两个扩展方法可以这样使用:

IEnumerable<records> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ChildRecords);

或者从相反的方向:

IEnumerable<record> records = ...;
IEnumerable<record> flattened = records.BreadthFirstFlatten(r => r.ParentRecords);

所有这些扩展方法都是迭代的,所以不受堆栈大小的限制。

我有很多这种类型的方法,包括预顺序和后顺序深度优先遍历,如果你想看到它们,我会做一个repo并上传到某处:)

如何:

IEnumerable<record> ParentGetSomeRecords()
{
    var nestedEnumerable = <whatever the heck gets your nested set>;
    // SelectMany with an identity flattens 
    // IEnumerable<IEnumerable<T>> to just IEnumerable<T>
    return nestedEnumerable.SelectMany(rec => rec);
}

效率不高,但你可以这样做:

List<Record> rcrdList = new List<Record>();
foreach (var item in someItems)
{
    rcrdList.AddRange(item.GetSomeRecords());
}
return rcrdList;