पायथन जनरेटर पैटर्न के बराबर सी ++


117

मुझे कुछ उदाहरण पायथन कोड मिले हैं जिनकी मुझे C ++ में नकल करने की आवश्यकता है। मुझे किसी विशिष्ट समाधान की आवश्यकता नहीं है (जैसे सह-दिनचर्या आधारित उपज समाधान, हालांकि वे स्वीकार्य उत्तर भी होंगे), मुझे बस कुछ तरीकों से शब्दार्थ को पुन: प्रस्तुत करने की आवश्यकता है।

अजगर

यह एक बुनियादी अनुक्रम जनरेटर है, स्पष्ट रूप से एक भौतिक संस्करण को स्टोर करने के लिए बहुत बड़ा है।

def pair_sequence():
    for i in range(2**32):
        for j in range(2**32):
            yield (i, j)

लक्ष्य ऊपर के अनुक्रम के दो उदाहरणों को बनाए रखना है, और अर्ध-लॉकस्टेप में उन पर पुनरावृति करना है, लेकिन विखंडू में। नीचे दिए गए उदाहरण first_passमें बफर को आरम्भ करने के लिए जोड़े के अनुक्रम का उपयोग किया जाता है, और उसी सटीक अनुक्रम को second_passपुन: बनाता है और बफर को फिर से संसाधित करता है।

def run():
    seq1 = pair_sequence()
    seq2 = pair_sequence()

    buffer = [0] * 1000
    first_pass(seq1, buffer)
    second_pass(seq2, buffer)
    ... repeat ...

सी ++

केवल एक चीज जो मैं C ++ में समाधान के लिए पा सकता हूं, वह yieldC ++ कोरआउट्स के साथ नकल करना है, लेकिन मुझे यह कैसे करना है पर कोई अच्छा संदर्भ नहीं मिला है। मुझे इस समस्या के वैकल्पिक (गैर सामान्य) समाधानों में भी दिलचस्पी है। पास के बीच अनुक्रम की एक प्रति रखने के लिए मेरे पास पर्याप्त मेमोरी बजट नहीं है।


जैसा कि आप यहाँ से देख सकते हैं stackoverflow.com/questions/3864410/… coroutine को लागू करना अच्छा विचार नहीं है। क्या आप इसे बफ़र्ड रीडिंग के साथ नहीं कर सकते? stackoverflow.com/questions/4685862/…
बैटबैटटर

C ++ पुनरावृत्तियों को इस तरह से कुछ का समर्थन करना चाहिए।
लालचंद

जवाबों:


79

जेनरेटर C ++ में मौजूद हैं, बस एक और नाम के तहत: इनपुट Iterators । उदाहरण के लिए, से पढ़ना std::cinएक जनरेटर के समान हैchar

आपको बस यह समझने की जरूरत है कि एक जनरेटर क्या करता है:

  • डेटा का एक समूह है: स्थानीय चर एक राज्य को परिभाषित करते हैं
  • एक init विधि है
  • एक "अगला" तरीका है
  • समाप्ति का संकेत देने का एक तरीका है

आपके तुच्छ उदाहरण में, यह काफी आसान है। सैद्धांतिक रूप:

struct State { unsigned i, j; };

State make();

void next(State&);

bool isDone(State const&);

बेशक, हम इसे एक उचित वर्ग के रूप में लपेटते हैं:

class PairSequence:
    // (implicit aliases)
    public std::iterator<
        std::input_iterator_tag,
        std::pair<unsigned, unsigned>
    >
{
  // C++03
  typedef void (PairSequence::*BoolLike)();
  void non_comparable();
public:
  // C++11 (explicit aliases)
  using iterator_category = std::input_iterator_tag;
  using value_type = std::pair<unsigned, unsigned>;
  using reference = value_type const&;
  using pointer = value_type const*;
  using difference_type = ptrdiff_t;

  // C++03 (explicit aliases)
  typedef std::input_iterator_tag iterator_category;
  typedef std::pair<unsigned, unsigned> value_type;
  typedef value_type const& reference;
  typedef value_type const* pointer;
  typedef ptrdiff_t difference_type;

  PairSequence(): done(false) {}

  // C++11
  explicit operator bool() const { return !done; }

  // C++03
  // Safe Bool idiom
  operator BoolLike() const {
    return done ? 0 : &PairSequence::non_comparable;
  }

  reference operator*() const { return ij; }
  pointer operator->() const { return &ij; }

  PairSequence& operator++() {
    static unsigned const Max = std::numeric_limts<unsigned>::max();

    assert(!done);

    if (ij.second != Max) { ++ij.second; return *this; }
    if (ij.first != Max) { ij.second = 0; ++ij.first; return *this; }

    done = true;
    return *this;
  }

  PairSequence operator++(int) {
    PairSequence const tmp(*this);
    ++*this;
    return tmp;
  }

private:
  bool done;
  value_type ij;
};

