#rust #hashmap
#Ржавчина #хэш-карта
Вопрос:
Я следую вводной книге. В разделе 13.1 авторы реализовали «кэшер» и попросили читателя реализовать его с HashMap
помощью . Я заставил его работать, но я не совсем доволен из-за вызова unwrap()
, который кажется немного неуместным.
В частности, в Cacher::value()
, Some(v) => v.unwrap()
вызов — это то, что меня не устраивает — учитывая документацию unwrap()
состояний, которые следует избегать вызовов функции. Какое лучшее / более идиоматическое предложение для выполнения того, что я пытаюсь выполнить?
Код:
use std::collections::HashMap;
struct Cacher<T> {
calculation: T,
hmap: HashMap<u32, Option<u32>>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
hmap: HashMap::new(),
}
}
fn value(amp;mut self, arg: u32) -> u32 {
match self.hmap.get(amp;arg) {
Some(v) => v.unwrap(),
None => {
let v = (self.calculation)(arg);
self.hmap.insert(arg, Some(v));
v
}
}
}
}
fn main() {
let mut cache = Cacher::new(|x| x * x);
let v = vec![cache.value(1), cache.value(2), cache.value(3)];
println!("v: {:?}", v);
}
Комментарии:
1. Зачем иметь
Option
inHashMap<u32, Option<u32>>
?2. Я думаю, идея заключалась в том, что вы могли либо вычислить значение, либо нет, поэтому я подумал, что оно будет хорошо представлено опцией. Но теперь, когда вы это сказали, ясно, что в этом нет необходимости, потому что сама хэш-карта делает это, либо имея запись, либо нет.
3. Если вы хотите сохранить значения как
Option<u32>
, вы можете рассматривать no entry иNone
запись как одинаковые, сопоставляя только onSome(Some(v))
: playground
Ответ №1:
Нет необходимости иметь Option
в HashMap<u32, Option<u32>>
, потому HashMap
что само по себе может представлять, кэшировано значение или нет. Новый и улучшенный код:
use std::collections::HashMap;
struct Cacher<T> {
calculation: T,
hmap: HashMap<u32, u32>,
}
impl<T> Cacher<T>
where
T: Fn(u32) -> u32,
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
hmap: HashMap::new(),
}
}
fn value(amp;mut self, arg: u32) -> u32 {
match self.hmap.get(amp;arg) {
Some(v) => *v,
None => {
let v = (self.calculation)(arg);
self.hmap.insert(arg, v);
v
}
}
}
}
fn main() {
let mut cache = Cacher::new(|x| x * x);
let v = vec![cache.value(1), cache.value(2), cache.value(3)];
println!("v: {:?}", v);
}
Комментарии:
1. Далее в вашем путешествии вы можете заглянуть в
entry
API дляHashMap
. Как написано, когда значение не найдено, вы вычисляете его, а затем вставляете, что требует дублирования работы по хешированию ключа и выяснения, куда он идет. С помощью API ввода вы получаете слот один раз заранее, а затем решаете, что делать, исходя из того, заполнен ли он.2. Спасибо за совет! Я обязательно изучу это, когда решу углубиться в более сложные вещи!