如何利用Rust的HashMap克服官方文档中闭包的约束?
- 内容介绍
- 文章标签
- 相关推荐
本文共计695个文字,预计阅读时间需要3分钟。
问题概述:值缓存是一种增加泛用性的实用行为,我们可能希望在其他代码中的其他闭包中也能使用它们。然而,目前Cacher的实现存在两个小问题,这使得在不同上下文中复用变得非常困难。
问题一:Cacher的实现细节不明确,导致在不同上下文中难以复用。
问题概述值缓存是一种更加广泛的实用行为,我们可能希望在代码中的其他闭包中也使用他们。然而,目前Cacher的实现存在两个小问题,这使得在不同上下文中复用变得很困难。
第一个问题是Cacher实例假设对于value方法的任何arg参数值总是会返回相同的值。也就是说,这个Cacher的测试会失败:
《rust程序设计语言》13章闭包内容提出的问题
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
具体代码请在《rust程序设计语言》第13章第一节Cacher实现的限制中找到。
解决思路
尝试修改Cacher存放一个哈希 map 而不是单独一个值。哈希 map 的 key 将是传递进来的arg值,而 value 则是对应 key 调用闭包的结果值。
相比之前检查self.value直接是Some还是None值,现在value函数会在哈希 map 中寻找arg,如果找到的话就返回其对应的值。如果不存在,Cacher会调用闭包并将结果值保存在哈希 map 对应arg值的位置。
《rust程序设计语言》13章闭包内容提出的解决思路
解决方案 更改Cacher结构体的类型
use std::collections::HashMap;
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: HashMap<u32, u32>,
}
将之前储存单一u32类型的value字段替换成HashMap类型,此HashMap的key和value都为u32类型。
更改chacher结构体的方法impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: HashMap::new(),
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value.get(&arg) {
Some(v) => *v,
None => {
let v = (self.calculation)(arg);
self.value.insert(arg, v);
arg
}
}
}
}
new方法中返回的Cacher实例,value字段不再为Option<u32>类型,取而代之的是一个被初始化的HashMap,用于存放不同参数的结果缓存。
value方法中,不再直接匹配结构体的value字段,而是通过参数去value字段的HashMap中找到储存的值并返回,要是找不到则在HashMap中插入key值为传入参数,value值为结构体闭包调用参数所得的结果。
测试结果
fn main() {
let mut cal = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
});
println!("{}", cal.value(1));
println!("{}", cal.value(2));
println!("{}", cal.value(1))
}
cargo run
Compiling closure v0.1.0 (D:\project\rust\closure)
Finished dev [unoptimized + debuginfo] target(s) in 0.66s
Running `target\debug\closure.exe`
calculating slowly...
1
calculating slowly...
2
1
测试通过
本文共计695个文字,预计阅读时间需要3分钟。
问题概述:值缓存是一种增加泛用性的实用行为,我们可能希望在其他代码中的其他闭包中也能使用它们。然而,目前Cacher的实现存在两个小问题,这使得在不同上下文中复用变得非常困难。
问题一:Cacher的实现细节不明确,导致在不同上下文中难以复用。
问题概述值缓存是一种更加广泛的实用行为,我们可能希望在代码中的其他闭包中也使用他们。然而,目前Cacher的实现存在两个小问题,这使得在不同上下文中复用变得很困难。
第一个问题是Cacher实例假设对于value方法的任何arg参数值总是会返回相同的值。也就是说,这个Cacher的测试会失败:
《rust程序设计语言》13章闭包内容提出的问题
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 2);
}
具体代码请在《rust程序设计语言》第13章第一节Cacher实现的限制中找到。
解决思路
尝试修改Cacher存放一个哈希 map 而不是单独一个值。哈希 map 的 key 将是传递进来的arg值,而 value 则是对应 key 调用闭包的结果值。
相比之前检查self.value直接是Some还是None值,现在value函数会在哈希 map 中寻找arg,如果找到的话就返回其对应的值。如果不存在,Cacher会调用闭包并将结果值保存在哈希 map 对应arg值的位置。
《rust程序设计语言》13章闭包内容提出的解决思路
解决方案 更改Cacher结构体的类型
use std::collections::HashMap;
struct Cacher<T>
where
T: Fn(u32) -> u32,
{
calculation: T,
value: HashMap<u32, u32>,
}
将之前储存单一u32类型的value字段替换成HashMap类型,此HashMap的key和value都为u32类型。
更改chacher结构体的方法impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: HashMap::new(),
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value.get(&arg) {
Some(v) => *v,
None => {
let v = (self.calculation)(arg);
self.value.insert(arg, v);
arg
}
}
}
}
new方法中返回的Cacher实例,value字段不再为Option<u32>类型,取而代之的是一个被初始化的HashMap,用于存放不同参数的结果缓存。
value方法中,不再直接匹配结构体的value字段,而是通过参数去value字段的HashMap中找到储存的值并返回,要是找不到则在HashMap中插入key值为传入参数,value值为结构体闭包调用参数所得的结果。
测试结果
fn main() {
let mut cal = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
num
});
println!("{}", cal.value(1));
println!("{}", cal.value(2));
println!("{}", cal.value(1))
}
cargo run
Compiling closure v0.1.0 (D:\project\rust\closure)
Finished dev [unoptimized + debuginfo] target(s) in 0.66s
Running `target\debug\closure.exe`
calculating slowly...
1
calculating slowly...
2
1
测试通过