तो हाँ, ... हो सकता है कि सी ++ एक और अधिक क्रिया है :)


2
मैंने आपका उत्तर (धन्यवाद!) स्वीकार कर लिया क्योंकि यह मेरे द्वारा दिए गए प्रश्न के लिए तकनीकी रूप से सही है। क्या आपके पास उन मामलों में तकनीकों के लिए कोई संकेत है जहां उत्पन्न होने वाले अनुक्रम अधिक जटिल हैं, या क्या मैं सी ++ के साथ यहां एक मृत घोड़े की पिटाई कर रहा हूं और वास्तव में कोरियोटीन सामान्यता का एकमात्र तरीका है?
नूह वाटकिंस

3
@NoahWatkins: जब भाषाएं उनका समर्थन करती हैं तो कोरटाइन एक आसान कार्यान्वयन के लिए बनाते हैं। दुर्भाग्य से C ++ नहीं है, इसलिए पुनरावृत्ति आसान है। यदि आपको वास्तव में कोरआउट की आवश्यकता है, तो आपको वास्तव में पक्ष पर अपने फ़ंक्शन कॉल के "स्टैक" को रखने के लिए एक पूर्ण विकसित धागे की आवश्यकता है। यह निश्चित रूप से कीड़े के खुले इस तरह के एक कर सकते हैं करने के लिए overkill है बस कि इस उदाहरण में के लिए, लेकिन आपका माइलेज अपने वास्तविक जरूरतों के आधार पर भिन्न हो सकते हैं।
मैथ्यू एम।

1
गैर-थ्रेड-आधारित कोरूटाइन कार्यान्वयन बूस्ट बूस्ट. org / doc
बॉयसी

2
@ लॉबी: वास्तव में कोरटाइन के लिए कई प्रस्ताव हैं, विशेष रूप से एक स्टैक-कम और दूसरा स्टैक-पूर्ण। यह दरार करने के लिए कठिन अखरोट है, इसलिए अब मैं इंतजार कर रहा हूं। हालांकि इस बीच, स्टैक-कम कोरआउट्स को इनपुट इटरेटर (बस, चीनी के बिना) के रूप में सीधे लागू किया जा सकता है।
मत्तीउह एम।

3
फिर भी समान रूप से, पुनरावृत्तियों जनरेटर के समान नहीं हैं।
Огћен Шобаји

52

C ++ में पुनरावृत्तियाँ हैं, लेकिन पुनरावृति लागू करना सीधा नहीं है: किसी को पुनरावृत्त अवधारणाओं से परामर्श करना होगा और उन्हें लागू करने के लिए नए पुनरावृत्त वर्ग को सावधानीपूर्वक डिज़ाइन करना होगा। शुक्र है, बूस्ट में एक iterator_facade टेम्प्लेट है जो पुनरावृत्तियों और इटरेटर-संगत जनरेटर को लागू करने में मदद करना चाहिए।

कभी-कभी एक स्टेटरलेस कोरटाइन का उपयोग इट्रेटर को लागू करने के लिए किया जा सकता है

PS इस लेख को भी देखें जिसमें ऑलिवर कोवल्के switchद्वारा क्रिस्टोफर एम। कोहलॉफ और बूस्ट.कोर्टाइन द्वारा की गई हैक दोनों का उल्लेख है । ओलिवर कोवल्के का काम Giovanni P. Deretta द्वारा Boost.Coroutine पर एक फॉलोअप है

PS मुझे लगता है कि आप लैम्बदास के साथ एक प्रकार का जनरेटर भी लिख सकते हैं :

