लूप के लिए रेंज के साथ उपयोग के लिए C ++ 11 में एक श्रेणी वर्ग है?


101

मैंने खुद को कुछ समय पहले लिखा था:

template <long int T_begin, long int T_end>
class range_class {
 public:
   class iterator {
      friend class range_class;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return iterator(T_begin); }
   iterator end() const { return iterator(T_end); }
};

template <long int T_begin, long int T_end>
const range_class<T_begin, T_end>
range()
{
   return range_class<T_begin, T_end>();
}

और यह मुझे इस तरह की चीजें लिखने की अनुमति देता है:

for (auto i: range<0, 10>()) {
    // stuff with i
}

अब, मुझे पता है कि मैंने जो लिखा है वह शायद सबसे अच्छा कोड नहीं है। और शायद इसे और अधिक लचीला और उपयोगी बनाने का एक तरीका है। लेकिन यह मुझे ऐसा लगता है जैसे इस मानक का हिस्सा बनाया जाना चाहिए था।

तो यह है? क्या पूर्णांक की एक सीमा पर पुनरावृत्तियों के लिए कुछ नई लाइब्रेरी जोड़ी गई थी, या शायद गणना किए गए स्केलर मानों की एक सामान्य श्रेणी?


17
+1। मैं अपनी उपयोगिताओं में ऐसी कक्षाएं लगाना चाहूंगा। :-)
नवाज

2
वैसे, rangeटेम्पलेट फ़ंक्शन लिखने की बात क्या है ? यह उस उपयोग के लिए कुछ भी नहीं जोड़ता है जिसका उपयोग range_classकिया जाता है। मेरा मतलब है, range<0,10>()और range_class<0,10>()बिल्कुल एक जैसा दिखता है!
नवाज

2
@ नवाज़: हाँ, आप सही कह रहे हैं। मेरे पास कुछ विषम दृष्टि थी कि मैं फ़ंक्शन को गतिशील और स्थिर मामले के बीच अंतर कर सकता था, लेकिन मुझे नहीं लगता कि यह किया जा सकता है।
सर्वव्यापी

2
@iililind: नवाज ने आपसे यही सवाल पूछा 35 मिनट आगे;)
सेबस्टियन मच

3
पांडित्यपूर्ण होने के लिए मुझे लगता है कि इस कार्यान्वयन में एक बग है, जो यह है कि आप इसका उपयोग पूरे पूर्णांक सीमा पर पुनरावृति करने के लिए नहीं कर सकते। यदि आप INT_MIN और INT_MAX को अपने टेम्प्लेट तर्कों के रूप में प्लग करते हैं, तो जब इंप्रूव किया गया INT_MAX, INT_MIN देगा और अनंत लूप देगा। एसटीएल में "अंत" माना जाता है कि "एक अंत पिछले" जो पूर्णांक प्रकार के अंदर फिट नहीं हो सकता है, इसलिए मुझे नहीं पता है कि यह वास्तव में किसी दिए गए प्लेटफ़ॉर्म पर व्यापक पूर्णांक प्रकार के लिए कुशलतापूर्वक लागू किया जा सकता है। छोटे पूर्णांक प्रकारों के लिए आप हमेशा इसे आंतरिक रूप से एक व्यापक प्रकार का उपयोग कर सकते हैं ...
जोसेफ गार्विन

जवाबों:


59

C ++ मानक पुस्तकालय में एक नहीं है, लेकिन Boost.Range में :: काउंटिंग_रेन्ग को बढ़ावा दिया गया है , जो निश्चित रूप से योग्य है। आप बूस्ट :: इरेंज का भी इस्तेमाल कर सकते हैं , जो थोड़ा ज्यादा स्कोप है।

C ++ 20 की रेंज लाइब्रेरी आपको इसके माध्यम से करने की अनुमति देगा view::iota(start, end)


3
हां, यह निश्चित रूप से प्रकृति है कि मैं क्या देख रहा हूं। मुझे खुशी है कि बूस्ट ने ऐसा किया। मुझे दुख है कि मानक समिति ने इसे किसी भी कारण से शामिल नहीं किया। यह रेंज-बेस-फॉर फीचर का एक बेहतरीन पूरक रहा होगा।
सर्वव्यापी

यह उत्तर मेरे प्रत्यक्ष प्रश्न का बेहतर उत्तर देता है, और इसलिए मैं इसे चुनूंगा, भले ही नवाज़ का उत्तर बहुत अच्छा हो।
सर्वव्यापी

