gpt4 book ai didi

generics - 是否可以对 HashMap 的键和值使用单个泛型?

转载 作者:行者123 更新时间:2023-11-29 08:07:36 24 4
gpt4 key购买 nike

chapter 13 of the Rust book ,您实现一个 Cacher 来使用内存来演示函数式编程以及如何加速长时间运行的任务。作为一项额外的挑战,他们建议使用 HashMapCacher 允许多个键,并利用泛型来实现更大的灵 active 。

Try modifying Cacher to hold a hash map rather than a single value. The keys of the hash map will be the arg values that are passed in, and the values of the hash map will be the result of calling the closure on that key. Instead of looking at whether self.value directly has a Some or a None value, the value function will look up the arg in the hash map and return the value if it’s present. If it’s not present, the Cacher will call the closure and save the resulting value in the hash map associated with its arg value.

The second problem with the current Cacher implementation is that it only accepts closures that take one parameter of type u32 and return a u32. We might want to cache the results of closures that take a string slice and return usize values, for example. To fix this issue, try introducing more generic parameters to increase the flexibility of the Cacher functionality.

我能够实现 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 个泛型(即 KV)的唯一方法还是有办法重用一个泛型?如果需要 2,我在上面缺少什么?

是否有更惯用的方法来解决这个问题?不幸的是,Rust 书没有提供解决方案。

最佳答案

您的实现无法编译,因为生命周期边界必须仅在 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

关于generics - 是否可以对 HashMap 的键和值使用单个泛型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50055756/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com