একসাথে দুই বা আরও বেশি পাত্রে পুনরাবৃত্তি করার সর্বোত্তম উপায়


114

সি ++ 11 কনটেইনারগুলির মাধ্যমে পুনরাবৃত্তি করার একাধিক উপায় সরবরাহ করে। উদাহরণ স্বরূপ:

রেঞ্জ-ভিত্তিক লুপ

for(auto c : container) fun(c)

এসটিডি :: for_each

for_each(container.begin(),container.end(),fun)

তবে এরকম কিছু অর্জনের জন্য একই আকারের দুটি (বা আরও) ধারককে পুনরাবৃত্তি করার প্রস্তাবিত উপায় কী:

for(unsigned i = 0; i < containerA.size(); ++i) {
  containerA[i] = containerB[i];
}

1
কি সম্পর্কে transformউপস্থিত #include <algorithm>?
অঙ্কিত আচার্য

অ্যাসাইনমেন্ট লুপ সম্পর্কে: যদি উভয়ই ভেক্টর বা অনুরূপ containerA = containerB;হয় তবে লুপের পরিবর্তে ব্যবহার করুন।
Emlai

অনুরূপ একটি প্রশ্ন: stackoverflow.com/questions/8511035/...
knedlsepp

উত্তর:


53

বরং দেরিতে পার্টিতে। তবে: আমি সূচকগুলি দিয়ে পুনরাবৃত্তি করব। তবে শাস্ত্রীয় forলুপের সাথে নয় পরিবর্তে forসূচকগুলির উপর একটি পরিসীমা-ভিত্তিক লুপের সাথে:

for(unsigned i : indices(containerA)) {
    containerA[i] = containerB[i];
}

indicesএকটি সাধারণ মোড়কের কাজ যা সূচকগুলির জন্য একটি (অলসভাবে মূল্যায়ন করা) পরিসীমা দেয়। যেহেতু বাস্তবায়ন - যদিও এটি সহজ - এটি এখানে পোস্ট করা কিছুটা দীর্ঘ, তাই আপনি গিটহাবের উপর একটি বাস্তবায়ন পেতে পারেন

এই কোডটি ম্যানুয়াল, শাস্ত্রীয় লুপ ব্যবহার করার মতো দক্ষfor

যদি এই প্যাটার্নটি আপনার ডেটাতে প্রায়শই ঘটে থাকে zipতবে যুক্ত করা উপাদানগুলির সাথে মিল রেখে অন্য প্যাটার্নটি ব্যবহার করুন যা দুটি সিকোয়েন্স এবং একটি টিউপস তৈরি করে:

for (auto& [a, b] : zip(containerA, containerB)) {
    a = b;
}

বাস্তবায়ন zipপাঠকের জন্য অনুশীলন হিসাবে রেখে দেওয়া হয়েছে, তবে এটি বাস্তবায়ন থেকে সহজেই অনুসরণ করা হয় indices

(সি ++ 17 এর আগে আপনাকে নিম্নলিখিতটি লিখতে হবে :)

for (auto items&& : zip(containerA, containerB))
    get<0>(items) = get<1>(items);

2
গণনা_ক্রমকে বাড়ানোর তুলনায় আপনার সূচকগুলি বাস্তবায়নের কোনও সুবিধা আছে কি? একটি সহজেই ব্যবহার করা যেতে পারেboost::counting_range(size_t(0), containerA.size())
সেবাস্তিয়ানক

3
@ সেবাস্তিয়ানকে এই ক্ষেত্রে সবচেয়ে বড় পার্থক্যটি সিনট্যাক্স: খনিটি (আমি দাবি করি) এই ক্ষেত্রে ব্যবহার করার জন্য উদ্দেশ্যমূলকভাবে আরও ভাল। তদতিরিক্ত, আপনি একটি পদক্ষেপের আকার নির্দিষ্ট করতে পারেন। উদাহরণস্বরূপ লিঙ্কযুক্ত গিথুব পৃষ্ঠা এবং বিশেষত README ফাইলটি দেখুন।
কনরাড রুডল্ফ

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