6
मानक (N4128) में पर्वतमाला प्राप्त करने के लिए हाल ही में बहुत प्रगति हुई है। प्रस्ताव और संदर्भ कार्यान्वयन के लिए github.com/ericniebler/range-v3 देखें ।
इला 782

1
@ Ela782: ... और अभी तक ऐसा लगता है कि हम इसे C ++ 17 में नहीं देखेंगे, है ना?
ईनपोकलुम

1
@ और हां, पर्वतमाला ने इसे कुछ समय पहले एक टीएस में बनाया था, लेकिन मुझे नहीं लगता कि कोई संदर्भ कार्यान्वयन था जो इसे std::experimental::rangesनामस्थान के तहत प्रमुख कंपाइलरों में बनाया गया था । range-v3हमेशा संदर्भ कार्यान्वयन की तरह था मैं कहूँगा। लेकिन अब मेरा मानना ​​है कि मूल श्रेणी के सामान को भी हाल ही में C ++ 20 में वोट दिया गया है, इसलिए हम वास्तव में इसे std::जल्द ही प्राप्त करेंगे ! :-)
एला --२ --२

47

जहाँ तक मुझे पता है, C ++ 11 में ऐसा कोई वर्ग नहीं है।

वैसे भी, मैंने आपके कार्यान्वयन को बेहतर बनाने का प्रयास किया। मैंने इसे गैर-टेम्प्लेट बनाया , क्योंकि मुझे इसे टेम्प्लेट बनाने में कोई फ़ायदा नहीं हुआ । इसके विपरीत, इसका एक प्रमुख नुकसान है: कि आप रनटाइम पर सीमा नहीं बना सकते हैं, जैसा कि आपको संकलन समय पर टेम्पलेट तर्कों को जानने की आवश्यकता है।

//your version
auto x = range<m,n>(); //m and n must be known at compile time

//my version
auto x = range(m,n);  //m and n may be known at runtime as well!

यहाँ कोड है:

class range {
 public:
   class iterator {
      friend class range;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return begin_; }
   iterator end() const { return end_; }
   range(long int  begin, long int end) : begin_(begin), end_(end) {}
private:
   iterator begin_;
   iterator end_;
};

टेस्ट कोड:

int main() {
      int m, n;
      std::istringstream in("10 20");
      if ( in >> m >> n ) //using in, because std::cin cannot be used at coliru.
      {
        if ( m > n ) std::swap(m,n); 
        for (auto i : range(m,n)) 
        {
             std::cout << i << " ";
        }
      }
      else 
        std::cout <<"invalid input";
}

आउटपुट:

10 11 12 13 14 15 16 17 18 19

ओनइन डेमो


3
मुझें यह पसंद है। मैंने एक गैर-टेम्पलेट संस्करण के बारे में सोचा। और मुझे लगता है कि एक अच्छा संकलक इसे मामले में अच्छी तरह से अनुकूलित करेगा जब मूल्य वास्तव में स्थिर होते हैं। मुझे इसका परीक्षण करना होगा।
सर्वव्यापी

10
@Nawaz: मैं अभी भी यह टेम्पलेट चाहते हैं, अभिन्न प्रकार पर :) मैं भी उर्फ करने का प्रस्ताव करता हूँ iteratorकरने के लिए const_iterator, राशि iteratorसे निकाले जाते हैं std::iteratorऔर rangeलागू करने cbeginऔर cend। ओह और ... क्यों करता है iterator::operator++एक रिटर्न स्थिरांक संदर्भ?
Matthieu M.

6
@RedX: डिज्कस्ट्रा एक अच्छा लेख क्यों रेंज लेबलिंग के रूप में सबसे अच्छा है पर है [begin, end)। @OP: +1 रेंज-आधारित छोरों पर सजा के लिए जो कि एक वाक्य नहीं है :-)
Kerrek SB

2
गैर-टेम्प्लेट संस्करण का लाभ यह है कि आपके छोरों की लंबाई को संकलन समय पर ज्ञात करने की आवश्यकता नहीं है। आप पूर्णांक प्रकार को अस्थायी बना सकते हैं, निश्चित रूप से।
कैशबैक

