পাইথন জেনারেটরের প্যাটার্নের সমতুল্য সি ++


117

আমি পাইথন কোডের একটি উদাহরণ পেয়েছি যা আমার সি ++ তে নকল করতে হবে। আমার কোনও নির্দিষ্ট সমাধানের প্রয়োজন নেই (যেমন সহ-রুটিন ভিত্তিক ফলন সমাধান, যদিও তারা গ্রহণযোগ্য উত্তরও হবে), আমার কেবল শব্দার্থবিজ্ঞানকে কিছু উপায়ে পুনরুত্পাদন করা প্রয়োজন।

পাইথন

এটি একটি মৌলিক সিকোয়েন্স জেনারেটর, কোনও বস্তুগত সংস্করণ সংরক্ষণ করার জন্য এটি স্পষ্টতই অনেক বড়।

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 ...

সি ++

সি ++ এর সমাধানের জন্য আমি যে জিনিসটি খুঁজে পেতে পারি তা হ'ল yieldসি ++ কর্টিনগুলির সাথে নকল করা , তবে কীভাবে এটি করা যায় সে সম্পর্কে আমি কোনও ভাল রেফারেন্স পাইনি। আমি এই সমস্যার জন্য বিকল্প (অ-সাধারণ) সমাধানগুলিতেও আগ্রহী। পাসগুলির মধ্যে ক্রমটির অনুলিপি রাখার মতো পর্যাপ্ত মেমরি বাজেট আমার নেই।


আপনি এখান থেকে দেখতে পারেন stackoverflow.com/questions/3864410/… কর্টাইন প্রয়োগ করা ভাল ধারণা নয়। আপনি বাফার পড়া দিয়ে এটি করতে পারবেন না? stackoverflow.com/questions/4685862/…
বাতবাটর

সি ++ পুনরুক্তি করা উচিত এই জাতীয় কিছু সমর্থন করা উচিত।
লালাল্যান্ড

উত্তর:


79

জেনারেটর সি ++ তে উপস্থিত রয়েছে কেবল অন্য নামে: ইনপুট ইটারেটর । উদাহরণস্বরূপ, থেকে পড়া 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
@ নোয়াহা ওয়াটকিনস: ভাষাগুলি যখন তাদের সমর্থন করে তখন কর্টিনগুলি একটি সহজেই বাস্তবায়নের ব্যবস্থা করে। দুর্ভাগ্যক্রমে সি ++ হয় না, তাই পুনরাবৃত্তি আরও সহজ। আপনার যদি সত্যিই কর্টিনগুলির প্রয়োজন হয় তবে আপনার ফাংশন কলটির "স্ট্যাক" পাশে রাখতে আপনার আসলে একটি পূর্ণ বর্ধিত থ্রেড দরকার। এটা স্পষ্টভাবে কৃমি খোলা যেমন একটি করতে Overkill এর মাত্র যে এই উদাহরণে এ জন্য, কিন্তু আপনার মাইলেজ কত আপনার প্রকৃত চাহিদার উপর নির্ভর করে পরিবর্তিত হতে পারে।
ম্যাথিউ এম।

1
একটি মানহীন -ভিত্তিক কর্টিন বাস্তবায়ন বুস্ট বুস্ট.আর.ডক / লিবস
বালক

2
@ বয়সি: কর্টাইনগুলির জন্য আসলে একাধিক প্রস্তাব রয়েছে, বিশেষত একটি স্ট্যাক-কম এবং অন্যটি স্ট্যাক পূর্ণ full এটি ক্র্যাক করা শক্ত বাদাম, তাই আপাতত আমি অপেক্ষা করছি। ইতিমধ্যে, স্ট্যাক-কম কর্টিনগুলি সরাসরি ইনপুট আইট্রেটার হিসাবে সরাসরি প্রয়োগ করা যায় (কেবল, চিনি ছাড়া)।
ম্যাথিউ এম।

3
তবুও সমান, পুনরাবৃত্তকারীরা জেনারেটরের মতো নয়।
Шобајић Шобајић

52

সি ++ এ পুনরুক্তি রয়েছে, তবে একটি পুনরুক্তি কার্যকর করা সহজবোধ্য নয়: তাদের পুনরাবৃত্তি সংক্রান্ত ধারণাগুলির সাথে পরামর্শ করতে হবে এবং এগুলি বাস্তবায়নের জন্য সাবধানতার সাথে নতুন পুনরাবৃত্ত শ্রেণীর নকশা তৈরি করতে হবে। ধন্যবাদ, বুস্টের একটি আয়র_ফ্যাকড টেম্পলেট রয়েছে যা পুনরাবৃত্তকারী এবং পুনরাবৃত্তির সাথে সামঞ্জস্যপূর্ণ জেনারেটরগুলি কার্যকর করতে সহায়তা করে।

