Оновлення:
Для людей, яких цікавить рівень вкладеності (глибина). Однією з хороших речей явної реалізації стеку перечислювача є те, що в будь-який момент (і, особливо, при виведенні елемента), stack.Count
репрезентує поточну глибину обробки. Отже, беручи це до уваги та використовуючи кортежі значень C # 7.0, ми можемо просто змінити декларацію методу наступним чином:
public static IEnumerable<(T Item, int Level)> ExpandWithLevel<T>(
this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
та yield
заява:
yield return (item, stack.Count);
Тоді ми можемо реалізувати оригінальний метод, застосувавши простий Select
наведений вище:
public static IEnumerable<T> Expand<T>(
this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector) =>
source.ExpandWithLevel(elementSelector).Select(e => e.Item);
Оригінал:
Дивно, але ніхто (навіть Ерік) не показав "природний" ітераційний порт рекурсивного попереднього замовлення DFT, тож ось:
public static IEnumerable<T> Expand<T>(
this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
{
var stack = new Stack<IEnumerator<T>>();
var e = source.GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
yield return item;
var elements = elementSelector(item);
if (elements == null) continue;
stack.Push(e);
e = elements.GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}