2
@weeska: उस ओवरलोड को पोस्टफिक्स इंक्रीमेंट लागू करना है, v++जो कि इंक्रीमेंट ऑपरेशन होने से पहले वैल्यू को वापस करना है। मैं आपको सलाह दूंगा कि आपस में अंतर का पता लगाएं ++iऔर i++कहां iघोषित किया जाता है int
नवाज

13

मैंने एक पुस्तकालय को rangeठीक उसी उद्देश्य के लिए लिखा था जिसे छोड़कर यह एक रन-टाइम रेंज है, और मेरे मामले में विचार पायथन से आया है। मैंने एक संकलन-समय संस्करण पर विचार किया, लेकिन मेरी विनम्र राय में संकलन-समय संस्करण को हासिल करने का कोई वास्तविक लाभ नहीं है। आप लाइब्रेरी को बिटकॉइन पर पा सकते हैं, और यह बूस्ट लाइसेंस: रेंज के तहत है । यह एक शीर्ष लेख पुस्तकालय है, जो C ++ 03 के साथ संगत है और C ++ 11 में छोरों के लिए रेंज-आधारित के साथ आकर्षण की तरह काम करता है :)

विशेषताएं :

  • सभी घंटियाँ और सीटी के साथ एक सच्चे यादृच्छिक अभिगमन कंटेनर!

  • रंग की तुलना शाब्दिक रूप से की जा सकती है।

  • एक नंबर के अस्तित्व की जांच करने के लिए दो कार्य exist(रिटर्न बूल), और find(पुनरावृत्ति रिटर्न)।

  • CATCH का उपयोग करके पुस्तकालय का यूनिट-परीक्षण किया जाता है ।

  • बुनियादी उपयोग के उदाहरण, मानक कंटेनरों के साथ काम करना, मानक एल्गोरिदम के साथ काम करना और छोरों के लिए सीमा के साथ काम करना।

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


एक मिनट का परिचय कहता है कि मेरे पास विकी की पहुंच नहीं है। आपको अपनी विकि को सार्वजनिक करने की आवश्यकता है।
निकोल बोल 20

@ नाइकोल बोलस मैं वास्तव में माफी चाहता हूं, यह अब सार्वजनिक है :)
आरा

इसके लिए धन्यवाद, यह आश्चर्यजनक है। मुझे ऐसा लगता है कि अधिक लोगों को इसके बारे में पता होना चाहिए।
राफेल किटओवर

5

मैंने पाया कि boost::irangeविहित पूर्णांक लूप की तुलना में बहुत धीमा था। इसलिए मैं एक प्रीप्रोसेसर मैक्रो का उपयोग करते हुए निम्नलिखित बहुत सरल समाधान पर बस गया:

#define RANGE(a, b) unsigned a=0; a<b; a++

तो आप इस तरह से पाश कर सकते हैं:

for(RANGE(i, n)) {
    // code here
}

यह सीमा स्वतः शून्य से शुरू होती है। इसे दिए गए नंबर से शुरू करने के लिए आसानी से बढ़ाया जा सकता है।


7
ध्यान दें कि for (RANGE(i, flag? n1: n2))आश्चर्यजनक परिणाम मिलेंगे, क्योंकि आप गैर-ईविल मैक्रों के बुनियादी नियमों में से एक का पालन करने में विफल रहे, जो आपके सभी मापदंडों (इस मामले में, सहित b) को कोष्ठक करना है । आपका दृष्टिकोण गैर-मैक्रो, "रेंज ऑब्जेक्ट"-आधारित दृष्टिकोण (जैसे नवाज का जवाब ) पर कोई प्रदर्शन लाभ प्रदान नहीं करता है ।
क्क्सप्लसोन

2

यहाँ एक सरल रूप है जो मेरे लिए अच्छी तरह से काम कर रहा है। क्या मेरे दृष्टिकोण में कोई जोखिम हैं?

r_iteratorएक प्रकार है जो व्यवहार करता है, जितना संभव हो, जैसे long int। इसलिए कई ऑपरेटर जैसे ==और ++, बस से गुजरते हैं long int। मैं operator long intऔर operator long int &रूपांतरणों के माध्यम से अंतर्निहित लंबे int को 'बेनकाब' करता हूं ।

#include <iostream>
using namespace std;

struct r_iterator {
        long int value;
        r_iterator(long int _v) : value(_v) {}
        operator long int () const { return value; }
        operator long int& ()      { return value; }
        long int operator* () const { return value; }
};
template <long int _begin, long int _end>
struct range {
        static r_iterator begin() {return _begin;}
        static r_iterator end  () {return _end;}
};
int main() {
        for(auto i: range<0,10>()) { cout << i << endl; }
        return 0;
}

