Может ли кто-нибудь показать мне, как реализовать рекурсивное лямбда-выражение для пересечения древовидной структуры на С#.
Рекурсивное выражение лямбда для пересечения дерева в С#
Ответ 1
Хорошо, наконец я нашел свободное время.
Здесь мы идем:
class TreeNode
{
public string Value { get; set;}
public List<TreeNode> Nodes { get; set;}
public TreeNode()
{
Nodes = new List<TreeNode>();
}
}
Action<TreeNode> traverse = null;
traverse = (n) => { Console.WriteLine(n.Value); n.Nodes.ForEach(traverse);};
var root = new TreeNode { Value = "Root" };
root.Nodes.Add(new TreeNode { Value = "ChildA"} );
root.Nodes[0].Nodes.Add(new TreeNode { Value = "ChildA1" });
root.Nodes[0].Nodes.Add(new TreeNode { Value = "ChildA2" });
root.Nodes.Add(new TreeNode { Value = "ChildB"} );
root.Nodes[1].Nodes.Add(new TreeNode { Value = "ChildB1" });
root.Nodes[1].Nodes.Add(new TreeNode { Value = "ChildB2" });
traverse(root);
Ответ 2
Правильным решением и, действительно, идиоматическим решением во многих функциональных языках программирования было бы использование комбинатора с фиксированной запятой. В двух словах: комбинатор с фиксированной запятой отвечает на вопрос "как определить рекурсивную анонимную функцию?". Но решение настолько нетривиально, что для их объяснения написаны целые статьи.
Простая, прагматичная альтернатива - "вернуться назад во времени" к проделкам C: объявление перед определением. Попробуйте следующее (функция "factorial"):
Func<int, int> fact = null;
fact = x => (x == 0) ? 1 : x * fact(x - 1);
Работает как шарм.
Или, для обхода дерева предварительного заказа на объекте класса TreeNode
который соответствующим образом реализует IEnumerable<TreeNode>
для обхода его потомков:
Action<TreeNode, Action<TreeNode>> preorderTraverse = null;
preorderTraverse = (node, action) => {
action(node);
foreach (var child in node) preorderTraverse(child, action);
};
Ответ 3
Простой альтернативой является "вернуться во времени" к выходкам из C и С++: декларация перед определением. Попробуйте следующее:
Func<int, int> fact = null; fact = x => (x == 0) ? 1 : x * fact(x - 1);
Работает как шарм.
Да, это работает, с небольшим предостережением. С# имеет изменяемые ссылки. Поэтому убедитесь, что вы случайно не сделали что-то вроде этого:
Func<int, int> fact = null;
fact = x => (x == 0) ? 1 : x * fact(x - 1);
// Make a new reference to the factorial function
Func<int, int> myFact = fact;
// Use the new reference to calculate the factorial of 4
myFact(4); // returns 24
// Modify the old reference
fact = x => x;
// Again, use the new reference to calculate
myFact(4); // returns 12
Конечно, этот пример немного надуман, но это может произойти при использовании изменяемых ссылок. Если вы используете комбинаторы из aku, это будет невозможно.
Ответ 4
Предполагая мифический объект TreeItem, который объединяет коллекцию Children для представления вашей иерархии.
public void HandleTreeItems(Action<TreeItem> item, TreeItem parent)
{
if (parent.Children.Count > 0)
{
foreach (TreeItem ti in parent.Children)
{
HandleTreeItems(item, ti);
}
}
item(parent);
}
Теперь, чтобы вызвать его, перейдите в лямбду, которая обрабатывает один элемент, напечатав его имя на консоли.
HandleTreeItems(item => { Console.WriteLine(item.Name); }, TreeItemRoot);