在单个搜索中查找BTreeSet中的更大和更低的键

时间:2018-05-15 00:50:40

标签: collections rust

我希望能够在Rust BTreeSet中找到严格低于和大于指定键的键。

例如,如果设置为{ "1", "3" },搜索关键字为"2",则答案应为("1""3")。在不存在较低或较大值的情况下,应返回None

我可以通过在range()上调用BTreeSet方法两次来实现我正在寻找的结果。

有没有办法使用单个搜索来完成此操作,就像在C ++中一样? C ++的std::set有一个双向迭代器:

// $CXX -std=c++17 less-than.c++ -o less-than && ./less-than

#include <cassert>
#include <optional>
#include <set>
#include <string>
#include <iostream>

using std::optional;
using std::pair;
using std::set;
using std::string;

pair<optional<string>, optional<string>> bounding_box(
    const set<string>& space,
    const string& point)
{
    if (space.empty()) { return {}; }

    optional<string> gt_bound;
    optional<string> lt_bound;

    const auto ge_bound_it = space.lower_bound(point);

    if (ge_bound_it != space.end()) {
        if (*ge_bound_it == point) {
            // lower_bound returned an equal point, use the next one
            // if it exists
            const auto gt_bound_it = std::next(ge_bound_it, 1);

            if (gt_bound_it != space.end()) {
                gt_bound = *gt_bound_it;
            }
        } else {
            gt_bound = *ge_bound_it;
        }

    }

    if (ge_bound_it != space.begin()) {
        lt_bound = *std::next(ge_bound_it, -1);
    }

    return {lt_bound, gt_bound};
}

int main() {
    {
        const auto box = bounding_box({"1", "3"}, "2");
        assert(box.first);
        assert(*box.first == "1");

        assert(box.second);
        assert(*box.second == "3");
    }

    {
        const auto box = bounding_box({"1", "3"}, "4");
        assert(box.first);
        assert(*box.first == "3");

        assert(!box.second);
    }

    {
        const auto box = bounding_box({"1", "3"}, "0");
        assert(!box.first);

        assert(box.second);
        assert(*box.second == "1");
    }

    {
        const auto box = bounding_box({"3", "3"}, "3");
        assert(!box.first);
        assert(!box.second);
    }

    {
        const auto box = bounding_box({"3", "4"}, "3");
        assert(!box.first);
        assert(box.second);
        assert(*box.second == "4");
    }

    {
        const auto box = bounding_box({}, "3");
        assert(!box.first);
        assert(!box.second);
    }
}

search方法有点热点,我想知道在Rust中是否有惯用的方法。

2 个答案:

答案 0 :(得分:4)

不,在单次搜索中无法做到这一点; you need to call range twice

一直在讨论如何将BTreeMap / BTreeSet加强为“游标”API。最近,a pull request was opened to do so,但它被关闭了,因为它被认为应该有更多关于这样的API应该如何看起来和工作的讨论。

也许会成为带头讨论这种API的人吗?

另见:

答案 1 :(得分:0)

在Rust中没有游标API,正如Shepmaster的答案所说。当迭代器使用DoubleEndedIteratornext()实现next_back()时,您有时可以模拟它。

但是,如果我很清楚你想要做什么,你就不需要这个东西,因为订购了一套。您可以通过浏览每一对来编写代码,并在第二个项目大于您的&#34;点&#34;时停止:

use std::collections::BTreeSet;

fn find_bounds<'a>(set: &'a BTreeSet<&str>, point: &str) -> (Option<&'a str>, Option<&'a str>) {
    let mut it = set.iter();
    let mut lower = match it.next() {
        None => return (None, None),
        Some(s) if *s > point => return (None, Some(s)),
        Some(s) => s,
    };

    while let Some(upper) = it.next() {
        if *upper > point {
            return (Some(lower), Some(*upper));
        }
        lower = upper;
    }
    (Some(lower), None)
}

#[test]
fn tests() {
    let mut s = BTreeSet::new();

    s.insert("a");
    s.insert("c");
    s.insert("t");
    s.insert("g");

    assert_eq!(find_bounds(&s, "f"), (Some("c"), Some("g")));
    assert_eq!(find_bounds(&s, "z"), (Some("t"), None));
    assert_eq!(find_bounds(&s, " "), (None, Some("a")));
}

代码编写得不好,但有效。