( संपादित करें: - हम rangeस्थिरांक के बजाय स्थैतिक के तरीके बना सकते हैं ।)


1

यह थोड़ी देर हो सकती है लेकिन मैंने अभी इस प्रश्न को देखा है और मैं अभी कुछ समय के लिए इस वर्ग का उपयोग कर रहा हूं:

#include <iostream>
#include <utility>
#include <stdexcept>

template<typename T, bool reverse = false> struct Range final {
    struct Iterator final{
        T value;
        Iterator(const T & v) : value(v) {}
        const Iterator & operator++() { reverse ? --value : ++value; return *this; }
        bool operator!=(const Iterator & o) { return o.value != value; }
        T operator*() const { return value; }
    };
    T begin_, end_;
    Range(const T & b, const T & e)  : begin_(b), end_(e) {
        if(b > e) throw std::out_of_range("begin > end");
    }

    Iterator begin() const { return reverse ? end_ -1 : begin_; }
    Iterator end() const { return reverse ? begin_ - 1: end_; }

    Range() = delete;
    Range(const Range &) = delete;
};

using UIntRange = Range<unsigned, false>;
using RUIntRange = Range<unsigned, true>;

उपयोग:

int main() {
    std::cout << "Reverse : ";
    for(auto i : RUIntRange(0, 10)) std::cout << i << ' ';
    std::cout << std::endl << "Normal : ";
    for(auto i : UIntRange(0u, 10u)) std::cout << i << ' ';
    std::cout << std::endl;
}

0

क्या आपने प्रयोग करने की कोशिश की है

template <class InputIterator, class Function>
   Function for_each (InputIterator first, InputIterator last, Function f);

ज्यादातर समय बिल फिट बैठता है।

उदाहरण के लिए

template<class T> void printInt(T i) {cout<<i<<endl;}
void test()
{
 int arr[] = {1,5,7};
 vector v(arr,arr+3);

 for_each(v.begin(),v.end(),printInt);

}

ध्यान दें कि PrintInt OFC को C ++ 0x में एक लैम्ब्डा से बदला जा सकता है। इसके अलावा इस उपयोग की एक और छोटी विविधता (random_iterator के लिए सख्ती से) हो सकती है

 for_each(v.begin()+5,v.begin()+10,printInt);

Fwd के लिए केवल पुनरावृत्त

 for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);

आप इसका उपयोग कैसे करेंगे? मैं अनुमान लगा रहा हूं कि आप फ़ंक्शन के लिए एक लैम्ब्डा का उपयोग करेंगे, लेकिन मुझे यकीन नहीं है।
सर्वव्यापी

1
हां बताओगे, लेकिन आप इसका उपयोग करने का सही तरीका सोचते हैं तो आपको जवाब स्वीकार करना होगा। : पी किडिंग। उदाहरण पहले से ही पोस्ट किया है।
अजीत गंगा

आप यहाँ एक लैम्ब्डा का उपयोग कर सकते हैं इसलिए ऑटो रेंज = myMultiMap.equal_range (कुंजी); for_each (range.first, range.second, [&] (घोषणापत्र (* range.first) कास्ट एंड आइटम) {// कोड यहाँ जाता है});
कैशबैक

-3

आप आसानी से C ++ 11 में std :: iota () का उपयोग करके एक बढ़ता क्रम उत्पन्न कर सकते हैं:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

template<typename T>
std::vector<T> range(T start, T end)
{
  std::vector<T> r(end+1-start, T(0));
  std::iota(r.begin(), r.end(), T(start));//increasing sequence
  return r;
}

int main(int argc, const char * argv[])
{
  for(auto i:range<int>(-3,5))
    std::cout<<i<<std::endl;

  return 0;
}

3
rangeवर्ग रेंज मॉडल होगा। हालाँकि आप सचमुच इसका निर्माण कर रहे हैं। यह मेमोरी और मेमोरी एक्सेस की बर्बादी है। समाधान अत्यधिक निरर्थक है, क्योंकि वेक्टर तत्वों की संख्या और पहले तत्व (यदि यह मौजूद है) के मूल्य को छोड़कर कोई वास्तविक जानकारी नहीं रखता है।
उपयोगकर्ता

हां, यह बहुत अक्षम है।
सर्वव्यापी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.