我希望能够在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中是否有惯用的方法。
答案 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的答案所说。当迭代器使用DoubleEndedIterator
和next()
实现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")));
}
代码编写得不好,但有效。