屈服,回报,大脑冻结
本文关键字:冻结 回报 屈服 | 更新日期: 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;