iwburns / id-tree

A library for creating and modifying Tree structures.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Is there a better name for swap_sub_tree?

iwburns opened this issue · comments

I should have brought this up during our discussion in #6, but I forgot to, so this is my fault. Luckily, we should be able to fit a change like this into the 1.0.0 release if it is deemed necessary.

I'm thinking at the very least it should be swap_sub_trees (plural trees) instead of swap_sub_tree (singular tree) since there are two potential sub-trees being swapped.

I'm also thinking it would be nice to have a version of swap_sub_trees that doesn't move any children around with it. With that in mind, there might be a better set of names that we can come up with that would have the same prefix, but have a different suffix for each behavior. This would be similar to how remove_node_drop_children and remove_node_lift_children are named.

@Drakulix do you have any thoughts on this?

I also though about implementing a swap version that does not change the children.
But what does it change then? Basically only the data. (And it should probably swap the node ids, if implemented as a separate function).

And that is already available via replace_data or plain mem::swap. I am not against adding this functionality, but I have no strong motivation to do so.

Yeah, you're correct, it's really almost not worth worrying about it right now.

Plus, it also introduces strange issues where if you have a copy of Node A's children in the calling context and then you swap A with B, now that Vec<&NodeId> doesn't actually contain A's children anymore.

I think we should put that function off for now, but I would still like to name swap_sub_trees something else. I would like it to be something that will allow for us to add the other swap function later if we need to. Needs to be something such that they have similar names and can be easily associated with each other.

Not saying you have to come up with a name, but if something comes to you, please feel free to shout it out.

I though about this for some time and I think the swap prefix is actually quite fitting, but we should maybe rename it to swap_nodes_with_children.
If you want to add a function, that only swaps the node, call it swap_node, if you want something that swaps only the children call it swap_children.

I can think of any other swap right now, because swap_parents would just be a confusing name for swap_nodes_with_children.
Basically you want to find names for these three functions and then think about how to name all of them, so the naming is consistent.

I may have a better solution to this in #18

@Drakulix I know it has been a while since we had this discussion, but I wanted to follow up on it with a few thoughts and see where you're standing on this.

What we have right now in #22 seems to be everything that we've decided to do for the 1.0.0 release except for this issue. We've got insert, remove_node, and move_node all set up with their different behavior enums, and the return types for get and get_mut are now consistent with the rest of the API.

At some point I think you mentioned that you found a use case for swapping two Nodes but leaving their children where they are. In other words, calling swap_nodes_leave_children(B,C) on:

  A
 / \
B   C
|   |
D   E

becomes:

  A
 / \
C   B
|   |
D   E

I took a quick look around but didn't find that comment anywhere (I'm sure I just missed it). Do you still think the above would be useful?

At this point, I'm thinking one option is to have a swap_nodes method that takes two NodeIds and a SwapBehavior enum:

pub enum SwapBehavior {
    WithChildren,
    WithoutChildren,
    //maybe more options here, but I doubt it
}

That would be nice because it fits into the behavior enum system nicely, but I'm a little worried that the types of behaviors we're talking about here (or might want to support in the future) might get too complex. In that case, it would just be better to have multiple methods so that we can change the method signature as necessary between the different behaviors.

If we go with different methods instead of behavior enums for this one, I'm thinking we should go with swap_sub_trees and swap_nodes. The former will do exactly what swap_sub_tree does right now (just renaming it by adding an s) and the latter will just perform the behavior that I drew out above.

No rush on getting to this, and (as always) I appreciate your time and your input very much!

Difficult choice.

but I'm a little worried that the types of behaviors we're talking about here (or might want to support in the future) might get too complex.

That is a perfectly good reason, but I am wondering, if it is just the implementation that is incredibly complex (and is internally split into multiple functions anyway) or if the behavior itself is also complex and difficult to grasp.

it would just be better to have multiple methods so that we can change the method signature as necessary
Also more related to the complexity of the action taken, then the actual implementation.

That said, I cannot think about any swap function, that has more arguments then two nodes getting swapped. (I also cannot think about more behaviors then outlined already.)

I am not decided, what I think is a better option. I just wanted to point out, that we should think about method signature and difficulty of understanding the behaviors only.

Just because swap_sub_tree is one of our most complex functions with a lot of edge cases in the code, it does not mean, that this behavior is any more difficult to use by potential library users.

These thoughts should be done for all new enum-behaviors.
I think is an important topic, I am just questioning, why this question only pops up for swap_sub_trees.

if it is just the implementation that is incredibly complex (...) or if the behavior itself is also complex and difficult to grasp.

Good question! I think the behavior might be a bit hard to understand (especially to newcomers) in certain cases (for example, when swapping nodes where one is in the sub-tree of the other), but pretty simple in other cases ("across"-the-tree swaps).

I cannot think about any swap function, that has more arguments then two nodes getting swapped. (I also cannot think about more behaviors then outlined already.)

I agree. The only other behavior that was discussed above is one where the children are swapped but not the nodes themselves.

I just wanted to point out, that we should think about method signature and difficulty of understanding the behaviors only.

This is a good point, and you're right that we need to think about this for all of the new enum-behaviors before "really" going forward with them.

I think the reason this only came to mind for swap_sub_trees is that it seems like a more complex and hairy method than the others do because of my knowledge of the implementation complexity. But, as you pointed out, that doesn't really have anything to do with the API design.

Just to go ahead and list them out so we can discuss them:

  • insert: I can't really think of any other types of behaviors that we would want for this one, and I especially can't think of any that would change the method signature. Inserting into any location in the Tree can be formulated as "under" an existing Node, or as the Root of the Tree. It doesn't make sense to Insert "above" or "next-to" another Node, because that could just as easily be formulated as an Insert "under" some other Node.
    The only other option is changing where the Node ends up in it's parent's children array, but even that wouldn't change the method signature.
  • remove: There's really nothing more to do here either. The only variation in behavior is what you do with the children of the Node that is removed, and we've covered all of the options I can think of there. So I don't really foresee any method signature changes here either.
  • move_node: Similar to insert I think you can describe any move to any location in the Tree as a move "under" an existing Node or a move to the root of the Tree. I can't think of any other behaviors that could be implemented here. Again, I don't foresee any method signature changes here.
  • swap_nodes: With this one, I think the three options (with children, without children, children only) that we've discussed are really the only ones that would be expected/useful in a library like this. And there's no behavior that I can think of that would require a change in method signature for this. You're always going to want exactly two NodeIds and some form of behavior for what exactly you want to happen with the children (similar but not quite exactly the same as remove).

Sorry for the wall of text, but when you get a chance, take a look and let me know what you think.

I completely agree on all four functions. So whats your conclusion? Implementing enum-behaviors on swap, right?

Yes, I think that's the right choice. To be honest, I'm not really looking forward to implementing those behaviors, but I think they're fine as far as the API goes.

I'll get started on it at some point soon... although, just FYI, I may not be able to get started on it until next week because I'm actually going to be pretty busy tonight and this coming weekend.

If you have time and the will-power/determination feel free to take a whack at it, but I will completely understand if you'd rather not deal with it.

I would if I would not currently be extremely busy. And the 1.0 release will mostly be a quality-of-life improvement, not really required, so I have other priorities at the moment.
If I find some time, I'll let you know.

I totally understand, no worries! And yeah, just let me know if you feel like going for it, otherwise I'll get to it some time next week.