我有一个需要的应用程序:
我只想通过1次哈希/查找来做到这一点。那可能吗?可以用2做吗?
我正在计算斐波那契以表明目的。从3个查找到1个查找进行优化的index()
函数是:
// Written by beefucurry
use std::collections::HashMap;
use std::cell::UnsafeCell;
fn main() {
let fib = SaveCall::<u32, u32>::new(|k, fib_| {
if k < 2 {
return k;
} else {
return fib_[k - 1] + fib_[k - 2];
}
});
for k in (0..30).rev() {
println!("fib[{}] = {}", k, fib[k]);
}
}
pub struct SaveCall<'a, In: std::cmp::Eq + std::hash::Hash + Copy, Out> {
cache: UnsafeCell<HashMap<In, Option<Out>>>,
f: Box<dyn Fn(In, &SaveCall<'a, In, Out>) -> Out + 'a>,
}
impl<'a, In: std::cmp::Eq + std::hash::Hash + Copy, Out> SaveCall<'a, In, Out> {
pub fn new(f: impl Fn(In, &Self) -> Out + 'a) -> Self {
Self {
cache: UnsafeCell::new(HashMap::new()),
f: Box::new(f),
}
}
}
impl<'a, In: std::cmp::Eq + std::hash::Hash + Copy, Out> std::ops::Index<In>
for SaveCall<'a, In, Out>
{
type Output = Out;
fn index<'b>(&'b self, input: In) -> &'b Self::Output {
unsafe {
//println!("~~Looking up {:?}", input);
let cache = self.cache.get();
let saved = (*cache).get(&input);
if saved.is_some() {
//println!("~~Found {:?}", input);
let z = saved.unwrap().as_ref();
if z.is_some() {
return z.unwrap();
} else {
panic!("Attempted to compute a value based on itself");
}
}
(*cache).insert(input, None);
//println!("~~Computing {:?}", input);
let output: Out = (self.f)(input, self);
//println!("~~Writing to cache {:?}", input);
(*cache).insert(input, Some(output));
let output_ref = (*cache).get(&input).unwrap().as_ref().unwrap();
output_ref
}
}
}
我只想通过1次哈希/查找来做到这一点。那可能吗?
仅凭一次哈希查找是不可能的,因为Rust无法知道计算本身是否可能尝试更改SaveCall
对象的结构。如果您的回调不接受self
作为参数,则有可能,但不能使用所需的API。
可以用2做吗?
可以使用2使用HashMap::entry
,它使您可以在地图中搜索键,并修改位置并检查它是否具有值,而不必重新计算哈希。
最初查询表并返回或插入时None
,可以使用此方法一次计算哈希,然后在获得最终结果时可以正常插入。
这将我们带到您的代码本身。无法使用std::ops::Index
特征来做您想要的事情。您的示例代码试图通过解决该问题UnsafeCell
,但是您的不安全代码违反了不安全代码必须遵守的要求。
简短的版本是该Index
特征返回一个引用。您特别希望通过在访问条目时插入条目来对地图进行变异,这意味着HashMap必须增加并为其值重新分配空间。如果您必须重新分配,这意味着无法返回所返回的引用index
,因为它们引用的是已重新分配的内存。
那会留在哪里?
Index
特征,而应在您的SavedCall
类型上实现不返回引用的方法。Out
直接返回,则Out
需要显式为一个Copy
类型。如果要支持非复制类型,则可能需要返回Rc
或Arc
。例如
impl<'a, In: std::cmp::Eq + std::hash::Hash + Copy, Out> SaveCall<'a, In, Out> {
fn get(&self, input: In) -> Rc<Out> {
要么
impl<'a, In: std::cmp::Eq + std::hash::Hash + Copy, Out: Copy> SaveCall<'a, In, Out> {
fn get(&self, input: In) -> Out {
这是在Rust操场上基于示例代码构建的更完整的版本。这使用Rc最好地演示了该示例,但是如果可以的话,可以将其精简Out: Copy
。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句