2017-04-18 43 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, 
相關問題