在chapter 13 of the Rust book中,您实现Cacher
以使用memoization来演示函数式编程以及如何加速长时间运行的任务。作为额外的挑战,他们建议Cacher
使用HashMap
允许多个密钥,并利用泛型来提供更大的灵活性。
尝试修改
Cacher
以保存哈希映射而不是单个值。 哈希映射的键将是传入的arg
值, 并且哈希映射的值将是调用的结果 关闭那把钥匙。而不是直接查看是否self.value
如果值为Some
或None
,则值函数将在arg
中查找 哈希映射并返回值(如果存在)。如果不是 现在,Cacher
将调用闭包并保存结果值 在与其arg
值关联的哈希映射中。当前
Cacher
实施的第二个问题是它 只接受带有u32
类型参数的闭包并返回au32
。我们可能想要缓存带字符串的闭包的结果 例如,切片并返回usize
值。要解决此问题,请尝试 引入更多通用参数以增加灵活性Cacher
功能。
我能够实现HashMap
,但是当尝试用通用类型替换闭包定义u32
并将其用作HashMap
的签名时,我遇到了问题。
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::thread;
use std::time::Duration;
struct Cacher<'a, T>
where
T: Fn(&'a u32) -> &'a u32,
{
calculation: T,
values: HashMap<&'a u32, &'a u32>,
}
impl<'a, T> Cacher<'a, T>
where
T: Fn(&'a u32) -> &'a u32,
{
fn new(calculation: T) -> Cacher<'a, T> {
Cacher {
calculation,
values: HashMap::new(),
}
}
fn values(&mut self, arg: &'a u32) -> &'a u32 {
match self.values.entry(arg) {
Entry::Occupied(e) => &*e.into_mut(),
Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)),
}
}
}
fn generate_workout(intensity: u32, random_number: u32) {
let mut expensive_result = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
&num
});
if intensity < 25 {
println!("Today, do {} pushups!", expensive_result.values(&intensity));
println!("Next, do {} situps!", expensive_result.values(&intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_result.values(&intensity)
);
}
}
}
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
generate_workout(simulated_user_specified_value, simulated_random_number);
}
我尝试了K, V
泛型,如下所示,它抱怨Expected one of 7 possible values here
指向第一个类型定义。
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::hash::Hash;
use std::thread;
use std::time::Duration;
struct Cacher<'a, T: 'a, K: 'a, V: 'a>
where
T: Fn(&'a K) -> &'a V,
K: Hash + Eq,
{
calculation: T,
values: HashMap<&'a K, &'a V>,
}
impl<'a, T: 'a, K: 'a, V: 'a> Cacher<'a, T: 'a, K: 'a, V: 'a>
where
T: Fn(&'a K) -> &'a V,
K: Hash + Eq,
{
fn new(calculation: T) -> Cacher<'a, T: 'a, K: 'a, V: 'a> {
Cacher {
calculation,
values: HashMap::new(),
}
}
fn values(&mut self, arg: &'a K) -> &'a V {
match self.values.entry(arg) {
Entry::Occupied(e) => &*e.into_mut(),
Entry::Vacant(e) => &*e.insert(&(self.calculation)(&arg)),
}
}
}
fn generate_workout(intensity: u32, random_number: u32) {
let mut expensive_result = Cacher::new(|num| {
println!("calculating slowly...");
thread::sleep(Duration::from_secs(2));
&num
});
if intensity < 25 {
println!("Today, do {} pushups!", expensive_result.values(&intensity));
println!("Next, do {} situps!", expensive_result.values(&intensity));
} else {
if random_number == 3 {
println!("Take a break today! Remember to stay hydrated!");
} else {
println!(
"Today, run for {} minutes!",
expensive_result.values(&intensity)
);
}
}
}
fn main() {
let simulated_user_specified_value = 10;
let simulated_random_number = 7;
generate_workout(simulated_user_specified_value, simulated_random_number);
}
导致以下错误:
error: expected one of `!`, `(`, `+`, `,`, `::`, `<`, or `>`, found `:`
--> src/main.rs:16:39
|
16 | impl<'a, T: 'a, K: 'a, V: 'a> Cacher<T: 'a, K: 'a, V: 'a>
| ^ expected one of 7 possible tokens here
是添加2个更多泛型的唯一方法(例如K
,V
)还是有办法重用单个泛型?如果需要2,我上面缺少什么?
有没有更惯用的方法来解决这个问题?不幸的是,Rust书并没有提供解决方案。
答案 0 :(得分:1)
您的实现无法编译,因为必须在impl
之后声明生命周期边界:
impl<'a, T: 'a, K: 'a, V: 'a> Cacher<'a, T, K, V>
where
T: Fn(&'a K) -> &'a V,
K: Hash + Eq,
{
fn new(calculation: T) -> Cacher<'a, T, K, V> {
Cacher {
calculation,
values: HashMap::new(),
}
}
}
将引用存储到HashMap
意味着您必须管理生命周期并确保HashMap
引用的值比Cacher
更长。
另一种需要考虑的方法可能是按值缓存:
struct Cacher<T, K, V>
where
T: Fn(K) -> V,
{
calculation: T,
value: HashMap<K, V>,
}
impl<T, K, V> Cacher<T, K, V>
where
T: Fn(K) -> V,
K: Hash + Eq + Clone
{
fn new(calculation: T) -> Cacher<T, K, V> {
Cacher {
calculation,
value: HashMap::new(),
}
}
fn value(& mut self, arg: K) -> &V {
match self.value.entry(arg.clone()) {
Entry::Occupied(v) => v.into_mut(),
Entry::Vacant(v) => v.insert((self.calculation)(arg)),
}
}
}
请注意,在此解决方案中,我添加了K
为Clone
答案 1 :(得分:0)
这个问题很老了,但我在网上浏览时发现了这个问题,看看我的解决方案是否“正确”。我将在这里分享它,因为它与接受的答案略有不同,并且保留了书中 value
方法的原始签名:
struct Cacher<T, K, V>
where T: Fn(K) -> V,
K: Eq + Hash + Copy,
V: Copy {
calculation: T,
cache: HashMap<K, V>,
}
impl<T, K, V> Cacher<T, K, V>
where T: Fn(K) -> V,
K: Eq + Hash + Copy,
V: Copy {
fn new(calculation: T) -> Cacher<T, K, V> {
Cacher {
calculation,
cache: HashMap::new(),
}
}
fn value(&mut self, arg: K) -> V {
match self.cache.get(&arg) {
Some(&v) => v,
None => {
let v = (self.calculation)(arg);
self.cache.insert(arg, v);
v
}
}
}
}
在这里,我为 HashMap 的键和值设置了明确的通用参数,这些参数将缓存 Cacher
的值。 K
有 Eq
、Hash
和 Copy
的 trait bound,而 V
只需要 Copy
trait bound。