std::function<int()> generator = []{
  int i = 0;
  return [=]() mutable {
    return i < 10 ? i++ : -1;
  };
}();
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

या फ़नकार के साथ:

struct generator_t {
  int i = 0;
  int operator() () {
    return i < 10 ? i++ : -1;
  }
} generator;
int ret = 0; while ((ret = generator()) != -1) std::cout << "generator: " << ret << std::endl;

पुनश्च यहाँ एक जनरेटर है जिसे कोरर कोरटाइन के साथ लागू किया गया है :

#include <iostream>
using std::cout; using std::endl;
#include <mordor/coroutine.h>
using Mordor::Coroutine; using Mordor::Fiber;

void testMordor() {
  Coroutine<int> coro ([](Coroutine<int>& self) {
    int i = 0; while (i < 9) self.yield (i++);
  });
  for (int i = coro.call(); coro.state() != Fiber::TERM; i = coro.call()) cout << i << endl;
}

22

चूंकि Boost.Coroutine2 अब इसे बहुत अच्छी तरह से समर्थन करता है (मुझे यह मिल गया क्योंकि मैं बिल्कुल उसी yieldसमस्या को हल करना चाहता था ), मैं C ++ कोड पोस्ट कर रहा हूं जो आपके मूल इरादे से मेल खाता है:

#include <stdint.h>
#include <iostream>
#include <memory>
#include <boost/coroutine2/all.hpp>

typedef boost::coroutines2::coroutine<std::pair<uint16_t, uint16_t>> coro_t;

void pair_sequence(coro_t::push_type& yield)
{
    uint16_t i = 0;
    uint16_t j = 0;
    for (;;) {
        for (;;) {
            yield(std::make_pair(i, j));
            if (++j == 0)
                break;
        }
        if (++i == 0)
            break;
    }
}

int main()
{
    coro_t::pull_type seq(boost::coroutines2::fixedsize_stack(),
                          pair_sequence);
    for (auto pair : seq) {
        print_pair(pair);
    }
    //while (seq) {
    //    print_pair(seq.get());
    //    seq();
    //}
}

इस उदाहरण में, pair_sequenceअतिरिक्त तर्क नहीं लेते हैं। यदि std::bindकिसी फ़ंक्शन ऑब्जेक्ट को उत्पन्न करने के लिए इसकी आवश्यकता है, या एक लैम्ब्डा का उपयोग किया जाना चाहिए , जो केवल एक तर्क (में) लेता हैpush_type ) का होता है, जब इसे coro_t::pull_typeकंस्ट्रक्टर को पास किया जाता है ।


ध्यान दें कि Coroutine2 को c ++ 11 की आवश्यकता है, जिसके लिए दृश्य स्टूडियो 2013 अपर्याप्त है क्योंकि यह केवल आंशिक रूप से समर्थित है।
वेस्टन

5

सभी उत्तर जो आपके स्वयं के इटिटर को लिखना शामिल है, पूरी तरह से गलत हैं। इस तरह के जवाब पूरी तरह से पायथन जनरेटर (भाषा की सबसे बड़ी और अनूठी विशेषताओं में से एक) के बिंदु को याद करते हैं। जनरेटर के बारे में सबसे महत्वपूर्ण बात यह है कि निष्पादन जहां इसे छोड़ देता है वहां उठाता है। यह पुनरावृत्तियों के लिए नहीं होता है। इसके बजाय, आपको मैन्युअल रूप से राज्य की जानकारी को स्टोर करना होगा जैसे कि ऑपरेटर ++ या ऑपरेटर * को नए सिरे से कहा जाता है, सही जानकारी अगले फ़ंक्शन कॉल की शुरुआत में होती है। यही कारण है कि अपने खुद के सी ++ इट्रेटर लिखना एक विशाल दर्द है; जबकि, जनरेटर सुरुचिपूर्ण हैं, और पढ़ना + लिखना आसान है।

मुझे नहीं लगता कि देशी सी ++ में पायथन जनरेटर के लिए एक अच्छा एनालॉग है, कम से कम अभी तक नहीं (एक अफवाह है कि उपज सी ++ 17 में उतरेगी )। आप थर्ड-पार्टी (जैसे योंगवेई के बूस्ट सुझाव) का सहारा लेकर, या अपना स्वयं का रोल करके कुछ समान पा सकते हैं।

