2012-07-04 16 views
21

संभव डुप्लिकेट:
Find position of element in C++11 range-based for loop?अजगर की तरह पाश गणन

मैं एक vector है और मैं यह पुनरावृति और एक ही समय में, चाहते हैं, के लिए उपयोग किया प्रत्येक व्यक्तिगत तत्व के लिए अनुक्रमणिका (मुझे एक तत्व में तत्व और इसकी अनुक्रमणिका दोनों को पारित करने की आवश्यकता है)। मैंने निम्नलिखित दो समाधानों पर विचार किया है:

std::vector<int> v = { 10, 20, 30 }; 

// Solution 1 
for (std::vector<int>::size_type idx = 0; idx < v.size(); ++idx) 
    foo(v[idx], idx); 

// Solution 2 
for (auto it = v.begin(); it != v.end(); ++it) 
    foo(*it, it - v.begin()); 

मैं सोच रहा था कि कोई और कॉम्पैक्ट समाधान हो सकता है या नहीं। पाइथन के enumerate के समान कुछ। यह सबसे नज़दीकी है कि मुझे सी ++ 11 रेंज-लूप का उपयोग करना पड़ा, लेकिन एक निजी दायरे में लूप के बाहर इंडेक्स को परिभाषित करना निश्चित रूप से 1 या 2:

{ 
    int idx = 0; 
    for (auto& elem : v) 
     foo(elem, idx++); 
} 
से अधिक खराब समाधान की तरह प्रतीत होता है

क्या नवीनतम उदाहरण को सरल बनाने के लिए कोई तरीका है (शायद बूस्ट का उपयोग करके) इस तरह से सूचकांक लूप में स्वयं निहित हो जाता है?

+5

सरल चीजों को सरल क्यों बनाएं? :-) – Kos

+0

आपको जेनरेटर-जैसे फ़ंक्शन/ऑब्जेक्ट बनाना होगा जो std :: जोड़ी देता है और जोड़ी के पहले और दूसरे फ़ील्ड का उपयोग करता है। आप शायद चाल करने के लिए मैक्रोज़ का उपयोग कर सकते हैं, लेकिन सी ++ में पायथन-जैसी वाक्यविन्यास का उपयोग करने के लिए कोई आसान और सुरुचिपूर्ण तरीका नहीं है। आपका दूसरा समाधान शायद सबसे अच्छी बात है। – Morwenn

+0

@ कोस मैं समाधान के साथ काफी ठीक हूं 2. अगर एक आसान तरीका भी है तो बस उत्सुक :) – betabandido

उत्तर

10

रूप @Kos कहते हैं, इस तरह के एक सरल बात यह है कि मैं वास्तव में, यह आगे सरल करने के लिए की जरूरत नहीं दिख रहा है और व्यक्तिगत रूप से सिर्फ सूचकांक के साथ पाश के लिए पारंपरिक से चिपके हैं, सिवाय इसके कि मैं खाई होता है std::vector<T>::size_type और बस std::size_t का उपयोग करें:

for(std::size_t i = 0; i < v.size(); ++i) 
    foo(v[i], i); 

मैं समाधान 2. पर भी उत्सुक नहीं कर रहा हूँ यह जरूरी है (थोड़े छिपा हुआ) रैंडम एक्सेस iterators जिसे आप आसानी से कंटेनर, जो मजबूत में से एक है स्वैप करने के लिए अनुमति नहीं होगी इटरेटर के अंक। आप iterators उपयोग करें और यह सामान्य बनाने (और संभवतः एक प्रदर्शन हिट उठाना जब iterators नहीं रैंडम एक्सेस कर रहे हैं) चाहते हैं, मैं std::distance का उपयोग कर की सलाह देते हैं:

for(auto it(v.begin()); it != v.end(); ++it) 
    foo(*it, std::distance(it, v.begin()); 
+3

यह देखते हुए कि पाइथन के गणना के करीब कहीं भी पहुंचने का कोई भी प्रयास एक विशाल कोड ब्लोट में समाप्त होता है, मुझे लगता है कि इन दोनों समाधानों में से किसी एक का उपयोग करना बेहतर है। – betabandido

1

एक तरीका है अपने आप के एक समारोह में लूप को लपेटना।

#include <iostream> 
#include <vector> 
#include <string> 

template<typename T, typename F> 
void mapWithIndex(std::vector<T> vec, F fun) { 
    for(int i = 0; i < vec.size(); i++) 
     fun(vec[i], i); 
} 

int main() { 
    std::vector<std::string> vec = {"hello", "cup", "of", "tea"}; 
    mapWithIndex(vec, [](std::string s, int i){ 
     std::cout << i << " " << s << '\n'; 
    }); 
} 
+1

आईएमओ यह केवल चीजों को जटिल बनाता है ... – SingerOfTheFall

+2

आप एक उचित बिंदु बनाते हैं। आम तौर पर, लूप के लिए एक सादा सबसे अच्छा है। जाहिर है ओपी हालांकि एक नहीं चाहता है। –

+1

मैं वास्तव में कोड को और सरल बनाना चाहता था (यदि संभव हो तो)। 'idx के लिए, ग्यारह में elem (v): foo (idx, elem) 'मुझे मेरे प्रश्न में या उत्तर में पोस्ट किए गए किसी भी अन्य समाधान की तुलना में सरल लगता है। लेकिन, ज़ाहिर है, यह एक पायथन समाधान है, और मैं एक सी ++ के लिए पूछ रहा था। – betabandido

13

यहाँ का उपयोग कर अजीब समाधान के कुछ प्रकार है आलसी मूल्यांकन। , तब

template<typename Iterable> 
class enumerate_object 
{ 
    private: 
     Iterable _iter; 
     std::size_t _size; 
     decltype(std::begin(_iter)) _begin; 
     const decltype(std::end(_iter)) _end; 

    public: 
     enumerate_object(Iterable iter): 
      _iter(iter), 
      _size(0), 
      _begin(std::begin(iter)), 
      _end(std::end(iter)) 
     {} 

     const enumerate_object& begin() const { return *this; } 
     const enumerate_object& end() const { return *this; } 

     bool operator!=(const enumerate_object&) const 
     { 
      return _begin != _end; 
     } 

     void operator++() 
     { 
      ++_begin; 
      ++_size; 
     } 

     auto operator*() const 
      -> std::pair<std::size_t, decltype(*_begin)> 
     { 
      return { _size, *_begin }; 
     } 
}; 

एक आवरण समारोह की गणना उस टेम्पलेट तर्क अनुमान और जनरेटर वापस आ जाएगी बनाएँ:: सबसे पहले, जनरेटर वस्तु enumerate_object निर्माण

template<typename Iterable> 
auto enumerate(Iterable&& iter) 
    -> enumerate_object<Iterable> 
{ 
    return { std::forward<Iterable>(iter) }; 
} 

अब आप अपने कार्य का उपयोग कर सकते हैं कि जिस तरह से:

int main() 
{ 
    std::vector<double> vec = { 1., 2., 3., 4., 5. }; 
    for (auto&& a: enumerate(vec)) { 
     size_t index = std::get<0>(a); 
     double& value = std::get<1>(a); 

     value += index; 
    } 
} 

कार्यान्वयन ऊपर एक मात्र खिलौना है: यह दोनों const और गैर const lvalue-संदर्भ के साथ काम करना चाहिए एस के साथ-साथ रावल-रेफरेंस, लेकिन बाद के लिए वास्तविक लागत है, हालांकि यह मानते हुए कि यह कई बार ऑब्जेक्ट ऑब्जेक्ट की प्रतिलिपि बनाता है। यह समस्या निश्चित रूप से अतिरिक्त tweaks के साथ हल किया जा सकता है।

int main() 
{ 
    std::vector<double> vec = { 1., 2., 3., 4., 5. }; 
    for (auto&& [index, value] a: enumerate(vec)) { 
     value += index; 
    } 
} 

मैं एक सी नहीं है:

के बाद से सी ++ 17, अपघटन घोषणाओं भी आप शांत अजगर की तरह वाक्य रचना सूचकांक और for प्रारंभकर्ता में सीधे मूल्य के नाम पर रखने की अनुमति ++ 17-compliant संकलक इसे जांचने के लिए हाथ में है, लेकिन मुझे उम्मीद है कि auto&& अपघटन में indexstd::size_t और valuedouble& के रूप में अनुमान लगाने में सक्षम है।

+0

यदि आप अस्थायी रूप से 'enumerate' पास करते हैं तो आपका कोड बहुत ही उड़ा देगा। – Xeo

+0

आप किस कंपाइलर का उपयोग कर रहे हैं? यह जी ++ 4.6 या 4.7 के साथ संकलित नहीं है। – betabandido

+0

@Xeo यह मजाकिया नहीं है? आप शायद सही हैं और मैं वास्तव में एक सुरक्षित संस्करण बनाने के लिए नहीं देखता हूं। वैसे भी, उस फ़ंक्शन का उपयोग सादे पुराने समाधान के रूप में आसान नहीं है। – Morwenn