কখনও কখনও একটি স্ট্যাকলেস কর্টিন একটি পুনরুক্তি প্রয়োগ করতে ব্যবহার করা যেতে পারে

পিএস এই নিবন্ধটিও দেখুন যা switchক্রিস্টোফার এম কোহলহফ এবং বুস্ট.কুরিটিন উভয়েরই অলিভার কাউয়ালকে উল্লেখ করেছে। অলিভার Kowalke এর কাজ একটি ফলোআপ হয় উপর Boost.Coroutine জিওভান্নি পি Deretta দ্বারা।

পিএস আমি মনে করি আপনি ল্যাম্বডাস সহ এক ধরণের জেনারেটরও লিখতে পারেন :

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;

PS এখানে মর্ডার কর্টিনগুলির সাথে প্রয়োগ করা একটি জেনারেটর রয়েছে :

#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

যেহেতু বুস্ট.কৌর্টাইন 2 এখন এটি খুব ভালভাবে সমর্থন করে (আমি এটি দেখতে পেয়েছি কারণ ঠিক একই yieldসমস্যাটি সমাধান করতে চেয়েছিলাম ), তাই আমি আপনার মূল উদ্দেশ্যটির সাথে মিলে সি ++ কোড পোস্ট করছি:

#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

আপনার নিজের পুনরুক্তি লেখার সাথে জড়িত সমস্ত উত্তর সম্পূর্ণ ভুল। এই জাতীয় উত্তরগুলি পাইথন জেনারেটরের বিন্দুটি পুরোপুরি মিস করে (ভাষার অন্যতম বৃহত এবং অনন্য বৈশিষ্ট্য)। জেনারেটরগুলির সম্পর্কে সর্বাধিক গুরুত্বপূর্ণ বিষয়টি হ'ল মৃত্যুদন্ড কার্যকর করা হবে it এটি পুনরাবৃত্তিকারীদের ক্ষেত্রে ঘটে না। পরিবর্তে, আপনাকে অবশ্যই রাষ্ট্রীয় তথ্য সংরক্ষণ করতে হবে যেমন অপারেটরকে ++ বা অপারেটর * যখন নতুন বলা হয়, ঠিক তখনই পরবর্তী তথ্য ফাংশনের কলের শুরুতে সঠিক তথ্য উপস্থিত থাকে । এ কারণেই আপনার নিজের সি ++ পুনরুক্তি লেখার একটি বিশাল ব্যথা; তবে, জেনারেটরগুলি মার্জিত, এবং পড়তে সহজ + লেখা।

স্থানীয় সি ++ তে পাইথন জেনারেটরের জন্য ভাল এনালগ আছে বলে আমি মনে করি না, কমপক্ষে এখনও হয়নি (এমন গুজব রয়েছে যে ফলন সি ++ 17 এ নেমে আসবে )। তৃতীয় পক্ষের (যেমন ইয়ংওয়েইয়ের বুস্ট পরামর্শ) অবলম্বন করে বা আপনার নিজস্ব ঘূর্ণায়নের মাধ্যমে আপনি অনুরূপ কিছু পেতে পারেন।

আমি বলব নেটিভ সি ++ এর নিকটতম জিনিসটি থ্রেড। একটি থ্রেড স্থানীয় ভেরিয়েবলগুলির একটি স্থগিত সেটটি বজায় রাখতে পারে, এবং জেনারেটরের মতো এটি যেখানে ছেড়ে যায় সেখানে কার্যকরকরণ চালিয়ে যেতে পারে তবে জেনারেটর অবজেক্ট এবং এর কলারের মধ্যে যোগাযোগকে সমর্থন করার জন্য আপনাকে কিছুটা অতিরিক্ত অবকাঠামো রোল করতে হবে। যেমন

// 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. প্রধান অতিরিক্ত ভেরিয়েবলগুলি: জোড়া, জেনারেটর

আপনি কার্যকারিতা সহ বিভ্রান্তিকর বাক্য গঠন করছেন। উপরের কয়েকটি উত্তর আসলে সি ++ কে শেষ কলের সময় ছেড়ে গেছে সেখান থেকে মৃত্যুদন্ড কার্যকর করার অনুমতি দেয়। এটা যাদুকর কিছুই না। বস্তুত হিসাবে, পাইথন হয় সি বাস্তবায়িত, তাই যাই হোক না কেন পাইথন সম্ভব যদিও সুবিধাজনক হিসাবে সি সম্ভব হয়।
এডি