मैं कहूंगा कि मूल C ++ में सबसे निकटतम चीज थ्रेड है। एक धागा स्थानीय चर के एक निलंबित सेट को बनाए रख सकता है, और जनरेटर को पसंद करते हुए निष्पादन को जारी रख सकता है, जहां इसे छोड़ दिया गया था, लेकिन जनरेटर ऑब्जेक्ट और इसके कॉलर के बीच संचार का समर्थन करने के लिए आपको अतिरिक्त बुनियादी ढांचे को रोल करने की आवश्यकता है। उदाहरण के लिए

// Infrastructure

template <typename Element>
class Channel { ... };

// Application

using IntPair = std::pair<int, int>;

void yield_pairs(int end_i, int end_j, Channel<IntPair>* out) {
  for (int i = 0; i < end_i; ++i) {
    for (int j = 0; j < end_j; ++j) {
      out->send(IntPair{i, j});  // "yield"
    }
  }
  out->close();
}

void MyApp() {
  Channel<IntPair> pairs;
  std::thread generator(yield_pairs, 32, 32, &pairs);
  for (IntPair pair : pairs) {
    UsePair(pair);
  }
  generator.join();
}

हालांकि इस समाधान में कई डाउनसाइड हैं:

  1. धागे "महंगे" हैं। ज्यादातर लोग इसे धागों का "असाधारण" उपयोग मानते हैं, खासकर जब आपका जनरेटर इतना सरल है।
  2. कुछ साफ-सुथरी क्रियाएं हैं जिन्हें आपको याद रखने की जरूरत है। ये स्वचालित हो सकते हैं, लेकिन आपको और भी अधिक बुनियादी ढांचे की आवश्यकता होगी, जो फिर से, "बहुत असाधारण" के रूप में देखे जाने की संभावना है। वैसे भी, आप की जरूरत है कि स्वच्छ अप:
    1. बाहर> पास ()
    2. generator.join ()
  3. यह आपको जनरेटर को रोकने की अनुमति नहीं देता है। आप उस क्षमता को जोड़ने के लिए कुछ संशोधन कर सकते हैं, लेकिन यह कोड में अव्यवस्था जोड़ता है। यह पायथन की उपज के बयान के रूप में कभी साफ नहीं होगा।
  4. 2 के अलावा, बॉयलरप्लेट के अन्य बिट्स भी हैं जिन्हें हर बार जब आप जनरेटर ऑब्जेक्ट को "इंस्टेंट" करना चाहते हैं:
    1. चैनल * पैरामीटर
    2. मुख्य में अतिरिक्त चर: जोड़े, जनरेटर

आप कार्यक्षमता के साथ वाक्यविन्यास को भ्रमित कर रहे हैं। ऊपर दिए गए कुछ उत्तर वास्तव में C ++ को अंतिम कॉल के दौरान जहां से छोड़ा गया है, वहां से निष्पादन लेने की अनुमति देते हैं। यह जादुई कुछ भी नहीं है। तथ्य की बात के रूप में, अजगर है सी में लागू है, इसलिए जो कुछ भी पायथन में संभव है यद्यपि सुविधाजनक के रूप में नहीं सी में संभव है।
ईडी

@ कॉम पहले से ही पहले पैराग्राफ में संबोधित नहीं है? वह दावा नहीं कर रहा है कि पारंपरिक C ++ में समतुल्य कार्यक्षमता नहीं बनाई जा सकती है, केवल यह कि यह "एक विशाल दर्द" है।
कैथिन

@Kaitain यहां सवाल यह नहीं है कि क्या C ++ में जनरेटर करने के लिए दर्द है, लेकिन क्या ऐसा करने के लिए एक पैटर्न है। उनका दावा है कि दृष्टिकोण "बिंदु को याद करता है", कि "निकटतम चीज" धागे हैं ... केवल भ्रामक हैं। क्या यह दर्द है? कोई अन्य उत्तरों के माध्यम से पढ़ सकता है और अपने लिए निर्णय ले सकता है।
ईडी

