मैं HashMap शाब्दिक कैसे बनाऊं?


87

मैं जंग में एक हाशपापल शाब्दिक कैसे बना सकता हूं? अजगर में मैं ऐसा कर सकता हूं:

hashmap = {
   'element0': {
       'name': 'My New Element',
       'childs': {
           'child0': {
               'name': 'Child For Element 0',
               'childs': {
                   ...
               }
           }
       }
   },
   ...
}

और इस तरह जाओ:

type Node struct {
    name string
    childs map[string]Node
}

hashmap := map[string]Node {
    "element0": Node{
        "My New Element",
        map[string]Node {
            'child0': Node{
                "Child For Element 0",
                map[string]Node {}
            }
        }
    }
}

जवाबों:


90

Rust में मैप शाब्दिक सिंटैक्स नहीं है। मुझे इसका सटीक कारण नहीं पता है , लेकिन मुझे उम्मीद है कि यह तथ्य है कि कई डेटा संरचनाएं हैं जो मैपलाइक (जैसे कि दोनों ) BTreeMapऔर जैसे कार्य करते हैं, HashMapएक को चुनना मुश्किल होगा।

हालाँकि, आप अपने लिए काम करने के लिए एक मैक्रो बना सकते हैं, जैसा कि यह दिखाया गया है कि यह जंग हाशप मैक्रो अब काम क्यों नहीं करती है? । यहाँ है कि मैक्रो ने थोड़ा सा सरलीकरण किया है और पर्याप्त संरचना के साथ खेल के मैदान में इसे चलाने योग्य बनाया जा सकता है :

macro_rules! map(
    { $($key:expr => $value:expr),+ } => {
        {
            let mut m = ::std::collections::HashMap::new();
            $(
                m.insert($key, $value);
            )+
            m
        }
     };
);

fn main() {
    let names = map!{ 1 => "one", 2 => "two" };
    println!("{} -> {:?}", 1, names.get(&1));
    println!("{} -> {:?}", 10, names.get(&10));
}

यह मैक्रो एक अनावश्यक इंटरमीडिएट को आवंटित करने से बचता है Vec, लेकिन इसका उपयोग नहीं करता है, क्योंकि इसमें HashMap::with_capacityकुछ बेकार हो सकते हैं जैसे HashMapकि मान जोड़े जाते हैं। मैक्रो का एक और अधिक जटिल संस्करण जो मानों को गिनता है, संभव है, लेकिन प्रदर्शन लाभ शायद कुछ नहीं हैं जो मैक्रो के अधिकांश उपयोग से लाभ होगा।


जंग के एक रात के संस्करण में, आप दोनों अनावश्यक आवंटन (और वसूली) से बच सकते हैं और साथ ही एक मैक्रो की आवश्यकता भी हो सकती है:

#![feature(array_value_iter)]

use std::array::IntoIter;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter::FromIterator;

fn main() {
    let s = Vec::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = BTreeSet::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = HashSet::<_>::from_iter(IntoIter::new([1, 2, 3]));
    println!("{:?}", s);

    let s = BTreeMap::from_iter(IntoIter::new([(1, 2), (3, 4)]));
    println!("{:?}", s);

    let s = HashMap::<_, _>::from_iter(IntoIter::new([(1, 2), (3, 4)]));
    println!("{:?}", s);
}

इस तर्क को एक मैक्रो में भी लपेटा जा सकता है:

#![feature(array_value_iter)]

use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};

macro_rules! collection {
    // map-like
    ($($k:expr => $v:expr),* $(,)?) => {
        std::iter::Iterator::collect(std::array::IntoIter::new([$(($k, $v),)*]))
    };
    // set-like
    ($($v:expr),* $(,)?) => {
        std::iter::Iterator::collect(std::array::IntoIter::new([$($v,)*]))
    };
}

fn main() {
    let s: Vec<_> = collection![1, 2, 3];
    println!("{:?}", s);

    let s: BTreeSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: HashSet<_> = collection! { 1, 2, 3 };
    println!("{:?}", s);

    let s: BTreeMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);

    let s: HashMap<_, _> = collection! { 1 => 2, 3 => 4 };
    println!("{:?}", s);
}

यह सभी देखें:


6
grabbag_macrosटोकरा में भी एक है । आप यहाँ स्रोत देख सकते हैं: github.com/DanielKeep/rust-grabbag/blob/master/grabbag_macros/…
डी.के.

4
आह, शर्म की बात है। मुझे इसके लिए स्विफ्ट दृष्टिकोण पसंद है, जो अपने प्रकारों से शाब्दिक सार करता है। A DictionaryLiteralका उपयोग किसी भी प्रकार को शुरू करने के लिए किया जा सकता है, जिसके अनुरूप ExpressibleByDictionaryLiteral(भले ही मानक पुस्तकालय एक ऐसा प्रकार प्रदान करता है Dictionary) ,
अलेक्जेंडर