@ এডি কি ইতিমধ্যে প্রথম অনুচ্ছেদে সম্বোধন করা হয়নি? তিনি দাবি করছেন না যে সমমানের কার্যকারিতা প্রচলিত সি ++ তে তৈরি করা যায় না, কেবল এটি "একটি বিশাল ব্যথা"।
কাইতাইন

@ কেইটাইন এখানে প্রশ্নটি নয় যে সি ++ তে জেনারেটর করতে ব্যথা হচ্ছে কিনা, তবে এটি করার কোনও প্যাটার্ন রয়েছে কিনা তা নয়। তাঁর দাবি যে এই পদ্ধতির "বিন্দুটি মিস", যে "নিকটতম জিনিস" থ্রেড ... কেবল বিভ্রান্তিকর। এটা কি ব্যথা? একজন অন্য উত্তরগুলির মাধ্যমে পড়তে পারেন এবং নিজের জন্য সিদ্ধান্ত নিতে পারেন।
এডি

@ এডি কিন্তু সমস্ত টিউরিং-সম্পূর্ণ ভাষাগুলি একই কার্যকারিতাটির জন্য চূড়ান্তভাবে সক্ষম কিনা তা এই শূন্য পয়েন্ট হিসাবে শেষ করে না? "এক্সে যা কিছু সম্ভব তা ওয়াইতে সম্ভব" এর গ্যারান্টিযুক্ত এ জাতীয় সমস্ত ভাষার জন্য এটি সত্য, তবে এটি আমার কাছে খুব আলোকিত পর্যবেক্ষণ বলে মনে হয় না।
কাইতাইন

@ কেইটাইন যথাযথভাবে কারণ সমস্ত টিউরিং-সম্পূর্ণ ভাষাগুলির মধ্যে একই ক্ষমতা থাকতে হবে, সুতরাং অন্য ভাষায় একটি বৈশিষ্ট্য কীভাবে প্রয়োগ করা যায় সে প্রশ্নটি বৈধ। পাইথনের যা কিছু আছে তা অন্য কোনও ভাষা দ্বারা সম্পূর্ণ করা যায় না; প্রশ্ন দক্ষতা এবং রক্ষণাবেক্ষণযোগ্যতা হয়। উভয় ক্ষেত্রেই, সি ++ একটি জরিমানা (র) পছন্দ হবে।
এডি 19

4

আপনার সম্ভবত স্টুডেন্ট :: ভিজ্যুয়াল স্টুডিওতে পরীক্ষামূলক :: পরীক্ষামূলকভাবে জেনারেটর পরীক্ষা করা উচিত: https://blogs.msdn.microsoft.com/vcblog/2014/11/12/resumable-function-in-c/

আমি মনে করি এটি ঠিক যা আপনি খুঁজছেন। সামগ্রিক জেনারেটর সি ++ 17 এ উপলব্ধ হওয়া উচিত কারণ এটি কেবলমাত্র পরীক্ষামূলক মাইক্রোসফ্ট ভিসি বৈশিষ্ট্য।


2

আপনার যদি কেবলমাত্র নির্দিষ্ট জেনারেটরের তুলনামূলকভাবে সংখ্যক সংখ্যক জেনারেটরের জন্য এটি করতে হয় তবে আপনি প্রতিটিটিকে একটি শ্রেণি হিসাবে প্রয়োগ করতে পারেন, যেখানে সদস্যের ডেথটি পাইথন জেনারেটর ফাংশনের স্থানীয় ভেরিয়েবলের সমান। তারপরে আপনার একটি পরবর্তী ফাংশন রয়েছে যা জেনারেটরের ফলনের পরের জিনিসটি ফিরিয়ে দেয়, অভ্যন্তরীণ অবস্থা যেমনটি তা আপডেট করে।

এটি পাইথন জেনারেটরগুলি কীভাবে বাস্তবায়িত করা হয় তার সাথে মিল রয়েছে, আমি বিশ্বাস করি। প্রধান পার্থক্য হচ্ছে তারা "অভ্যন্তরীণ অবস্থার" অংশ হিসাবে জেনারেটর ফাংশনের জন্য বাইটকোডে একটি অফসেট মনে করতে পারে, যার অর্থ জেনারেটরগুলি ফলনযুক্ত লুপ হিসাবে লেখা যেতে পারে। পরিবর্তে পূর্বের থেকে পরবর্তী মানটি গণনা করতে হবে। আপনার ক্ষেত্রেpair_sequence , এটি বেশ তুচ্ছ। এটি জটিল জেনারেটরের জন্য নাও থাকতে পারে।