@ कॉम लेकिन क्या यह अंत एक खाली बिंदु नहीं है, यह देखते हुए कि सभी ट्यूरिंग-पूर्ण भाषाएं अंततः समान कार्यक्षमता के लिए सक्षम हैं? "जो कुछ भी X में संभव है वह वाई में संभव है" ऐसी सभी भाषाओं के लिए सही होने की गारंटी है, लेकिन यह मुझे बहुत रोशन करने वाला अवलोकन नहीं लगता है।
काइटैन

@Kaainain सटीक रूप से क्योंकि सभी ट्यूरिंग-पूर्ण भाषाओं में समान क्षमता होनी चाहिए, इस प्रकार एक भाषा को दूसरी भाषा में लागू करने का प्रश्न वैध है। कुछ भी नहीं है कि पायथन को किसी अन्य भाषा द्वारा पूरा नहीं किया जा सकता है; प्रश्न दक्षता और स्थिरता है। दोनों के संबंध में, C ++ एक अच्छा (r) विकल्प होगा।
ईडी

4

आपको संभवतः std में जनरेटर की जाँच करनी चाहिए :: Visual Studio 2015 में प्रायोगिक। जैसे: https://blogs.msdn.microsoft.com/vcblog/2014/11/12/resumable-functions-in-c/

मुझे लगता है कि यह वही है जो आप देख रहे हैं। कुल मिलाकर जनरेटर C ++ 17 में उपलब्ध होना चाहिए क्योंकि यह केवल प्रायोगिक Microsoft VC सुविधा है।


2

यदि आपको केवल विशिष्ट जनरेटर की अपेक्षाकृत कम संख्या के लिए ऐसा करने की आवश्यकता है, तो आप प्रत्येक को एक वर्ग के रूप में लागू कर सकते हैं, जहां सदस्य डेटा पायथन जनरेटर फ़ंक्शन के स्थानीय चर के बराबर है। तब आपके पास एक अगला फ़ंक्शन होता है जो अगली चीज़ को लौटाता है जनरेटर उत्पन्न होता है, आंतरिक स्थिति को अपडेट करता है क्योंकि यह ऐसा करता है।

यह मूल रूप से पायथन जनरेटर को लागू करने के तरीके के समान है, मेरा मानना ​​है। प्रमुख अंतर यह है कि वे "आंतरिक राज्य" के हिस्से के रूप में जनरेटर फ़ंक्शन के लिए बायोटेक में एक ऑफसेट को याद कर सकते हैं, जिसका अर्थ है कि जनरेटर को पैदावार वाले छोरों के रूप में लिखा जा सकता है। आपको इसके बजाय पिछले से अगले मान की गणना करनी होगी। अपने मामले मेंpair_sequence , यह बहुत तुच्छ है। यह जटिल जनरेटर के लिए नहीं हो सकता है।

आपको समाप्ति का संकेत देने के कुछ तरीके भी चाहिए। यदि आप जो लौटा रहे हैं वह "पॉइंटर-लाइक" है, और NULL एक वैध उपज योग्य मूल्य नहीं होना चाहिए, तो आप NULL पॉइंटर को समाप्ति संकेतक के रूप में उपयोग कर सकते हैं। अन्यथा आपको एक आउट-ऑफ-बैंड सिग्नल की आवश्यकता है।


1

कुछ ऐसा ही है:

struct pair_sequence
{
    typedef pair<unsigned int, unsigned int> result_type;
    static const unsigned int limit = numeric_limits<unsigned int>::max()

    pair_sequence() : i(0), j(0) {}

    result_type operator()()
    {
        result_type r(i, j);
        if(j < limit) j++;
        else if(i < limit)
        {
          j = 0;
          i++;
        }
        else throw out_of_range("end of iteration");
    }

    private:
        unsigned int i;
        unsigned int j;
}

ऑपरेटर () का उपयोग करना केवल एक सवाल है कि आप इस जनरेटर के साथ क्या करना चाहते हैं, आप इसे एक धारा के रूप में भी बना सकते हैं और यह सुनिश्चित कर सकते हैं कि यह एक उदाहरण के लिए istream_iterator को adapts करता है।


1

रेंज- v3 का उपयोग करना :

#include <iostream>
#include <tuple>
#include <range/v3/all.hpp>

