更简洁的HashMap初始化

时间:2015-02-08 08:28:15

标签: rust

我正在使用HashMap来计算字符串中不同字符的出现次数:

let text = "GATTACA";
let mut counts: HashMap<char, i32> = HashMap::new();
counts.insert('A', 0);
counts.insert('C', 0);
counts.insert('G', 0);
counts.insert('T', 0);

for c in text.chars() {
    match counts.get_mut(&c) {
        Some(x) => *x += 1,
        None => (),
    }
}

是否有更简洁或声明性的方式来初始化HashMap?例如在Python中我会这样做:

counts = { 'A': 0, 'C': 0, 'G': 0, 'T': 0 }

counts = { key: 0 for key in 'ACGT' }

3 个答案:

答案 0 :(得分:39)

您可以使用迭代器来模拟字典理解,例如

let counts = "ACGT".chars().map(|c| (c, 0_i32)).collect::<HashMap<_, _>>();

甚至是for c in "ACGT".chars() { counts.insert(c, 0) }

此外,可以编写宏来允许对任意值进行简洁的初始化。

macro_rules! hashmap {
    ($( $key: expr => $val: expr ),*) => {{
         let mut map = ::std::collections::HashMap::new();
         $( map.insert($key, $val); )*
         map
    }}
}

let counts = hashmap!['A' => 0, 'C' => 0, 'G' => 0, 'T' => 0];类似。

答案 1 :(得分:2)

我在官方文档中看到的另一种方式: https://doc.rust-lang.org/std/collections/struct.HashMap.html

use std::collections::HashMap;

fn main() {
    let timber_resources: HashMap<&str, i32> =
    [("Norway", 100),
     ("Denmark", 50),
     ("Iceland", 10)]
     .iter().cloned().collect();
    // use the values stored in map
}

答案 2 :(得分:0)

这种(非常常见)的情况就是为什么当我发现Python的 defaultdict (一个字典)时听到天使在唱歌的原因,如果您尝试获取字典中没有的键,则会立即为该键创建一个默认值。因此,在Python中,您可以执行以下操作:

counts = defaultdict(lambda: 0)
counts['A'] = counts['A'] + 1

对于计数出现次数,这是一种首选方法,因为当键空间很大或对于程序员来说未知时,尝试预填充哈希表会出现问题(想象一下,有些东西会对输入给它的文本中的单词进行计数。预填充所有英语单词?如果有新单词输入词典怎么办?)

您可以使用 Option 类中鲜为人知的方法在Rust中实现此目的。认真地说,当您有空闲时间时,请通读 Option 中的所有方法。那里有一些非常方便的方法。

尽管不处理简洁的初始化(这是wubject所要求的),但这里有两个答案(可以说,这对于执行OP试图执行的操作来说更好)。

let text = "GATTACA";
let mut counts:HashMap<char,i32> = HashMap::new();
for c in text.chars() {
    counts.insert(c,*(counts.get(&c).get_or_insert(&0))+1);
}

上述方法使用Option的 get或insert()方法,如果该方法为Some(),则返回值,如果为None,则返回您提供的值。 请注意,即使该方法名为get_or_insert(),也不会插入到哈希图中;这是Option的一种方法,哈希图不知道是否正在进行故障转移。令人高兴的是,这可以为您带来价值。这与Python的defaultdict非常相似,不同之处在于,您必须在代码的多个位置提供默认值(引发错误,但还提供了defaultdict缺乏的灵活性)。

let text = "GATTACA";
let mut counts:HashMap<char,i32> = HashMap::new();
for c in text.chars() {
    counts.insert(c,counts.get(&c).or_else(|| Some(&0)).unwrap()+1);
}

此方法使用Option的 or else()方法,该方法允许您指定用于生成值的lambda,并且至关重要的是,允许您 still 返回None(如果您想想要检查某个键的哈希图,如果找不到,请检查另一个哈希图,并且只有在两者中均未找到的情况下,您才会产生None)。因为 or else()返回一个选项,所以我们必须使用 unwrap()(如果在None上使用会出现恐慌,但我们知道这不适用于此)。