使用整数获取映射到数字范围的值,以在Rust中查找键
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用整数获取映射到数字范围的值,以在Rust中查找键相关的知识,希望对你有一定的参考价值。
在Rust中,使用位于该范围内的整数,我可以为映射到一系列数字的值选择最简单的方法是什么?
就像是:
range_values.insert(0..1000, "A");
range_values.insert(1001..2000, "B");
get_value(range_values, 1500); // returns "B"
答案
最简单的方法是使用对向量并按顺序迭代它们以在向量中找到正确的条目。
use std::ops::Range;
fn get_value<'a>(range_values: &[(Range<i32>, &'a str)], key: i32) -> Option<&'a str> {
for &(ref range, value) in range_values {
//if range.contains(key) { // unstable as of Rust 1.23.0
if (range.start <= key) && (key < range.end) {
return Some(value);
}
}
return None;
}
fn main() {
let mut range_values = vec![];
range_values.push((0..1000, "A"));
range_values.push((1001..2000, "B"));
println!("{:?}", get_value(&range_values, -1)); // prints None
println!("{:?}", get_value(&range_values, 0)); // prints Some("A")
println!("{:?}", get_value(&range_values, 500)); // prints Some("A")
println!("{:?}", get_value(&range_values, 1000)); // prints None
println!("{:?}", get_value(&range_values, 1500)); // prints Some("B")
println!("{:?}", get_value(&range_values, 2500)); // prints None
}
但是,它并不是最有效的方法,尤其是当向量变大时。如果矢量被排序,则执行二分搜索(O(log n))而不是线性扫描(O(n))更有效。这假设范围不重叠。 (如果矢量没有排序,你只使用一次,那么进行线性扫描会更快,因为排序是O(n log n),即比单个线性扫描慢。)
use std::ops::Range;
fn get_value<'a>(range_values: &[(Range<i32>, &'a str)], key: i32) -> Option<&'a str> {
// Find the index of the first item where `range.start <= key`.
let index =
match range_values.binary_search_by_key(&key, |&(ref range, _)| range.start) {
Ok(index) => Some(index),
// If the requested key is smaller than the smallest range in the slice,
// we would be computing `0 - 1`, which would underflow an `usize`.
// We use `checked_sub` to get `None` instead.
Err(index) => index.checked_sub(1),
};
if let Some(index) = index {
let (ref range, value) = range_values[index];
if key < range.end {
return Some(value);
}
}
return None;
}
fn main() {
let mut range_values = vec![];
range_values.push((0..1000, "A"));
range_values.push((1001..2000, "B"));
// if the vector is not already sorted:
range_values.sort_by_key(|&(ref range, _)| range.start);
println!("{:?}", get_value(&range_values, -1)); // prints None
println!("{:?}", get_value(&range_values, 0)); // prints Some("A")
println!("{:?}", get_value(&range_values, 500)); // prints Some("A")
println!("{:?}", get_value(&range_values, 1000)); // prints None
println!("{:?}", get_value(&range_values, 1500)); // prints Some("B")
println!("{:?}", get_value(&range_values, 2500)); // prints None
}
另一答案
与Francis Gagné's idea of using binary search类似,我们可以使用BTreeMap
用于相同的目的。这样可以更轻松地添加和删除,而无需每次都对列表进行重新排序。与其他答案一样,它仅适用于间隔不重叠的情况。
我们需要围绕Range
构造一个新类型,以使用范围的下限作为查找的关键。我们还包装BTreeMap
以提供更好的访问方法。
#![feature(inclusive_range_syntax)]
use std::collections::BTreeMap;
use std::{borrow, cmp};
fn main() {
let mut range_values = RangeMap::default();
range_values.insert(0..1000, "A");
range_values.insert(1001..2000, "B");
assert_eq!(range_values.get(-1), None);
assert_eq!(range_values.get(0), Some(&"A"));
assert_eq!(range_values.get(500), Some(&"A"));
assert_eq!(range_values.get(1000), None);
assert_eq!(range_values.get(1500), Some(&"B"));
assert_eq!(range_values.get(2500), None);
}
#[derive(Debug, Default, Clone)]
struct RangeMap<V>(BTreeMap<RangeKey, V>);
impl<V> RangeMap<V> {
fn insert(&mut self, r: std::ops::Range<i32>, v: V) {
self.0.insert(RangeKey(r), v);
}
fn get(&self, id: i32) -> Option<&V> {
self.0.range(..=id).rev().next().and_then(|(k, v)| {
// Recheck inside range
if k.contains(id) {
Some(v)
} else {
None
}
})
}
}
#[derive(Debug, Clone, Eq)]
struct RangeKey(std::ops::Range<i32>);
impl RangeKey {
fn contains(&self, k: i32) -> bool {
k >= self.0.start && k < self.0.end
}
fn key(&self) -> &i32 {
&self.0.start
}
}
impl PartialEq for RangeKey {
fn eq(&self, other: &Self) -> bool {
self.key().eq(&other.key())
}
}
impl PartialOrd for RangeKey {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RangeKey {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.key().cmp(&other.key())
}
}
impl borrow::Borrow<i32> for RangeKey {
fn borrow(&self) -> &i32 {
self.key()
}
}
不幸的是,这需要不稳定的RangeInclusive
。
对于更复杂的解决方案,您需要一个interval tree。
另一答案
如果数字是从0开始按顺序开始的,那么最简单和最有效的方法就是使用向量:
let mut range_values = vec!["A"; 1000];
range_values.append(&mut vec!["B"; 1000]);
assert_eq!(range_values[1500], "B");
如果它们是顺序但不以0开头,您仍然可以通过简单的翻译来完成:
assert_eq!(range_values[1500 - start], "B");
如果它们不是连续的,那么你需要使用Francis Gagné's或Shepmaster's答案。
以上是关于使用整数获取映射到数字范围的值,以在Rust中查找键的主要内容,如果未能解决你的问题,请参考以下文章