#tree-node #node-tree #tree #node #string #data

data_tree

支持路径和搜索的分层数据树

6个版本

0.1.5 2023年3月14日
0.1.4 2023年3月14日

#998数据结构

每月 47 次下载

MIT 许可证

80KB
1.5K SLoC

数据树

这是一个分层数据树,使用可读的字符串键/路径来导航/搜索节点。

功能

  • 实际上是一个树(我见过一些通过vec模拟'树'的解决方案)
  • 没有'不安全'的代码
  • 单结构库(所有节点都是它们自己树的'根')
  • 详细的路径支持
  • 支持树分析,如获取树中所有可能的路径
  • 不依赖于任何形式的引用计数
  • 使用serde派生节点
  • 显式所有权管理
  • 反向查找
  • 搜索函数(带有回调)
  • 文档包括有关Rust的'初学者提示',供读者了解
  • 非停止(在任何时候,库错误都不会触发panic)
  • 一些函数重复,其中一组支持高性能,而它们的对应函数收集错误数据以帮助调试

库限制

  • 单方向:节点无法存储其父节点(可以从根节点获取父节点)
  • 灵活性和实用性在合理的地方优先于效率;因此,不要期望这个库比更精心设计的竞争对手表现更好
  • 没有自动平衡(对于这种类型的树来说没有意义)

一般备注

此readme不提供所有可用函数的详尽列表。有关更多信息,请参阅文档页面。

如何使用此库

基本示例

//
// Crate
//
use data_tree::Node;
//
// Test area
//
fn main() {
//
// Definitions for the sake of this documentation:
//
// 'key' - This refers to any single-string value, usually used to access a node's
// adjacent child.
//
// 'path' - This is effectively a 'slice of keys.'
//
let path_original = [ "a", "b", "c" ];
//
// All new, immediately configurable, nodes *must* be instantiated from outside
// the tree.
//
let mut node_root = Node::new();
let mut node_child_new = Node::new();
//
// 'Data' stored in the node at 'field_string_data'. This is a dedicated String.
// This allows for easy interoperability with serde, while providing a guaranteed
// data-type for development.
//
let example_data = "test";
node_child_new.field_string_data = example_data.to_string();
//
// Creates any necessary intermediary nodes
// WARNING: Upon passing a node to an 'insert' function, the tree *will*
// take ownership (see 'Design' section for details).
//
// This is necessary keep the tree's flexibility high.
//
match node_root.insert_node_at_path( &path_original, node_child_new ) {
  Ok( () ) => {},
  Err( err ) => panic!( "{}", err )
};
//
// Don't worry. You can get the child back.
//
// The node can now be fetched at the path; with the option of having it mutable.
//
let node_child_fetched_as_mut =
  match node_root.get_node_mut_at_path_or_error( &path_original ) {
    Ok( result ) => result,
    Err( err ) => panic!( "{}", err )
  };
//
// The data extracted from the node should match what was originally stored
//
assert_eq!( &node_child_fetched_as_mut.field_string_data, example_data );
//
// You can mutate the data without errors
//
let example_data_new = "test_new";

node_child_fetched_as_mut.field_string_data = example_data_new.to_string();

assert_eq!( node_child_fetched_as_mut.field_string_data, example_data_new );
//////////////////////////////////////////////////////////////////////////////////////////////////
// WARNING: node_child_fetched_as_mut will limit what else we can do if we try to keep it in use
// with other function calls. At this point, we should ignore it from here on to avoid compiler
// gripes.
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// However, if we want to get it as immutable, this improves our flexibility. One issue, I forgot
// the path already. Let's do a search.
//
let vec_of_nodes_found_in_search = node_root.get_vec_of_nodes_satisfying_callback(
    |item_node| {
        item_node.field_string_data == example_data_new
    }
);
//
// One result. Looks promising.
//
assert_eq!( vec_of_nodes_found_in_search.len(), 1 );

let node_from_search =
    match vec_of_nodes_found_in_search.get( 0 ) {
        Some( result ) => result,
        None => panic!( "Failed to get node from vec." )
    };

println!( "node_from_search.field_string_data = {}", node_from_search.field_string_data );

// Check to see if this node's data is what we assigned it earlier
assert_eq!( node_from_search.field_string_data, example_data_new );
//
// Is this the same node we originally created?
//
let path_to_node =
    match node_root.get_path_to_node( &node_from_search ) {
        Some( result ) => result,
        None => panic!( "Failed to find path to node." )
    };
//
// Looks like we got the right one
//
println!( "path_to_node = {:?}", path_to_node );
assert_eq!( path_to_node, path_original );
//
// This example is non-exhaustive and the crate docs should be consulted for other options
//

}

设计选择

每个节点都是其子树的'根'节点

此库是单结构,因为所有管理节点子节点和孙节点的功能都可以在其中管理。

节点存储

每个节点都有一个承诺的String字段:field_string_data

而不是定义访问器和'设置'函数,此字段仅公开。

提示:建议使用serde与此crate一起使用。

没有持久内部引用

这解决了两个大问题

  • 从外部管理生命周期
  • 处理重叠(不可变)引用错误

在这种情况下,树是关于其内容和所有相关生命周期的'单一事实来源'。

只返回节点作为引用

类似于没有内部引用,此选择旨在最大限度地提高库外树的功效。

返回的路径和键被视为在访问器函数运行时拍摄的'快照'。

由于只返回节点作为引用,跟踪给定根节点处于何种“借用”状态相对简单。

所有权

树对其插入的节点拥有所有权。

如果向树中添加节点,则节点会被“移动”到树中。通过这些函数传递的路径和键在内部被复制。

一些优化说明

  • 由于开发者没有太多避免从外部进行冗余复制的选项,因此在内部克隆必要的字符串组件更有意义。
  • 所有查询都是“按引用传递”,除了返回值。这些需要被克隆以避免借用检查器问题。
  • 尽可能使用零成本选项,而不是不必要的复制。

更新(主要)

0.1.5

  • 从不需要使用“self”的函数中移除了“self”
  • 使路径函数更明确地处理路径长度

0.1.3

  • 进一步改进了代码的可读性和简洁性
  • 减少了类型转换和“collect”调用的数量

0.1.2

  • 减少了某些冗余

依赖

~0.4–1MB
~23K SLoC