48

मैं मैपलेट टोकरा सुझाता हूं

प्रलेखन से उद्धृत करने के लिए:

विशिष्ट प्रकार के कंटेनर कंटेनर के लिए मैक्रो।

use maplit::hashmap;

let map = hashmap!{
    "a" => 1,
    "b" => 2,
};

=>मानचित्रण टोकरा मानचित्रण मैक्रो के लिए वाक्यविन्यास का उपयोग करता है । :नियमित macro_rules!मैक्रोज़ में प्रतिबंधों के कारण विभाजक के रूप में उपयोग करना संभव नहीं है ।

ध्यान दें कि जंग मैक्रोज़ लचीले होते हैं जिसमें आप कोष्ठक के लिए उपयोग करते हैं। आप उन्हें के रूप में उपयोग कर सकते हैं hashmap!{}या hashmap![]या hashmap!()। यह टोकरा {}मानचित्र &सेट मैक्रोज़ के सम्मेलन के रूप में सुझाव देता है , यह उनके डिबग आउटपुट से मेल खाता है।

मैक्रो

  • btreemapBTreeMapकुंजी-मूल्य जोड़े की सूची से बनाएं
  • btreesetBTreeSetतत्वों की सूची से बनाएं ।
  • hashmapHashMapकुंजी-मूल्य जोड़े की सूची से बनाएं
  • hashsetHashSetतत्वों की सूची से बनाएं ।

42

दस्तावेज़HashMap में इसे कैसे प्राप्त किया जाए, इसका एक उदाहरण है :

let timber_resources: HashMap<&str, i32> = [("Norway", 100), ("Denmark", 50), ("Iceland", 10)]
    .iter()
    .cloned()
    .collect();

4
जबकि यह इस मामले में काम करता है, इसके Stringबजाय कुछ का उपयोग करते समय यह अनावश्यक रूप से महंगा हो जाता है &str
शेमपस्टर

7
निश्चित रूप से रनटाइम लागतें हैं, लेकिन एक और टोकरा निर्भरता को जोड़ने या हार्ड-टू-समझने वाले मैक्रो को परिभाषित करने के साथ जुड़े लागत भी हैं। अधिकांश कोड प्रदर्शन महत्वपूर्ण नहीं है और मुझे यह संस्करण बहुत पठनीय लगता है।
रसप्रा।

2
इसके अतिरिक्त, यह उन प्रकारों के लिए काम नहीं करता है जो क्लोन नहीं हैं।
हच मूर

10
इस उदाहरण को उन मुद्दों से बचने के लिए तैयार किया जा सकता है, जिनका उपयोग करकेvec![ (name, value) ].into_iter().collect()
जोहान्स

1
@ स्टार्गिटर क्योंकि मैं समय से पहले अनुकूलन नहीं करता हूं और न ही आपको करना चाहिए। यह हो सकता है कि आपके कार्यक्रम को अधिक अनुकूलित हैशपैम शाब्दिक आरंभीकरण से लाभ होगा, लेकिन यह शायद नहीं होगा। यदि आप सर्वश्रेष्ठ प्रदर्शन प्राप्त करना चाहते हैं, तो अपने कार्यक्रम के उन हिस्सों को मापें और उनका अनुकूलन करें जो महत्वपूर्ण पथ पर हैं। बेतरतीब ढंग से अनुकूलित सामान सिर्फ एक खराब रणनीति है। मेरा जवाब 99.9999% लोगों के लिए ठीक रहेगा। यह पठनीय है, तेजी से संकलन करता है, निर्भरता का परिचय नहीं देता है जो जटिलता को बढ़ाता है और संभावित सुरक्षा कमजोरियों का परिचय देता है।
जूपी ०१

4

जैसा कि टिप्पणियों में @ जोहान्स ने लिखा है, इसका उपयोग करना संभव है vec![]क्योंकि:

  • Vec<T>IntoIterator<T>गुण को लागू करता है
  • HashMap<K, V> औजार FromIterator<Item = (K, V)>

जिसका मतलब है कि आप यह कर सकते हैं:

let map: HashMap<String, String> = vec![("key".to_string(), "value".to_string())]
    .into_iter()
    .collect();

आप उपयोग कर सकते हैं, &strलेकिन आपको जीवनकाल को एनोटेट करने की आवश्यकता हो सकती है यदि यह नहीं है 'static:

let map: HashMap<&str, usize> = vec![("one", 1), ("two", 2)].into_iter().collect();

क्या यह द्विआधारी में वेक्टर शाब्दिक का प्रतिनिधित्व नहीं करेगा और फिर इसे रनटाइम पर हैशमैप में इकट्ठा करेगा?
एलेक्स एलियास

1

