2017-04-18 41 views
0

我有一系列国家,我想生成这些国家之间的关系,以便国家A和国家B之间的关系总是与国家B和国家A之间的关系相同。如何将两个变量之间的双向关系存储为第三个变量?

例如,日本和厄瓜多尔的关系值为15.我希望能够同时运行get_relationship("Japan", "Ecuador");get_relationship("Ecuador", "Japan");并始终得到15。理想情况下,我不想将这个值存储两次,因为我不想让它保持同步。

下面是我目前的实验代码。请注意,由于无关联的原因,我将国家/地区作为HashMap存储为(name as String, Nation as struct),主要是我可以通过名称来提取所需的Nation对象。

extern crate rand; 

use std::collections::HashMap; 
use rand::Rng; 

struct Nation; 

pub struct NationManager { 
    nations: HashMap<String, Nation>, // The string is the nation's name 
    rels: HashMap<String, HashMap<String, i8>>, // Again, both Strings are names 
} 

impl NationManager { 
    fn generate_relationships(&mut self) { 
     let mut random_rel: i8 = rand::thread_rng().gen_range(1, 101); 
     for (source, _) in &self.nations { 
      for (target, _) in &self.nations { 
       if source > target { 
        self.rels 
         .get(source) 
         .expect("ERROR") 
         .insert(target.clone(), random_rel); 
       } else if source < target { 
        self.rels 
         .get(target) 
         .expect("ERROR") 
         .insert(source.clone(), random_rel); 
       } else { 
        continue; 
       } 
      } 
     } 
    } 
} 

fn main() {} 

我不认为这是达到所需结果的最佳方法,而且目前还不能编译;实际上是否可以像这样嵌套两个for循环?

error: cannot borrow immutable borrowed content as mutable 
    --> src/main.rs:19:21 
    | 
19 |      self.rels 
    | _____________________^ starting here... 
20 | |       .get(source) 
21 | |       .expect("ERROR") 
    | |________________________________________^ ...ending here: cannot borrow as mutable 

error: cannot borrow immutable borrowed content as mutable 
    --> src/main.rs:24:21 
    | 
24 |      self.rels 
    | _____________________^ starting here... 
25 | |       .get(target) 
26 | |       .expect("ERROR") 
    | |________________________________________^ ...ending here: cannot borrow as mutable 
+4

一个不相关的注释:而不是'。expect(“ERROR”),你可以使用'.unwrap()',这在眼睛上更容易。 'expect()'的意思是提供一个有意义的错误信息。 – user4815162342

+2

编译器提供的实际错误信息是什么? “不归因于所有权欺诈”不是很有用。 –

+0

@ user4815162342我重申,“当前的实验代码”。 @ E_net4“不能借用可变”,指向'self.rels.get(target).expect(“ERROR”)'。 我很希望有人会有更好的解决方案。我无法想象这就是我要实现这一任务的原因。 – Quintus

回答

5

首先第一件事情:你的问题是get返回一个不可改变的参考,并尝试insert在那里。你会想要使用get_mut来获得可执行的可变引用,其中insert可以被执行。


不过,我会建议改变设计:

  1. 存储的地图名称 - > ID,
  2. 使用ID作为其他地图的关键。

该方案的主要优点是数字ID比字符串更便宜/更高效。

pub struct NationManager { 
    last_id: u32, 
    name_to_id: HashMap<String, u32>, 
    relationships: HashMap<(u32, u32), i8>, // (smaller ID, larger ID) -> score 
} 

执行查找两个国家之间的关系将涉及知道它们的ID(两个查找窗口中name_to_id),然后抬头的关系分数。

relationships压扁将极大简化您的生成步骤,以及:

impl NationManager { 
    fn generate_relationships(&mut self) { 
     let random_rel: i8 = rand::thread_rng().gen_range(1, 101); 
     for source in 0..self.last_id { 
      for target in (source + 1)..self.last_id { 
       self.relationships.insert((source, target), random_rel); 
      } 
     } 
    } 
} 

注:实际上,一个域分析,可以让我们用更小的ID为好;你不应该需要超过65535个国家,所以u16肯定是足够的,并且很可能u8(255个国家)就足够了(有193个国家在联合国注册)。

+1

“你应该需要65535多个国家”? :) –

+0

这是一个非常聪明的解决方案,谢谢!使用数字肯定会让我更容易缠绕脑袋。所以,对于getter,我可以假设任何两个国家之间的关系都存储在一个'(ID较低,ID较高的)'元组中,对吧?另外,感谢您指出'get_mut'方法 - 我仍然围绕Rust进行所有权。 – Quintus

+1

FWIW,使用'u8' /'u16' /'u32' [全部似乎具有相同的结构大小](https://play.integer32.com/?gist=d4dba07dc00e07ce1c1a0b97d7e45281&version=undefined) - 88个字节。 – Shepmaster

1

问题是您正在使用错误的方法。 HashMap::get不允许变异的结果:

fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> 
    where K: Borrow<Q>, 
      Q: Hash + Eq, 

你想get_mut

fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V> 
    where K: Borrow<Q>, 
      Q: Hash + Eq, 
相关问题