@ সেবাস্তিয়ানক আমি স্বীকার করি যে আমি যখন কোডটি লিখেছিলাম তখন আমি কোনও লাইব্রেরি ব্যবহার না করেই বিচ্ছিন্নভাবে জীবনযাপন করা যথেষ্ট সহজ বলে মনে করেছি (এবং এটি!)। এখন আমি সম্ভবত এটি বুস্ট.রেঞ্জের চারপাশে মোড়ক হিসাবে লিখব। বলেছিল, আমার গ্রন্থাগারের কর্মক্ষমতা ইতিমধ্যে অনুকূল is আমি এর দ্বারা যা বোঝাতে চাই তা হ'ল আমার indicesপ্রয়োগটি ব্যবহার করে সংকলক আউটপুট পাওয়া যায় যা ম্যানুয়াল লুপগুলি ব্যবহারের অনুরূপfor । যাই হোক না কেন ওভারহেড নেই।
কনরাড রুডল্ফ

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

38

আপনার নির্দিষ্ট উদাহরণের জন্য, কেবল ব্যবহার করুন

std::copy_n(contB.begin(), contA.size(), contA.begin())

আরও সাধারণ ক্ষেত্রে, আপনি zip_iteratorলুপের জন্য পরিসীমা ভিত্তিক ব্যবহারের যোগ্য করতে একটি ছোট ফাংশন সহ বুস্ট.আইট্রেটার ব্যবহার করতে পারেন । বেশিরভাগ ক্ষেত্রে, এটি কাজ করবে:

template<class... Conts>
auto zip_range(Conts&... conts)
  -> decltype(boost::make_iterator_range(
  boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
  boost::make_zip_iterator(boost::make_tuple(conts.end()...))))
{
  return {boost::make_zip_iterator(boost::make_tuple(conts.begin()...)),
          boost::make_zip_iterator(boost::make_tuple(conts.end()...))};
}

// ...
for(auto&& t : zip_range(contA, contB))
  std::cout << t.get<0>() << " : " << t.get<1>() << "\n";

সরাসরি উদাহরণ।

যাইহোক, পূর্ণবিকশিত genericity স্বরূপ, আপনি সম্ভবত আরো ভালো কিছু চান এই , যা অ্যারে এবং ব্যবহারকারী-সংজ্ঞায়িত প্রকারের সদস্য না জন্য সঠিকভাবে কাজ করবে begin()/ end()কিন্তু না আছে begin/ endতাদের নামস্থানে ফাংশন। এছাড়াও, এটি ব্যবহারকারীর দ্বারা বিশেষত ফাংশনগুলির constমাধ্যমে অ্যাক্সেস পাওয়ার অনুমতি দেবে zip_c...

এবং যদি আপনি চমৎকার ত্রুটি বার্তা একজন উকিল আছেন, আমার মতো, তাহলে আপনি সম্ভবত চান এই , যা চেক যদি থাকে অস্থায়ী পাত্রে কোন গৃহীত হয় zip_...ফাংশন, এবং যদি তাই হয় একটা চমৎকার ত্রুটি বার্তা ছাপে।


1
ধন্যবাদ! যদিও একটি প্রশ্ন, আপনি কেন অটো && ব্যবহার করেন, এর অর্থ && এর অর্থ কী?
মেমিকস

@memecs: আমি মাধ্যমে পড়া সুপারিশ এই প্রশ্নের aswell হিসাবে, আমার এই উত্তরের যা ধরণ সিদ্ধান্তগ্রহণ এবং রেফারেন্স ধ্বসে কিভাবে সম্পন্ন করা হয় ব্যাখ্যা করে। নোট করুন যে autoকোনও টেম্পলেট প্যারামিটারের মতোই একই কাজ করে, এবং T&&একটি টেমপ্লেটে সর্বজনীন রেফারেন্স হিসাবে প্রথম লিঙ্কে ব্যাখ্যা করা হয়েছে, সুতরাং auto&& v = 42হিসাবে ছাড় করা হবে int&&এবং auto&& w = v;তারপরে যেমন ছাড় করা হবে int&। এটি আপনাকে লভ্য্যুয়ালের পাশাপাশি মূল্যের সাথে মেলাতে এবং উভয়কেই অনুলিপি করতে দেয়, একটি অনুলিপি তৈরি না করেই।
Xeo

