Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
600 views
in Technique[技术] by (71.8m points)

c# - Is there a way to simplify this reverse-enumerator?

We have a case where we have a hierarchical tree structure and we need to get the 'branch' of any particular node. The structure is a one-way linked-list from the child to the parent, but we want to define the branch in the direction from parent to child.

Here's an over-simplified example of the implementation we came up with. Just wondering if there's a better/more effective way to achieve this given those constraints. Just feels verbose to me to do it this way.

#nullable enable

public class Node {

    public Node(String name, Node? parent)
        => (Name, Parent) = (name, parent);

    public string Name   { get; set; }
    public Node?  Parent { get; init; }

    IEnumerable<Node> GetBranch(){
    
        static IEnumerable<Node> getBranchReversed(Node? node) { 
    
            while (node is not null) {
                yield return node;
                node = node.Parent;
            }
        }
    
        return getBranchReversed(this).Reverse();
    }
}

Only other way I can think of is to accumulate into a list where I insert into the first position, then just return the list (typing this from my memory so it may not compile...)

ReadOnlyCollection<Node> GetBranch(){

    Node? node = this;
    var branch = new List<Node>();

    while (node is not null) {
        branch.insert(0, node);
        node = node.Parent;
    }

    return branch.AsReadOnly();
}

Again, just wondering if there's any other way to achieve this.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

To summarize the comments

IEnumerable.Reverse more or less does the following

var buffer = new List<int>(ienumerableToReverse);
for (int i = buffer.count - 1; i >= 0; --i)
    yield return buffer.items[i];

Something to note about this is that it will do the buffering each time the iterator is used. It has to do this since IEnumerable is lazy. If you want to keep the lazy behavior this is just about as good as you can do, whatever solution you want to use, you will need a buffer.

If you do not want the lazy behavior I would probably do the buffering myself:

var branch = new List<Node>();

while (node is not null) {
    branch.Add(node);
    node = node.Parent;
}
branch.Reverse();
return branch;

This should be better than your example since Insert will need to move items around for each insert causing quadratic scaling, while .Reverse is linear.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...