using namespace std;
using namespace ranges;

auto generator = [x = view::iota(0) | view::take(3)] {
    return view::cartesian_product(x, x);
};

int main () {
    for (auto x : generator()) {
        cout << get<0>(x) << ", " << get<1>(x) << endl;
    }

    return 0;
}

0

कुछ इस तरह :

उदाहरण का उपयोग करें:

using ull = unsigned long long;

auto main() -> int {
    for (ull val : range_t<ull>(100)) {
        std::cout << val << std::endl;
    }

    return 0;
}

0 से 99 तक नंबर प्रिंट करेंगे


0

खैर, आज मैं भी C ++ 11 के तहत आसान संग्रह कार्यान्वयन की तलाश में था। वास्तव में मैं निराश था, क्योंकि मैंने जो कुछ भी पाया, वह अजगर जनरेटर, या सी # उपज ऑपरेटर ... या बहुत जटिल जैसी चीजों से बहुत दूर है।

उद्देश्य संग्रह करना है जो आवश्यक होने पर ही अपनी वस्तुओं का उत्सर्जन करेगा।

मैं चाहता था कि यह ऐसा हो:

auto emitter = on_range<int>(a, b).yield(
    [](int i) {
         /* do something with i */
         return i * 2;
    });

मुझे यह पोस्ट मिला, IMHO का सबसे अच्छा उत्तर यह था। बूंगी योंगवेई वू द्वारा । चूंकि यह वही है जो लेखक चाहता था।

यह बढ़ावा देने के आंगन सीखने लायक है .. और मैं शायद सप्ताहांत पर करूँगा। लेकिन अभी तक मैं अपने बहुत छोटे कार्यान्वयन का उपयोग कर रहा हूं। आशा है कि यह किसी और की मदद करता है।

नीचे उपयोग का उदाहरण है, और फिर कार्यान्वयन।

Example.cpp

#include <iostream>
#include "Generator.h"
int main() {
    typedef std::pair<int, int> res_t;

    auto emitter = Generator<res_t, int>::on_range(0, 3)
        .yield([](int i) {
            return std::make_pair(i, i * i);
        });

    for (auto kv : emitter) {
        std::cout << kv.first << "^2 = " << kv.second << std::endl;
    }

    return 0;
}

Generator.h

template<typename ResTy, typename IndexTy>
struct yield_function{
    typedef std::function<ResTy(IndexTy)> type;
};

template<typename ResTy, typename IndexTy>
class YieldConstIterator {
public:
    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;

    typedef YieldConstIterator<ResTy, IndexTy> mytype_t;
    typedef ResTy value_type;

    YieldConstIterator(index_t index, yield_function_t yieldFunction) :
            mIndex(index),
            mYieldFunction(yieldFunction) {}

    mytype_t &operator++() {
        ++mIndex;
        return *this;
    }

    const value_type operator*() const {
        return mYieldFunction(mIndex);
    }

    bool operator!=(const mytype_t &r) const {
        return mIndex != r.mIndex;
    }

protected:

    index_t mIndex;
    yield_function_t mYieldFunction;
};

template<typename ResTy, typename IndexTy>
class YieldIterator : public YieldConstIterator<ResTy, IndexTy> {
public:

    typedef YieldConstIterator<ResTy, IndexTy> parent_t;

    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;
    typedef ResTy value_type;

    YieldIterator(index_t index, yield_function_t yieldFunction) :
            parent_t(index, yieldFunction) {}

    value_type operator*() {
        return parent_t::mYieldFunction(parent_t::mIndex);
    }
};

template<typename IndexTy>
struct Range {
public:
    typedef IndexTy index_t;
    typedef Range<IndexTy> mytype_t;

    index_t begin;
    index_t end;
};

template<typename ResTy, typename IndexTy>
class GeneratorCollection {
public:

    typedef Range<IndexTy> range_t;

    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;
    typedef YieldIterator<ResTy, IndexTy> iterator;
    typedef YieldConstIterator<ResTy, IndexTy> const_iterator;

    GeneratorCollection(range_t range, const yield_function_t &yieldF) :
            mRange(range),
            mYieldFunction(yieldF) {}