@ শিও: তবে অটো এবং অ্যান্ড ও ও ফোরচ লুপে ওভারের সুবিধা কী?
ভিক্টর সেহর

@ ভিক্টরসেহর: এটি আপনাকে তৈরির মতো অস্থায়ী উপাদানগুলির সাথে আবদ্ধ করতে দেয় zip_range
Xeo

23
@ শিও উদাহরণগুলির সমস্ত লিঙ্কটি নষ্ট হয়ে গেছে।
কিনান

34

আমি অবাক হয়েছি কেন কেন কেউ এটি উল্লেখ করেনি:

auto ItA = VectorA.begin();
auto ItB = VectorB.begin();

while(ItA != VectorA.end() || ItB != VectorB.end())
{
    if(ItA != VectorA.end())
    {
        ++ItA;
    }
    if(ItB != VectorB.end())
    {
        ++ItB;
    }
}

PS: যদি ধারক মাপগুলি মেলে না, তবে আপনাকে যদি স্টেটমেন্টের বিবরণে কোডটি রাখতে হবে।


9

সেখানে উপায়ে করতে অনেক আছে নির্দিষ্ট কিছু যেমন দেওয়া একাধিক পাত্রে সঙ্গে algorithmহেডার। উদাহরণস্বরূপ, আপনি যে উদাহরণ দিয়েছেন তাতে আপনি std::copyলুপের জন্য স্পষ্টের পরিবর্তে ব্যবহার করতে পারেন ।

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

তবে, আপনি যদি নিজের নিজস্ব "for_each" স্টাইলের ফাংশন তৈরি করতে চান যা কেবল দুটি কন্টেইনারটির মাধ্যমে সংক্ষিপ্ত দৈর্ঘ্যের দৈর্ঘ্য পর্যন্ত পুনরাবৃত্তি করে, আপনি এই জাতীয় কিছু করতে পারেন:

template <typename Container1, typename Container2>
void custom_for_each(
  Container1 &c1,
  Container2 &c2,
  std::function<void(Container1::iterator &it1, Container2::iterator &it2)> f)
{
  Container1::iterator begin1 = c1.begin();
  Container2::iterator begin2 = c2.begin();
  Container1::iterator end1 = c1.end();
  Container2::iterator end2 = c2.end();
  Container1::iterator i1;
  Container1::iterator i2;
  for (i1 = begin1, i2 = begin2; (i1 != end1) && (i2 != end2); ++it1, ++i2) {
    f(i1, i2);
  }
}

স্পষ্টতই আপনি যে কোনও ধরণের পুনরাবৃত্তি কৌশলটি চান একইভাবে করতে পারেন।

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


মনে হচ্ছে লুপের আগে আপনাকে পুনরাবৃত্তিগুলি ঘোষণা করতে হবে? আমি এটি চেষ্টা করেছিলাম: for (Container1::iterator i1 = c1.begin(), Container2::iterator i2 = c2.begin(); (i1 != end1) && (i2 != end2); ++it1, ++i2)তবে সংকলক চিৎকার করছে। এটি কেন অবৈধ তা কেউ ব্যাখ্যা করতে পারেন?
ডেভিড ডরিয়া

