Рекурсивное лямбда-выражение для обхода дерева в C#

StackOverflow https://stackoverflow.com/questions/61143

  •  09-06-2019
  •  | 
  •  

Вопрос

Может ли кто-нибудь показать мне, как реализовать рекурсивное лямбда-выражение для обхода древовидной структуры в C#.

Это было полезно?

Решение

Хорошо, наконец-то я нашел немного свободного времени.
Вот так:

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);

Другие советы

Правильным решением и даже идиоматическим решением во многих функциональных языках программирования было бы использование комбинатор с фиксированной точкой.В двух словах:комбинатор с фиксированной точкой отвечает на вопрос «как определить рекурсивную анонимную функцию?».Но решение настолько нетривиально, что для его объяснения написаны целые статьи.

Простая и прагматичная альтернатива — «вернуться в прошлое» к выходкам C:декларация перед определением.Попробуйте следующее (функция «факториал»):

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);
};

Простая альтернатива — «вернуться в прошлое» к выходкам C и C++:декларация перед определением.Попробуйте следующее:

Func<int, int> fact = null;
fact = x => (x == 0) ? 1 : x * fact(x - 1);

Работает как шарм.

Да, это работает, с одной небольшой оговоркой.В C# есть изменяемые ссылки.Поэтому убедитесь, что вы случайно не сделали что-то вроде этого:

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

Конечно, этот пример немного надуман, но такое может произойти при использовании изменяемых ссылок.Если вы используете комбинаторы из акуссылки, это будет невозможно.

Предположим, что это мифический объект 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);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top