आप velcroटोकरा * का उपयोग कर सकते हैं । यह maplitअन्य उत्तरों में अनुशंसित के समान है, लेकिन अधिक संग्रह प्रकारों के साथ, बेहतर सिंटैक्स (कम से कम मेरी राय में!) और अधिक सुविधाओं के साथ।

यह मानते हुए कि आप Stringइसके बजाय s का उपयोग करना चाहते हैं &str, आपका सटीक उदाहरण इस तरह दिखाई देगा:

use std::collections::HashMap;
use velcro::hash_map;

struct Node {
    name: String
    children: HashMap<String, Node>,
}

let map = hash_map! {
    String::from("element0"): Node {
        name: "My New Element".into(),
        children: hash_map! {
            String::from("child0"): Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

यह थोड़ा बदसूरत है क्योंकि Stringनिर्माण कैसे किया जाता है। लेकिन कुंजी प्रकार को बदले बिना, इसे थोड़ा साफ किया जा सकता है, जिसके उपयोग hash_map_from!से स्वचालित रूप से रूपांतरण होगा:

use std::collections::HashMap;
use velcro::{hash_map, hash_map_from};

let map: HashMap<String, Node> = hash_map_from! {
    "element0": Node {
        name: "My New Element".into(),
        children: hash_map_from! {
            "child0": Node {
                name: "child0".into(),
                children: hash_map!{}
            }
        }
    }
};

जो गो संस्करण की तुलना में बहुत अधिक क्रिया नहीं है


* पूर्ण प्रकटीकरण: मैं इस टोकरे का लेखक हूं।


0

एक तत्व के लिए

यदि आप मानचित्र को एक पंक्ति में केवल एक तत्व के साथ शुरू करना चाहते हैं (और आपके कोड में दृश्य उत्परिवर्तन के बिना), तो आप कर सकते हैं:

let map: HashMap<&'static str, u32> = Some(("answer", 42)).into_iter().collect();

यह Optionएक Iteratorप्रयोग बनने में सक्षम होने की उपयोगिता के लिए धन्यवाद हैinto_iter()

वास्तविक कोड में, आपको संभवतः कंपाइलर को टाइप के साथ मदद करने की आवश्यकता नहीं है:

use std::collections::HashMap;

fn john_wick() -> HashMap<&'static str, u32> {
    Some(("answer", 42)).into_iter().collect()
}

fn main() {
    let result = john_wick();

    let mut expected = HashMap::new();
    expected.insert("answer", 42);

    assert_eq!(result, expected);
}

इसे चेन करने का एक तरीका यह भी है कि एक से अधिक तत्वों को कुछ करने की तरह है Some(a).into_iter().chain(Some(b).into_iter()).collect(), लेकिन यह अधिक लंबा है, कम पठनीय है और शायद कुछ अनुकूलन मुद्दे हैं, इसलिए मैं इसके खिलाफ सलाह देता हूं।


-2

मैंने फैंसी समाधानों का एक गुच्छा देखा है, लेकिन मुझे बस कुछ सरल चाहिए था। उस छोर तक, यहाँ एक विशेषता है:

use std::collections::HashMap;

trait Hash {
   fn to_map(&self) -> HashMap<&str, u16>;
}

impl Hash for [(&str, u16)] {
   fn to_map(&self) -> HashMap<&str, u16> {
      self.iter().cloned().collect()
   }
}

fn main() {
   let m = [("year", 2019), ("month", 12)].to_map();
   println!("{:?}", m)
}

मुझे लगता है कि यह एक अच्छा विकल्प है, क्योंकि यह अनिवार्य रूप से रूबी और निम द्वारा पहले से ही इस्तेमाल किया गया है:


2
"मुझे लगता है कि यह एक अच्छा विकल्प है, क्योंकि इसके अनिवार्य रूप से पहले से ही रूबी और निम द्वारा उपयोग किए जाने वाले व्हाट्सएप" मुझे समझ नहीं आता कि यह एक तर्क कैसे है? रूबी या निम ऐसा क्यों करते हैं यह एक बेहतर तर्क होगा "बस वे ऐसा करते हैं तो यह अच्छा है"
स्टारगेटर

तर्क के बिना एक राय एसओ जवाब में उपयोगी नहीं है
स्टारगॉटर

2
एक विवरण: विशेषता को "हैश" कहना एक बुरा विचार है: उस नाम के साथ पहले से ही एक विशेषता है और जंग में हैशिंग के साथ काम करने पर आप तेजी से इसका सामना करेंगे।
डेनसिटी सेगुरेट

2
मैं यह भी सलाह copied()दूंगा कि जिस प्रकार की नकल पर अमल न करना पसंद है, उसे लागू न करने के लिए, कुछ भी नहीं करने के लिए क्लोनिंग से बचें, या कम से कम इसे एक क्लोन_टो_मैप () का उपयोग करके स्पष्ट करें यदि आप भी केवल क्लोन प्रकार के साथ ऐसा करने में सक्षम होना चाहते हैं।
स्टारगिटर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.