@ ডেভিডডোরিয়া লুপের প্রথম অংশটি একক বিবৃতি। আপনি একই বিবৃতিতে বিভিন্ন ধরণের দুটি ভেরিয়েবল ঘোষণা করতে পারবেন না। কেন for (int x = 0, y = 0; ...কাজ for (int x = 0, double y = 0; ...)করে তা ভেবে দেখুন তবে তা হয় না।
wjl

1
.. তবে আপনি স্ট্যান্ড :: জোড় <কন্টেইনার 1 :: পুনরুক্তিকারী, ধারক 2 :: পুনরুক্তিকারী> এর = {c1.begin (), c2.begin () have থাকতে পারেন;
লোরো

1
আরেকটি বিষয় লক্ষণীয় যে এটি সহজেই C ++ 14 এর সাথে typename...
বৈচিত্র্যময় করা

8

কেবলমাত্র যখন আপনি কেবল 2 টি পাত্রে একসাথে পুনরাবৃত্তি করতে হবে তখন বুস্ট রেঞ্জ লাইব্রেরিতে স্ট্যান্ডার্ড for_each অ্যালগরিদমের বর্ধিত সংস্করণ রয়েছে, যেমন:

#include <vector>
#include <boost/assign/list_of.hpp>
#include <boost/bind.hpp>
#include <boost/range/algorithm_ext/for_each.hpp>

void foo(int a, int& b)
{
    b = a + 1;
}

int main()
{
    std::vector<int> contA = boost::assign::list_of(4)(3)(5)(2);
    std::vector<int> contB(contA.size(), 0);

    boost::for_each(contA, contB, boost::bind(&foo, _1, _2));
    // contB will be now 5,4,6,3
    //...
    return 0;
}

যখন আপনাকে একটি অ্যালগরিদমে 2 টিরও বেশি কনটেইনার হ্যান্ডেল করা দরকার, তখন আপনাকে জিপ সহ খেলতে হবে।


চমৎকার! তুমি কিভাবে খুঁজে পেলে? দেখে মনে হচ্ছে এটি কোথাও নথিভুক্ত নয়।
মিখাইল

4

অন্য সমাধানটি হ'ল একটি ল্যাম্বডায় অন্য ধারকটির পুনরাবৃত্তির একটি রেফারেন্স ক্যাপচার করতে পারে এবং এতে পোস্ট ইনক্রিমেন্ট অপারেটর ব্যবহার করা যেতে পারে। উদাহরণস্বরূপ সরল অনুলিপিটি হ'ল:

vector<double> a{1, 2, 3};
vector<double> b(3);

auto ita = a.begin();
for_each(b.begin(), b.end(), [&ita](auto &itb) { itb = *ita++; })

ল্যাম্বদার ভিতরে আপনি যা কিছু করতে পারেন itaএবং তারপরে এটি বৃদ্ধি করুন। এটি সহজেই একাধিক পাত্রে ক্ষেত্রে প্রসারিত হয়।


3

একটি পরিসীমা-গ্রন্থাগার এটি এবং অন্যান্য খুব কার্যকারী কার্যকারিতা সরবরাহ করে। নিম্নলিখিত উদাহরণটি বুস্ট.রেঞ্জ ব্যবহার করেএরিক নাইবার্সের রেঞ্জভি 3 একটি ভাল বিকল্প হওয়া উচিত।

#include <boost/range/combine.hpp>
#include <iostream>
#include <vector>
#include <list>

int main(int, const char*[])
{
    std::vector<int> const v{0,1,2,3,4};
    std::list<char> const  l{'a', 'b', 'c', 'd', 'e'};

    for(auto const& i: boost::combine(v, l))
    {
        int ti;
        char tc;
        boost::tie(ti,tc) = i;
        std::cout << '(' << ti << ',' << tc << ')' << '\n';
    }

    return 0;
}

সি ++ 17 স্ট্রাকচার্ড বাইন্ডিংয়ের সাথে এটি আরও উন্নত করে তুলবে:

int main(int, const char*[])
{
    std::vector<int> const v{0,1,2,3,4};
    std::list<char> const  l{'a', 'b', 'c', 'd', 'e'};

    for(auto const& [ti, tc]: boost::combine(v, l))
    {
        std::cout << '(' << ti << ',' << tc << ')' << '\n';
    }

    return 0;
}

এই প্রোগ্রামটি জি ++ 4.8.0 দিয়ে সংকলন করছে না। delme.cxx:15:25: error: no match for 'operator=' (operand types are 'std::tuple<int&, char&>' and 'const boost::tuples::cons<const int&, boost::tuples::cons<const char&, boost::tuples::null_type> >') std::tie(ti,tc) = i; ^
সিয়াম

স্ট্যান্ড :: টাই বুস্ট করার জন্য: টাই পরে, এটি সংকলিত।
সায়াম

স্ট্রাকচার্ড বাইন্ডিং (এমএসভিসি 19.13.26132.0এবং উইন্ডোজ এসডিকে সংস্করণ ব্যবহার করে 10.0.16299.0) সহ সংস্করণটির জন্য আমি নিম্নলিখিত সংকলন ত্রুটিটি error C2679: binary '<<': no operator found which takes a right-hand operand of type 'const boost::tuples::cons<const char &,boost::fusion::detail::build_tuple_cons<boost::fusion::single_view_iterator<Sequence,boost::mpl::int_<1>>,Last,true>::type>' (or there is no acceptable conversion)
পেয়েছি

কাঠামোগত বাইন্ডিংগুলি এতে কাজ করছে বলে মনে হচ্ছে না boost::combine: stackoverflow.com/q/55585723/8414561
দেব নুল

2

আমিও একটু দেরি করেছি; তবে আপনি এটি ব্যবহার করতে পারেন (সি স্টাইলের ভ্যারানডিক ফাংশন):

template<typename T>
void foreach(std::function<void(T)> callback, int count, ...) {
    va_list args;
    va_start(args, count);

    for (int i = 0; i < count; i++) {
        std::vector<T> v = va_arg(args, std::vector<T>);
        std::for_each(v.begin(), v.end(), callback);
    }

    va_end(args);
}

foreach<int>([](const int &i) {
    // do something here
}, 6, vecA, vecB, vecC, vecD, vecE, vecF);

বা এটি (একটি ফাংশন প্যারামিটার প্যাক ব্যবহার করে):

template<typename Func, typename T>
void foreach(Func callback, std::vector<T> &v) {
    std::for_each(v.begin(), v.end(), callback);
}

template<typename Func, typename T, typename... Args>
void foreach(Func callback, std::vector<T> &v, Args... args) {
    std::for_each(v.begin(), v.end(), callback);
    return foreach(callback, args...);
}

foreach([](const int &i){
    // do something here
}, vecA, vecB, vecC, vecD, vecE, vecF);

বা এটি (একটি ব্রেস-সংযুক্ত আরম্ভকারী তালিকা ব্যবহার করে):

template<typename Func, typename T>
void foreach(Func callback, std::initializer_list<std::vector<T>> list) {
    for (auto &vec : list) {
        std::for_each(vec.begin(), vec.end(), callback);
    }
}

foreach([](const int &i){
    // do something here
}, {vecA, vecB, vecC, vecD, vecE, vecF});

বা আপনি এখানে ভেক্টরগুলিতে যোগদান করতে পারেন: দুটি ভেক্টরকে সংযুক্ত করার সেরা উপায় কী? এবং তারপরে বড় ভেক্টর দিয়ে পুনরাবৃত্তি করুন।


0

এখানে একটি বৈকল্পিক

template<class ... Iterator>
void increment_dummy(Iterator ... i)
    {}

template<class Function,class ... Iterator>
void for_each_combined(size_t N,Function&& fun,Iterator... iter)
    {
    while(N!=0)
        {
        fun(*iter...);
        increment_dummy(++iter...);
        --N;
        }
    }

ব্যবহারের উদাহরণ

void arrays_mix(size_t N,const float* x,const float* y,float* z)
    {
    for_each_combined(N,[](float x,float y,float& z){z=x+y;},x,y,z);    
    }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.