আপনার সমাপ্তির ইঙ্গিত করার কিছু উপায়ও প্রয়োজন। আপনি যা ফিরিয়ে দিচ্ছেন তা যদি "পয়েন্টার-সদৃশ" হয় এবং 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;
}

অপারেটরটি () ব্যবহার করে আপনি এই জেনারেটরের সাথে কী করতে চান তা কেবলমাত্র একটি প্রশ্ন, আপনি এটি একটি স্ট্রিম হিসাবে তৈরি করতে এবং উদাহরণস্বরূপ, এটি একটি আইসক্রিম_আউটরেটারের সাথে মানিয়ে নেওয়ার বিষয়টি নিশ্চিত করতে পারেন।


1

পরিসর-ভি 3 ব্যবহার করে :

#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

ঠিক আছে, আজ আমি সি ++ 11 এর অধীনে সহজ সংগ্রহ বাস্তবায়নও খুঁজছিলাম। আসলে আমি হতাশ ছিলাম, কারণ আমি যা কিছু পেয়েছি তা পাইথন জেনারেটর, বা সি # ফলন অপারেটর ... বা খুব জটিল বিষয়গুলির থেকে অনেক দূরে।

উদ্দেশ্যটি সংগ্রহ করা যা এটির প্রয়োজন হলে কেবল তার আইটেমগুলি নির্গত হয়।

আমি চেয়েছিলাম এটি যেন এমন হয়:

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

আমি এই পোস্টটি পেয়েছি, আইএমএইচওর সর্বোত্তম উত্তরটি ছিল ইউস্টওয়ে উয়ের দ্বারা boost.coroutine2 সম্পর্কে । যেহেতু এটি লেখক যা চেয়েছিলেন তার নিকটতম।

এটি উত্সাহ বাড়ানোর দরজা শিখতে হবে .. এবং আমি সম্ভবত সপ্তাহান্তে করব। তবে এখন পর্যন্ত আমি আমার খুব ছোট বাস্তবায়ন ব্যবহার করছি। আশা করি এটি অন্য কাউকে সাহায্য করবে।

নীচে ব্যবহারের উদাহরণ, এবং তারপরে বাস্তবায়ন।

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

এই উত্তরটি সিতে কাজ করে (এবং তাই আমি মনে করি সি ++ তেও কাজ করে)

#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

ঠিক যেমন কোনও ফাংশন একটি স্ট্যাকের ধারণার অনুকরণ করে, জেনারেটর একটি সারি ধারণার অনুকরণ করে। বাকীটি শব্দার্থক।

পার্শ্ব নোট হিসাবে, আপনি সর্বদা ডেটার পরিবর্তে স্ট্যাক অপারেশন ব্যবহার করে স্ট্যাকের সাহায্যে একটি সারি সারি করতে পারেন। এর ব্যবহারিকভাবে যা বোঝায় তা হ'ল আপনি কোনও জুটি ফিরিয়ে একটি সারি-জাতীয় আচরণ বাস্তবায়ন করতে পারেন, যার দ্বিতীয় মানটির পরবর্তী ফাংশনটি বলা হয় বা নির্দেশ করে যে আমরা মূল্যবোধের বাইরে। তবে ফলন বনাম রিটার্ন যা দেয় তার চেয়ে এটি সাধারণ। এটি জেনারেটরের কাছ থেকে প্রত্যাশাযুক্ত একজাতীয় মানের পরিবর্তে কোনও পূর্ণ অভ্যন্তরীণ সারি না রেখে যে কোনও মানের একটি সারি অনুকরণ করতে দেয়।

আরও সুনির্দিষ্টভাবে, যেহেতু সি ++ এর কোনও সারির জন্য প্রাকৃতিক বিমূর্ততা নেই, তাই আপনাকে অভ্যন্তরীণভাবে একটি সারি প্রয়োগকারী কনস্ট্রাক্ট ব্যবহার করতে হবে। সুতরাং উত্তর যা পুনরাবৃত্তকারীদের সাথে উদাহরণ দিয়েছে তা ধারণার একটি শালীন বাস্তবায়ন।

এর ব্যবহারিকভাবে এর অর্থ হ'ল আপনি খালি-হাড়ের কাতারের কার্যকারিতা সহ কিছু বাস্তবায়ন করতে পারেন যদি আপনি কেবল কিছু দ্রুত চান এবং তারপরে একটি জেনারেটর থেকে প্রাপ্ত মানগুলি যেমন ব্যবহার করেন ঠিক তেমনভাবে সারির মানগুলি গ্রাস করেন।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.