    iterator begin() {
        return iterator(mRange.begin, mYieldFunction);
    }

    iterator end() {
        return iterator(mRange.end, mYieldFunction);
    }

    const_iterator begin() const {
        return const_iterator(mRange.begin, mYieldFunction);
    }

    const_iterator end() const {
        return const_iterator(mRange.end, mYieldFunction);
    }

private:
    range_t mRange;
    yield_function_t mYieldFunction;
};

template<typename ResTy, typename IndexTy>
class Generator {
public:
    typedef IndexTy index_t;
    typedef ResTy res_t;
    typedef typename yield_function<res_t, index_t>::type yield_function_t;

    typedef Generator<ResTy, IndexTy> mytype_t;
    typedef Range<IndexTy> parent_t;
    typedef GeneratorCollection<ResTy, IndexTy> finalized_emitter_t;
    typedef  Range<IndexTy> range_t;

protected:
    Generator(range_t range) : mRange(range) {}
public:
    static mytype_t on_range(index_t begin, index_t end) {
        return mytype_t({ begin, end });
    }

    finalized_emitter_t yield(yield_function_t f) {
        return finalized_emitter_t(mRange, f);
    }
protected:

    range_t mRange;
};      

0

यह उत्तर C में काम करता है (और इसलिए मुझे लगता है कि c ++ में भी काम करता है)

#include <stdio.h>

const uint64_t MAX = 1ll<<32;

typedef struct {
    uint64_t i, j;
} Pair;

Pair* generate_pairs()
{
    static uint64_t i = 0;
    static uint64_t j = 0;
    
    Pair p = {i,j};
    if(j++ < MAX)
    {
        return &p;
    }
        else if(++i < MAX)
    {
        p.i++;
        p.j = 0;
        j = 0;
        return &p;
    }
    else
    {
        return NULL;
    }
}

int main()
{
    while(1)
    {
        Pair *p = generate_pairs();
        if(p != NULL)
        {
            //printf("%d,%d\n",p->i,p->j);
        }
        else
        {
            //printf("end");
            break;
        }
    }
    return 0;
}

यह एक जनरेटर की नकल करने के लिए सरल, गैर-वस्तु-उन्मुख तरीका है। यह मेरे लिए उम्मीद के मुताबिक काम किया।


-1

जैसे कोई फ़ंक्शन स्टैक की अवधारणा का अनुकरण करता है, वैसे ही जनरेटर एक कतार की अवधारणा का अनुकरण करते हैं। शेष शब्दार्थ है।

एक साइड नोट के रूप में, आप हमेशा डेटा के बजाय ऑपरेशन के ढेर का उपयोग करके एक स्टैक के साथ एक कतार अनुकरण कर सकते हैं। व्यावहारिक रूप से इसका मतलब यह है कि आप एक जोड़ी को वापस करके एक कतार-जैसा व्यवहार लागू कर सकते हैं, जिसका दूसरा मूल्य या तो अगला फ़ंक्शन है जिसे कॉल किया जाना है या इंगित करता है कि हम मूल्यों से बाहर हैं। लेकिन यह पैदावार बनाम रिटर्न की तुलना में अधिक सामान्य है। यह सजातीय मूल्यों के बजाय किसी भी मूल्यों की एक कतार का अनुकरण करने की अनुमति देता है जो आप एक जनरेटर से उम्मीद करते हैं, लेकिन एक पूर्ण पंक्ति को रखे बिना।

विशेष रूप से, चूंकि C ++ में कतार के लिए एक प्राकृतिक अमूर्त नहीं है, आपको उन निर्माणों का उपयोग करने की आवश्यकता है जो आंतरिक रूप से एक कतार को लागू करते हैं। तो जो उत्तर ने पुनरावृत्तियों के साथ उदाहरण दिया वह अवधारणा का एक अच्छा कार्यान्वयन है।

व्यावहारिक रूप से इसका मतलब यह है कि आप नंगे-हड्डियों की कतार की कार्यक्षमता के साथ कुछ लागू कर सकते हैं यदि आप बस कुछ जल्दी चाहते हैं और फिर कतार के मूल्यों का उपभोग करते हैं जैसे कि आप एक जनरेटर से प्राप्त मूल्यों का उपभोग करेंगे।

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