একাধিক 'for' লুপগুলি লেখার পরিষ্কার উপায়


98

একাধিক মাত্রা সহ একটি অ্যারের জন্য, আমাদের সাধারণত এর forপ্রতিটি মাত্রার জন্য একটি লুপ লিখতে হয় । উদাহরণ স্বরূপ:

vector< vector< vector<int> > > A;

for (int k=0; k<A.size(); k++)
{
    for (int i=0; i<A[k].size(); i++)
    {
        for (int j=0; j<A[k][i].size(); j++)
        {
            do_something_on_A(A[k][i][j]);
        }
    }
}

double B[10][8][5];
for (int k=0; k<10; k++)
{
    for (int i=0; i<8; i++)
    {
        for (int j=0; j<5; j++)
        {
            do_something_on_B(B[k][i][j]);
        }
    }
}

আপনি for-for-forআমাদের কোডে এই ধরণের লুপগুলি ঘন ঘন দেখতে পান। for-for-forলুপগুলি সংজ্ঞায়িত করতে আমি কীভাবে ম্যাক্রোগুলি ব্যবহার করব যাতে প্রতিবার এই জাতীয় কোডটি পুনরায় লেখার দরকার না হয়? এই কাজ করতে একটি ভাল উপায় আছে কি?


62
সুস্পষ্ট উত্তর হ'ল আপনি তা করেন না। আপনি ম্যাক্রো (বা অন্য কোনও কৌশল) ব্যবহার করে কোনও নতুন ভাষা তৈরি করেন না; আপনার পরে আসা ব্যক্তি কোডটি পড়তে অক্ষম হবে।
জেমস কানজে

17
আপনার যখন কোনও ভেক্টরের ভেক্টর থাকে তখন এটি খারাপ ডিজাইনের একটি লক্ষণ।
মারুন

5
@ নিম: আপনি এটি 1 টি সমতল অ্যারে দিয়ে করতে পারেন (এটি আরও ভাল কিনা তা নিশ্চিত নয়)।
জারোড 42

16
আমি মনে করি আপনি সম্ভাব্য O(n) = n^3কোডটি গোপন করতে চাইবেন না ...
poy

36
@ টিসি 1: এবং তারপরে আমি পড়তে আরও কঠিন find এটি সমস্ত ব্যক্তিগত পছন্দগুলির একটি প্রশ্ন এবং এটি আসলে এখানে হাতছাড়া প্রশ্নে সহায়তা করে না।

উত্তর:


281

প্রথম জিনিসটি আপনি এই জাতীয় ডেটা স্ট্রাকচার ব্যবহার করবেন না। আপনার যদি ত্রি-মাত্রিক ম্যাট্রিক্সের প্রয়োজন হয় তবে আপনি একটিটি সংজ্ঞায়িত করুন:

class Matrix3D
{
    int x;
    int y;
    int z;
    std::vector<int> myData;
public:
    //  ...
    int& operator()( int i, int j, int k )
    {
        return myData[ ((i * y) + j) * z + k ];
    }
};

অথবা আপনি যদি সূচি ব্যবহার করে চান [][][], আপনার operator[] একটি প্রক্সি ফেরত দরকার ।

একবার আপনি এটি করেন, যদি আপনি দেখতে পান যে উপস্থাপিত হিসাবে আপনাকে নিয়মিত পুনরাবৃত্তি করতে হবে, আপনি এমন একটি পুনরুক্তি প্রকাশ করবেন যা এটি সমর্থন করবে:

class Matrix3D
{
    //  as above...
    typedef std::vector<int>::iterator iterator;
    iterator begin() { return myData.begin(); }
    iterator end()   { return myData.end();   }
};

তারপরে আপনি কেবল লিখুন:

for ( Matrix3D::iterator iter = m.begin(); iter != m.end(); ++ iter ) {
    //  ...
}

(অথবা শুধুই:

for ( auto& elem: m ) {
}

আপনার যদি সি ++ 11 থাকে)

এবং যদি এই জাতীয় পুনরাবৃত্তির সময় আপনার তিনটি সূচকের প্রয়োজন হয় তবে একটি পুনরুক্তি তৈরি করা সম্ভব যা এগুলি প্রকাশ করে:

class Matrix3D
{
    //  ...
    class iterator : private std::vector<int>::iterator
    {
        Matrix3D const* owner;
    public:
        iterator( Matrix3D const* owner,
                  std::vector<int>::iterator iter )
            : std::vector<int>::iterator( iter )
            , owner( owner )
        {
        }
        using std::vector<int>::iterator::operator++;
        //  and so on for all of the iterator operations...
        int i() const
        {
            ((*this) -  owner->myData.begin()) / (owner->y * owner->z);
        }
        //  ...
    };
};

21
এই উত্তরটি আরও উত্সাহিত হওয়া উচিত কারণ সমস্যাটির আসল উত্সটিই কেবলমাত্র এটির সাথে সম্পর্কিত deals

5
এটি উত্তম উত্তর হতে পারে তবে আমি সম্মত করি না এটি ভাল a x10 বার ধীর সংকলনের সময় এবং সম্ভবত x10 ধীর ডিবাগ (আরও হতে পারে) কোড সহ প্রচুর ক্রিপ্টিক টেম্পলেট কোড রয়েছে। আমার কাছে অবশ্যই মূল কোডটি আমার কাছে আরও স্পষ্ট ...
গোর্কেম

10
@ বিহরফ ... এবং আরও অনেক ধীর। কারণ সি এবং সি ++ এর বহু-মাত্রিক অ্যারেগুলি প্রকৃত অর্থে নেস্টেড অ্যারেগুলি রয়েছে যে বাহ্যিক মাত্রাগুলি স্টোর করে নেস্টেড অ্যারেগুলিকে নির্দেশ করে। এই নেস্টেড অ্যারেগুলি পরে যেকোন প্রিফেচিং এবং ক্যাশে কার্যকরভাবে পরাস্ত করে মেমোরিটিতে নির্বিচারে ছড়িয়ে দেওয়া হয়। আমি উদাহরণগুলি জানি যেখানে কোনও vector<vector<vector<double> > >ত্রি-মাত্রিক ক্ষেত্রটি উপস্থাপনের জন্য কেউ কোড লিখেছিল । উপরের সমাধানের সমতুল্য কোডটি পুনরায় লেখার ফলে 10 এর গতিবেগ আসে
মাইকেল ওয়াইল্ড

5
@Behorf আপনি কোন টেম্পলেট কোড দেখতে পাচ্ছেন? (বাস্তবে, Matrix3Dসম্ভবত একটি টেমপ্লেট হওয়া উচিত, তবে এটি একটি খুব সোজা টেম্পলেট।) এবং আপনাকে কেবলমাত্র ডিবাগ করতে হবে Matrix3D, প্রতিবারই 3 ডি ম্যাট্রিক্সের প্রয়োজন হবে না, তাই আপনি ডিবাগিংয়ে প্রচুর সময় সাশ্রয় করুন। স্পষ্টতা হিসাবে: কিভাবে std::vector<std::vector<std::vector<int>>>পরিষ্কার Matrix3D? Matrix3Dআপনার ম্যাট্রিক্স রয়েছে এই বিষয়টি প্রয়োগ করে এমনটি উল্লেখ না করা , যখন নেস্টেড ভেক্টরগুলি র‌্যাগ করা যেতে পারে এবং উপরেরটি সম্ভবত উল্লেখযোগ্যভাবে দ্রুত গতিতে এসেছে।
জেমস কানজে

10
@ মিশেলওয়িল্ড অবশ্যই, আমার পদ্ধতির আসল সুবিধা হ'ল ক্লায়েন্ট কোডের কোনও পরিবর্তন না করেই আপনার পরিবেশের দ্রুততর উপর নির্ভর করে আপনি উপস্থাপনাটি পরিবর্তন করতে পারবেন। ভাল পারফরম্যান্সের মূল চাবিকাঠিটি হ'ল এনক্যাপসুলেশন, যাতে আপনি যে পরিবর্তনটি করতে পারেন তা প্রোফাইলার জানান যে পুরো অ্যাপ্লিকেশনটি নতুন করে লেখা না করে আপনার প্রয়োজন।
জেমস কানজে

44

forলুপগুলি লুকানোর জন্য ম্যাক্রো ব্যবহার করা অনেক বিভ্রান্তিকর হতে পারে, কেবল কয়েকটি অক্ষর সংরক্ষণ করতে। আমি ব্যবহার করতাম পরিসর-জন্য পরিবর্তে লুপ:

for (auto& k : A)
    for (auto& i : k)
        for (auto& j : i)
            do_something_on_A(j);

অবশ্যই আপনি প্রতিস্থাপন করতে পারেন auto&সঙ্গে const auto&যদি জিজ্ঞেস করা হয়, আসলে, ডাটা পরিবর্তন নয়।


4
ধরে নিচ্ছি ওপি সি ++ 11 ব্যবহার করতে পারে।
জারোড 42

4
পুনরুদ্ধারকারীদের ক্ষেত্রে @ হেরোহুইংটাও যা এখানে আরও বুদ্ধিমান হতে পারে তবে এমন ঘটনাও রয়েছে যেখানে আপনি তিনটি intভেরিয়েবল চান ।
জেমস কানজে

4
এবং তা হওয়া উচিত নয় do_something_on_A(*j)?
জেমস কানজে 8'14

4
@ জেফফ্রে আহ, হ্যাঁ প্রকারটি বানান করার আর একটি কারণ। (আমি ব্যবহার অনুমান autoজন্য kএবং i; বাস্তব সমস্যা হল তিনি নেস্টেড ভেক্টর ব্যবহার হয় সমর্থনযোগ্য হতে পারে ছাড়া এটা এখনও ভুল পর্যায়ে সমস্যা solves।।)
জেমস Kanze

4
@ ধারা kহ'ল ভেক্টরগুলির সম্পূর্ণ ভেক্টর (এটির একটি ভাল রেফারেন্স), কোনও সূচক নয়।
ইয়াক্ক - অ্যাডাম নেভ্রামাউন্ট

21

এর মতো কিছু সাহায্য করতে পারে:

 template <typename Container, typename Function>
 void for_each3d(const Container &container, Function function)
 {
     for (const auto &i: container)
         for (const auto &j: i)
             for (const auto &k: j)
                 function(k);
 }

 int main()
 {
     vector< vector< vector<int> > > A;     
     for_each3d(A, [](int i){ std::cout << i << std::endl; });

     double B[10][8][5] = { /* ... */ };
     for_each3d(B, [](double i){ std::cout << i << std::endl; });
 }

এটিকে এন-অ্যারি করতে আমাদের কিছু টেমপ্লেট যাদু প্রয়োজন need এই মানটি বা ধারক কিনা তা আলাদা করার জন্য সবার আগে আমাদের SFINAE কাঠামো তৈরি করা উচিত। মানগুলির জন্য ডিফল্ট বাস্তবায়ন এবং অ্যারে এবং প্রতিটি ধারক প্রকারের জন্য বিশেষত্ব। কীভাবে @ জেতা নোট করেন, আমরা নেস্টেড iteratorটাইপ দ্বারা স্ট্যান্ডার্ড পাত্রে নির্ধারণ করতে পারি (আদর্শভাবে আমাদের পরীক্ষা করা উচিত যে টাইপটি পরিসর-বেসের সাথে ব্যবহার করা যায় কিনা for)।

 template <typename T>
 struct has_iterator
 {
     template <typename C>
     constexpr static std::true_type test(typename C::iterator *);

     template <typename>
     constexpr static std::false_type test(...);

     constexpr static bool value = std::is_same<
         std::true_type, decltype(test<typename std::remove_reference<T>::type>(0))
     >::value;
 };

 template <typename T>
 struct is_container : has_iterator<T> {};

 template <typename T>
 struct is_container<T[]> : std::true_type {};

 template <typename T, std::size_t N>
 struct is_container<T[N]> : std::true_type {}; 

 template <class... Args>
 struct is_container<std::vector<Args...>> : std::true_type {};

বাস্তবায়ন for_eachসহজবোধ্য is ডিফল্ট ফাংশন কল করবে function:

 template <typename Value, typename Function>
 typename std::enable_if<!is_container<Value>::value, void>::type
 rfor_each(const Value &value, Function function)
 {
     function(value);
 }

এবং বিশেষীকরণ নিজেকে পুনরাবৃত্তভাবে কল করবে:

 template <typename Container, typename Function>
 typename std::enable_if<is_container<Container>::value, void>::type
 rfor_each(const Container &container, Function function)
 {
     for (const auto &i: container)
         rfor_each(i, function);
 }

এবং ভয়েলা:

 int main()
 {
     using namespace std;
     vector< vector< vector<int> > > A;
     A.resize(3, vector<vector<int> >(3, vector<int>(3, 5)));
     rfor_each(A, [](int i){ std::cout << i << ", "; });
     // 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,

     std::cout << std::endl;
     double B[3][3] = { { 1. } };
     rfor_each(B, [](double i){ std::cout << i << ", "; });
     // 1, 0, 0, 0, 0, 0, 0, 0, 0,
 }

এছাড়াও এটি পয়েন্টারগুলির জন্য কাজ করবে না (গাদাতে বরাদ্দ করা অ্যারে)।


প্রতিবন্ধকতাগুলির সাথে @ হ্যারোহ্যুংটাও আমরা Containerঅন্যদের জন্য এবং তাদের জন্য দুটি বিশেষজ্ঞকরণ প্রয়োগ করতে পারি ।

4
@ হেরোহ্যুংটাও আমি কে-আরি ফোরচের একটি উদাহরণ তৈরি করেছি।

4
@ ফ্যাস্কড: is_container : has_iterator<T>::valueআমার উত্তরটি ব্যবহার করুন এবং আপনার প্রতিটি প্রকারের জন্য একটি বিশেষায়নের লেখার দরকার নেই, কারণ প্রতিটি ধারককে টাইপেইফ থাকতে হবে iterator। আমার উত্তর থেকে সম্পূর্ণরূপে ব্যবহার করতে নির্দ্বিধায় আপনার ইতিমধ্যে আরও ভাল।
জিটা

এই জন্য @ জেতা +1। আমি যেমন উল্লেখ করেছি Containerধারণাটিও সহায়তা করবে।

::iteratorএকটি পুনরাবৃত্ত পরিসীমা করতে পারে না। int x[2][3][4]পুরোপুরি পুনরাবৃত্তিমূলক, যেমনটি struct foo { int x[3]; int* begin() { return x; } int* end() { return x+3; } }; আমি নিশ্চিত নই যে T[]বিশেষায়নের জন্য কী করা উচিত?
ইয়াক্ক - আদম নেভ্রামুমন্ট

17

বেশিরভাগ উত্তর কেবলমাত্র সি ++ কে বোধগম্য সিনট্যাকটিক এক্সটেনশান, আইএমএইচওতে পরিণত করতে পারে তা দেখায়।

যে কোনও টেম্পলেট বা ম্যাক্রো সংজ্ঞায়িত করে আপনি অন্যান্য প্রোগ্রামারগুলিকে অপ্রচলিত কোডের অন্যান্য বিটগুলি আড়াল করার জন্য নকশাকৃত অবলম্বন কোডের বিটগুলি বুঝতে বাধ্য করেন force
আপনার কোডটি পড়া প্রতিটি লোককে আপনি স্পষ্ট শব্দার্থবিজ্ঞানের সাহায্যে অবজেক্টগুলি সংজ্ঞায়িত করার কাজটি এড়াতে কেবল টেমপ্লেট দক্ষতা নিতে বাধ্য করবেন।

যদি আপনি 3 টি মাত্রিক অ্যারেগুলির মতো কাঁচা ডেটা ব্যবহার করার সিদ্ধান্ত নিয়ে থাকেন তবে কেবল এটির সাথেই থাকুন, না হলে এমন কোনও শ্রেণীর সংজ্ঞা দিন যা আপনার ডেটাতে কিছুটা বোধগম্য অর্থ দেয়।

for (auto& k : A)
for (auto& i : k)
for (auto& current_A : i)
    do_something_on_A(current_A);

কোন স্পষ্ট শব্দার্থবিজ্ঞান সহ int এর ভেক্টর এর ভেক্টর এর ভেক্টরের ক্রিপ্টিক সংজ্ঞাটির সাথে সামঞ্জস্যপূর্ণ।


10
#include "stdio.h"

#define FOR(i, from, to)    for(int i = from; i < to; ++i)
#define TRIPLE_FOR(i, j, k, i_from, i_to, j_from, j_to, k_from, k_to)   FOR(i, i_from, i_to) FOR(j, j_from, j_to) FOR(k, k_from, k_to)

int main()
{
    TRIPLE_FOR(i, j, k, 0, 3, 0, 4, 0, 2)
    {
        printf("i: %d, j: %d, k: %d\n", i, j, k);
    }
    return 0;
}

আপডেট: আমি জানি, আপনি এটি চেয়েছিলেন, তবে আপনি এটি ব্যবহার না করাই ভাল হবে :)


5
আমি জানি ওপি যা চেয়েছিল, কিন্তু গুরুত্ব সহকারে ... এটিকে অবলোকনের এক দুর্দান্ত উদাহরণ বলে মনে হচ্ছে। ধরুন TRIPLE_FORকিছু শিরোনামে সংজ্ঞায়িত করা হয়েছিল, আমি যখন এখানে `TRIPLE_FOR দেখি তখন আমি কী ভাবব।
জেমস কানজে 8'14

4
হ্যাঁ, আমার ধারণা, আপনি ঠিক বলেছেন :) আমি মনে করি, আমি ম্যাক্রো ব্যবহার করে এটি করা যেতে পারে তার উদাহরণ হিসাবে এটি এখানে রেখে যাব, তবে একটি নোট যুক্ত করুন যে এটি না করা ভাল :) :) আমি কেবল জেগে উঠলাম আপ, এবং এই প্রশ্নটি মনের জন্য একটি ছোট ওয়ার্ম-আপ হিসাবে ব্যবহার করার সিদ্ধান্ত নিয়েছে।
ফ্রি ডাকনাম

5

একটি ধারণা হ'ল একটি পুনরাবৃত্তযোগ্য সিউডো-ধারক শ্রেণি লিখুন যা সমস্ত মাল্টি-ইনডেক্সের টিপলসকে "ইনডেক্স" করবে যা আপনি সূচী করে নেবেন। এখানে কোনও বাস্তবায়ন নেই কারণ এটি খুব বেশি সময় নিবে তবে ধারণাটি হ'ল আপনার লিখতে সক্ষম হওয়া উচিত ...

multi_index mi (10, 8, 5);
  //  The pseudo-container whose iterators give {0,0,0}, {0,0,1}, ...

for (auto i : mi)
{
  //  In here, use i[0], i[1] and i[2] to access the three index values.
}

সেরা উত্তর এখানে imo।
ডেভিডিঘে

4

আমি এখানে অনেক উত্তর দেখতে পাচ্ছি যে ইনপুটটি ধারক কিনা তা সনাক্ত করে পুনরাবৃত্তির সাথে কাজ করে। পরিবর্তে, বর্তমান স্তরটি যেমন ফাংশনটি গ্রহণ করে, একই ধরণের হয় কিনা তা কেন সনাক্ত করবেন না? এটি অনেক সহজ, এবং আরও শক্তিশালী ফাংশনগুলির জন্য অনুমতি দেয়:

//This is roughly what we want for values
template<class input_type, class func_type> 
void rfor_each(input_type&& input, func_type&& func) 
{ func(input);}

//This is roughly what we want for containers
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func) 
{ for(auto&& i : input) rfor_each(i, func);}

তবে এটি (স্পষ্টতই) আমাদের দ্ব্যর্থতার ত্রুটি দেয়। সুতরাং আমরা বর্তমান ইনপুটটি ফাংশনে ফিট করে কি না তা সনাক্ত করতে আমরা SFINAE ব্যবহার করি

//Compiler knows to only use this if it can pass input to func
template<class input_type, class func_type>
auto rfor_each(input_type&& input, func_type&& func) ->decltype(func(input)) 
{ return func(input);}

//Otherwise, it always uses this one
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func) 
{ for(auto&& i : input) rfor_each(i, func);}

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

template<class input_type, class func_type>
auto rfor_each(input_type&& input, func_type&& func, int) ->decltype(func(input)) 
{ return func(input);}

//passing the zero causes it to look for a function that takes an int
//and only uses ... if it absolutely has to 
template<class input_type, class func_type>
void rfor_each(input_type&& input, func_type&& func, ...) 
{ for(auto&& i : input) rfor_each(i, func, 0);}

এটাই. ছয়টি, কোডের তুলনামূলকভাবে সহজ লাইন এবং আপনি অন্যান্য উত্তরগুলির মতো নয়, মান, সারি বা অন্য কোনও উপ-ইউনিট নিয়ে পুনরাবৃত্তি করতে পারেন।

#include <iostream>
int main()
 {

     std::cout << std::endl;
     double B[3][3] = { { 1.2 } };
     rfor_each(B[1], [](double&v){v = 5;}); //iterate over doubles
     auto write = [](double (&i)[3]) //iterate over rows
         {
             std::cout << "{";
             for(double d : i) 
                 std::cout << d << ", ";
             std::cout << "}\n";
         };
     rfor_each(B, write );
 };

এখানে এবং এখানে সংকলন এবং সম্পাদনের প্রমাণ Pro

আপনি যদি সি ++ 11 এ আরও সুবিধাজনক সিনট্যাক্স চান, আপনি ম্যাক্রো যুক্ত করতে পারেন। (নিম্নলিখিতটি স্বাক্ষরিত নয়)

template<class container>
struct container_unroller {
    container& c;
    container_unroller(container& c_) :c(c_) {}
    template<class lambda>
    void operator <=(lambda&& l) {rfor_each(c, l);}
};
#define FOR_NESTED(type, index, container) container_unroller(container) <= [](type& index) 
//note that this can't handle functions, function pointers, raw arrays, or other complex bits

int main() {
     double B[3][3] = { { 1.2 } };
     FOR_NESTED(double, v, B) {
         std::cout << v << ", ";
     }
}

3

আমি এই জবাবটি নীচের বিবৃতি দিয়ে উত্সাহিত করেছি: এটি কেবল তখনই কাজ করবে যদি আপনি প্রকৃত অ্যারে ব্যবহার করে থাকেন - এটি আপনার উদাহরণ ব্যবহার করে কাজ করবে না std::vector

আপনি যদি প্রতিটি আইটেমের অবস্থান সম্পর্কে চিন্তা না করে কোনও বহুমাত্রিক অ্যারের প্রতিটি উপাদানগুলিতে একই ক্রিয়াকলাপটি চালাচ্ছেন, তবে আপনি এই অ্যারেটিকে সংক্ষিপ্ত মেমরির স্থানে রেখেছেন এবং পুরো জিনিসটিকে এক হিসাবে বিবেচনা করে নিতে পারেন বড় এক-মাত্রিক অ্যারে। উদাহরণস্বরূপ, আমরা যদি আপনার দ্বিতীয় উদাহরণে প্রতিটি উপাদানকে ২.০ দিয়ে গুণ করতে চাই:

double B[3][3][3];
// ... set the values somehow
double* begin = &B[0][0][0];     // get a pointer to the first element
double* const end = &B[3][0][0]; // get a (const) pointer past the last element
for (; end > begin; ++begin) {
    (*begin) *= 2.0;
}

নোট করুন যে উপরোক্ত পদ্ধতির ব্যবহার কিছু "যথাযথ" সি ++ কৌশল ব্যবহারের অনুমতিও দেয়:

double do_something(double d) {
    return d * 2.0;
}

...

double B[3][3][3];
// ... set the values somehow
double* begin = &B[0][0][0];  // get a pointer to the first element
double* end = &B[3][0][0];    // get a pointer past the last element

std::transform(begin, end, begin, do_something);

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



@ ক্যাটমুর: আকর্ষণীয় - আমি সবেমাত্র কাজ করতে পেরেছি, তাই আমি এটি পরীক্ষা করে দেখেছি এবং উত্তর অনুসারে আপডেট / মুছে ফেলব। ধন্যবাদ
আইক্যাবড

@ ক্যাটমুর: আমি সি ++ ১১ টি স্ট্যান্ডার্ড (বিভাগ 8.3.4) দেখেছি এবং আমি যা লিখেছি তা কাজ করা উচিত এবং এটি আমার কাছে অবৈধ মনে হচ্ছে না। আপনার দেওয়া লিঙ্কটি সংজ্ঞায়িত অ্যারে আকারের বাইরে সদস্যদের অ্যাক্সেসের সাথে সম্পর্কিত। যদিও এটি সত্য যে আমি ঠিক অ্যারের অতীতের ঠিকানা পেয়েছি, এটি ডেটা অ্যাক্সেস করছে না - এটি একটি "শেষ" সরবরাহ করার জন্য যাতে আপনি পয়েন্টারগুলিকে পুনরাবৃত্তকারী হিসাবে ব্যবহার করতে পারেন, "শেষ" একটি অতীত হওয়ার সাথে শেষ উপাদান।
আইক্যাবড

আপনি কার্যকরভাবে অ্যাক্সেস B[0][0][i]করছেন i >= 3; এটি (অভ্যন্তরীণ) অ্যারের বাইরে অ্যাক্সেস করার কারণে এটি অনুমোদিত নয়।
ইকামত্মুর

4
শেষ করার নির্ধারনের একটি পরিষ্কার উপায় আইএমও যদি আপনি এটি করতে থাকেন তবে শেষ = শুরু + (xSize * ySize * zSize)
noggin182

2

আমি একপ্রকার হতবাক হয়ে গিয়েছিলাম যে কাজটি করার জন্য কেউ কোনও পাটিগণিত-যাদু ভিত্তিক লুপ প্রস্তাব করেনি। যেহেতু সি ওয়াং কোনও নেস্টেড লুপগুলি ছাড়াই সমাধান খুঁজছেন , তাই আমি এর প্রস্তাব দেব:

double B[10][8][5];
int index = 0;

while (index < (10 * 8 * 5))
{
    const int x = index % 10,
              y = (index / 10) % 10,
              z = index / 100;

    do_something_on_B(B[x][y][z]);
    ++index;
}

ভাল, এই পদ্ধতির মার্জিত এবং নমনীয় নয়, তাই আমরা সমস্ত প্রক্রিয়া একটি টেমপ্লেট ফাংশনে প্যাক করতে পারি:

template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
    const int limit = X * Y * Z;
    int index = 0;

    while (index < limit)
    {
        const int x = index % X,
                  y = (index / X) % Y,
                  z = index / (X * Y);

        func(xyz[x][y][z]);
        ++index;
    }
}

এই টেম্পলেট ফাংশনটি নেস্টেড লুপগুলির আকারেও প্রকাশ করা যেতে পারে:

template <typename F, typename T, int X, int Y, int Z>
void iterate_all(T (&xyz)[X][Y][Z], F func)
{
    for (auto &yz : xyz)
    {
        for (auto &z : yz)
        {
            for (auto &v : z)
            {
                func(v);
            }
        }
    }
}

এবং প্রতিটি আকারের আকার গণনা করার জন্য প্যারামিটার ছাড়ের কঠোর পরিশ্রম করতে দিয়ে সালিশি আকারের সাথে ফাংশন নামের একটি 3 ডি অ্যারে সরবরাহ করে ব্যবহার করা যেতে পারে:

int main()
{
    int A[10][8][5] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
    int B[7][99][8] = {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};

    iterate_all(A, do_something_on_A);
    iterate_all(B, do_something_on_B);

    return 0;
}

আরও জেনেরিকের দিকে

তবে আবারও, এতে নমনীয়তার অভাব রয়েছে কারণ এটি কেবল 3 ডি অ্যারেগুলির জন্যই কাজ করে, তবে SFINAE ব্যবহার করে আমরা একটি স্বেচ্ছাচারিত মাত্রার অ্যারেগুলির জন্য কাজ করতে পারি, প্রথমে আমাদের একটি টেম্পলেট ফাংশন প্রয়োজন যা র‌্যাঙ্ক 1 এর অ্যারেগুলিকে পুনরাবৃত্তি করে :

template<typename F, typename A>
typename std::enable_if< std::rank<A>::value == 1 >::type
iterate_all(A &xyz, F func)
{
    for (auto &v : xyz)
    {
        func(v);
    }
}

এবং অন্য এক যা পুনরাবৃত্তি করে যে কোনও র‌্যাঙ্কের অ্যারেগুলিকে পুনরাবৃত্তি করে:

template<typename F, typename A>
typename std::enable_if< std::rank<A>::value != 1 >::type
iterate_all(A &xyz, F func)
{
    for (auto &v : xyz)
    {
        iterate_all(v, func);
    }
}

এটি আমাদেরকে স্বেচ্ছাচারিত আকারের অ্যারেটারি-আকারের সমস্ত মাত্রায় সমস্ত উপাদানগুলিকে পুনরাবৃত্তি করতে দেয়।


সাথে কাজ করা std::vector

একাধিক নেস্টেড ভেক্টরের জন্য, সমাধানটি সালিশী-মাত্রাগুলি স্বেচ্ছাসৈনিক আকারের অ্যারেগুলির মধ্যে একটিটিকে পুনরায় সাজিয়ে তোলে তবে SFINAE ছাড়াই: প্রথমে আমাদের একটি টেম্পলেট ফাংশন প্রয়োজন যা এর পুনরাবৃত্তি করে std::vectorএবং পছন্দসই ফাংশনটি কল করে:

template <typename F, typename T, template<typename, typename> class V>
void iterate_all(V<T, std::allocator<T>> &xyz, F func)
{
    for (auto &v : xyz)
    {
        func(v);
    }
}

এবং অন্য একটি টেম্পলেট ফাংশন যা কোনও ধরণের ভেক্টরকে পুনরাবৃত্তি করে এবং নিজেকে কল করে:

template <typename F, typename T, template<typename, typename> class V> 
void iterate_all(V<V<T, std::allocator<T>>, std::allocator<V<T, std::allocator<T>>>> &xyz, F func)
{
    for (auto &v : xyz)
    {
        iterate_all(v, func);
    }
}

নেস্টিং স্তর নির্বিশেষে, iterate_allভেক্টর-অফ-ভ্যালু সংস্করণকে কল করা হবে যদি না ভেক্টর অফ-ভ্যালু সংস্করণ আরও ভাল ম্যাচ না হয় যার ফলে পুনরাবৃত্তির অবসান হয়।

int main()
{
    using V0 = std::vector< std::vector< std::vector<int> > >;
    using V1 = std::vector< std::vector< std::vector< std::vector< std::vector<int> > > > >;

    V0 A0 =   {{{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}};
    V1 A1 = {{{{{9, 8}, {7, 6}}, {{5, 4}, {3, 2}}}}};

    iterate_all(A0, do_something_on_A);
    iterate_all(A1, do_something_on_A);

    return 0;
}

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

এখানে লাইভ ডেমো দেখুন ।

আশা করি এটা সাহায্য করবে.


1

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

doOn( structure A, operator o)
{
    for (int k=0; k<A.size(); k++)
    {
            for (int i=0; i<A[k].size(); i++)
            {
                for (int j=0; j<A[k][i].size(); j++)
                {
                        o.actOn(A[k][i][j]);
                }
            }
    }
}

doOn(a, function12)
doOn(a, function13)

1

লুপের জন্য নেস্টেড সাথে লাঠি!

এখানে প্রস্তাবিত সমস্ত পদ্ধতির পাঠযোগ্যতা বা নমনীয়তার দিক থেকে অসুবিধা রয়েছে।

আপনার যদি বাহ্যিক লুপে প্রসেসিংয়ের জন্য কোনও অভ্যন্তরীণ লুপের ফলাফলগুলি ব্যবহার করতে হয় তবে কী হবে? যদি আপনার অভ্যন্তরের লুপের মধ্যে বাইরের লুপ থেকে কোনও মান প্রয়োজন হয় তবে কী হবে? বেশিরভাগ "এনক্যাপসুলেশন" পদ্ধতি এখানে ব্যর্থ হয়।

বিশ্বাস করুন আমি লুপগুলির জন্য নেস্টেড "পরিষ্কার" করার বেশ কয়েকটি প্রচেষ্টা দেখেছি এবং শেষ পর্যন্ত দেখা গেছে যে নেস্টেড লুপটি আসলে সবচেয়ে পরিষ্কার এবং সবচেয়ে নমনীয় সমাধান।


0

আমি যে কৌশলটি ব্যবহার করেছি তা হ'ল টেমপ্লেট। যেমন:

template<typename T> void do_something_on_A(std::vector<T> &vec) {
    for (auto& i : vec) { // can use a simple for loop in C++03
        do_something_on_A(i);
    }
}

void do_something_on_A(int &val) {
    // this is where your `do_something_on_A` method goes
}

তারপরে আপনি কেবল do_something_on_A(A)নিজের মূল কোডটিতে কল করুন । টেম্পলেট ফাংশনটি প্রতিটি মাত্রার জন্য একবার তৈরি হয়ে যায়, প্রথমবারের সাথে T = std::vector<std::vector<int>>, দ্বিতীয়বার দিয়ে T = std::vector<int>

আপনি চাইলে আপনি std::functionদ্বিতীয় যুক্তি হিসাবে এটি আরও জেনেরিক (বা সি ++ 03 তে ফাংশন-জাতীয় বস্তু) ব্যবহার করে তৈরি করতে পারেন:

template<typename T> void do_something_on_vec(std::vector<T> &vec, std::function &func) {
    for (auto& i : vec) { // can use a simple for loop in C++03
        do_something_on_vec(i, func);
    }
}

template<typename T> void do_something_on_vec(T &val, std::function &func) {
    func(val);
}

তারপরে এটিকে কল করুন:

do_something_on_vec(A, std::function(do_something_on_A));

ফাংশনগুলির একই স্বাক্ষর থাকা সত্ত্বেও এটি কাজ করে কারণ std::vectorপ্রকারের সাথে যে কোনও কিছুর জন্য প্রথম ফাংশনটি আরও ভাল মিল ।


0

আপনি এর মতো একটি লুপে সূচকগুলি তৈরি করতে পারেন (এ, বি, সি মাত্রা):

int A = 4, B = 3, C = 3;
for(int i=0; i<A*B*C; ++i)
{
    int a = i/(B*C);
    int b = (i-((B*C)*(i/(B*C))))/C;
    int c = i%C;
}

আমি আপনার সাথে একমত, এটি বিশেষত 3 টি মাত্রার জন্য ডিজাইন করা হয়েছে;)
জানেক

4
এটি অবিশ্বাস্যভাবে ধীর উল্লেখ না!
noggin182

@ নোগজিন 182: প্রশ্নটি গতি সম্পর্কে নয় তবে নেস্টেড লুপগুলি এড়ানো সম্পর্কে ছিল; এছাড়াও, সেখানে অপ্রয়োজনীয় বিভাগ রয়েছে, i / (বি * সি)
জানেক

ঠিক আছে, এটি একটি বিকল্প উপায়, সম্ভবত আরও দক্ষ (জাভাস্ক্রিপ্ট): এর জন্য (var i = 0, j = 0, k = 0; i <A; i + = (j == B-1 && k == C - 1)? 1: 0, জে = (কে == সি - 1)? ((জে == বি -1)? 0: জে + 1): জে, কে = (কে == সি - 1)? 0: কে + 1) so কনসোল.লগ (i + "" + জে "" + কে); }
জানেক

0

আপনি কেবলমাত্র অভ্যন্তরের সর্বাধিক লুপে বিবৃতি উপস্থিত রাখতে চাইলে একটি জিনিস - এবং আপনার উদ্বেগের কোডটির অত্যধিক ভার্বোস প্রকৃতি সম্পর্কে বেশি - একটি আলাদা সাদা স্থান স্কিম ব্যবহার করা। এটি কেবল তখনই কাজ করবে যদি আপনি নিজের লুপগুলি যথেষ্ট পরিমাণে ঠিকঠাক করে বলতে পারেন যাতে সেগুলি সমস্ত এক লাইনে ফিট থাকে।

আপনার প্রথম উদাহরণের জন্য, আমি এটিকে আবার লিখব:

vector< vector< vector<int> > > A;
int i,j,k;
for(k=0;k<A.size();k++) for(i=0;i<A[k].size();i++) for(j=0;j<A[k][i].size();j++) {
    do_something_on_A(A[k][i][j]);
}

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

দ্বিতীয় উদাহরণটি আরও ভাল:

double B[10][8][5];
int i,j,k;

for(k=0;k<10;k++) for(i=0;i<8;i++) for(j=0;j<5;j++) {
    do_something_on_B(B[k][i][j]);
}

এটি আপনার ব্যবহার করতে পছন্দ করার চেয়ে পৃথক হোয়াইটস্পেস কনভেনশন হতে পারে তবে এটি একটি কমপ্যাক্ট ফলাফল অর্জন করে যে তবুও সি / সি ++ (যেমন ম্যাক্রো কনভেনশনস) এর বাইরে কোনও জ্ঞানের প্রয়োজন নেই এবং ম্যাক্রোর মতো কোনও কৌশল প্রয়োজন হয় না।

আপনি যদি সত্যিই ম্যাক্রো চান, তবে আপনি এর মতো কিছু দিয়ে এই পদক্ষেপটি আরও এগিয়ে নিতে পারেন:

#define FOR3(a,b,c,d,e,f,g,h,i) for(a;b;c) for(d;e;f) for(g;h;i)

যা দ্বিতীয় উদাহরণটিতে পরিবর্তিত হবে:

double B[10][8][5];
int i,j,k;

FOR3(k=0,k<10,k++,i=0,i<8,i++,j=0,j<5,j++) {
    do_something_on_B(B[k][i][j]);
}

এবং প্রথম উদাহরণের ভাড়াগুলি আরও ভাল:

vector< vector< vector<int> > > A;
int i,j,k;
FOR3(k=0,k<A.size(),k++,i=0,i<A[k].size(),i++,j=0,j<A[k][i].size(),j++) {
    do_something_on_A(A[k][i][j]);
}

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


4
এগুলির পাঠযোগ্যতা ভয়াবহ। এক forলাইনের উপর একাধিক লুপ জ্যামিং করা এটিকে বেশি পঠনযোগ্য করে তোলে না এটি কম করে

0

এখানে একটি সি ++ 11 বাস্তবায়ন যা পুনরাবৃত্তিযোগ্য সবকিছু পরিচালনা করে। অন্যান্য সমাধানগুলি ::iteratorটাইপিডফ বা অ্যারেযুক্ত ধারকগুলিতে নিজেকে সীমাবদ্ধ করে : তবে একটি for_eachপুনরাবৃত্তির বিষয়ে, একটি ধারক না হয়ে।

আমি is_iterableবৈশিষ্ট্যের একক স্পটে SFINAE বিচ্ছিন্নও করি । প্রেরণ (উপাদান এবং পুনরাবৃত্তের মধ্যে) ট্যাগ প্রেরণের মাধ্যমে সম্পন্ন হয়, যা আমি দেখতে পাই একটি পরিষ্কার সমাধান।

ধারকগুলিতে এবং উপাদানগুলিতে প্রয়োগ করা ফাংশনগুলি সমস্ত নিখুঁতভাবে ফরোয়ার্ড করা হয়, উভয়ই রেঞ্জ এবং ফান্টেক্টরগুলিতে constঅ- constঅ্যাক্সেসের অনুমতি দেয় ।

#include <utility>
#include <iterator>

আমি প্রয়োগ করছি টেমপ্লেট ফাংশন। সমস্ত কিছু বিশদ নেমস্পেসে যেতে পারে:

template<typename C, typename F>
void for_each_flat( C&& c, F&& f );

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

template<typename C, typename F>
void for_each_flat_helper( C&& c, F&& f, std::true_type /*is_iterable*/ ) {
  for( auto&& x : std::forward<C>(c) )
    for_each_flat(std::forward<decltype(x)>(x), f);
}
template<typename D, typename F>
void for_each_flat_helper( D&& data, F&& f, std::false_type /*is_iterable*/ ) {
  std::forward<F>(f)(std::forward<D>(data));
}

এটি লেখার জন্য কিছু বয়লারপ্লেট প্রয়োজন is_iterable। আমি যুক্তি নির্ভরতাযুক্ত অনুসন্ধান করি beginএবং endবিশদ নামস্থানে এটি অনুকরণ করে যা একটি for( auto x : y )লুপ যুক্তিসঙ্গতভাবে ভাল করে:

namespace adl_aux {
  using std::begin; using std::end;
  template<typename C> decltype( begin( std::declval<C>() ) ) adl_begin(C&&);
  template<typename C> decltype( end( std::declval<C>() ) ) adl_end(C&&);
}
using adl_aux::adl_begin;
using adl_aux::adl_end;

TypeSinkযদি কোড বৈধ পরীক্ষা করার জন্য দরকারী। আপনি TypeSink< decltype(কোড করেন ) >এবং যদি codeবৈধ হয় তবে এক্সপ্রেশন হয় void। কোডটি বৈধ না হলে SFINAE কিক করে এবং বিশেষীকরণটি ব্লক করা হয়:

template<typename> struct type_sink {typedef void type;};
template<typename T> using TypeSink = typename type_sink<T>::type;

template<typename T, typename=void>
struct is_iterable:std::false_type{};
template<typename T>
struct is_iterable<T, TypeSink< decltype( adl_begin( std::declval<T>() ) ) >>:std::true_type{};

আমি কেবল পরীক্ষার জন্য begin। একটি adl_endপরীক্ষাও করা যেতে পারে।

চূড়ান্ত বাস্তবায়ন for_each_flatশেষ পর্যন্ত অত্যন্ত সহজ:

template<typename C, typename F>
void for_each_flat( C&& c, F&& f ) {
  for_each_flat_helper( std::forward<C>(c), std::forward<F>(f), is_iterable<C>() );
}        

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

এটি নীচে নীচে: শীর্ষ উত্তরগুলির জন্য নির্দ্বিধায় নির্দ্বিধায়, যা শক্ত। আমি আরও কয়েকটি আরও ভাল কৌশল ব্যবহার করা চাই!


-2

প্রথমত, আপনার ভেক্টরগুলির ভেক্টর ব্যবহার করা উচিত নয়। প্রতিটি ভেক্টরকে সংমিশ্রিত স্মৃতিশক্তি থাকার গ্যারান্টিযুক্ত তবে ভেক্টরগুলির একটি ভেক্টরের "গ্লোবাল" স্মৃতিটি নয় (এবং সম্ভবত তা হবে না)। আপনার সি-স্টাইল অ্যারের পরিবর্তে স্ট্যান্ডার্ড লাইব্রেরি টাইপ অ্যারে ব্যবহার করা উচিত।

using std::array;

array<array<array<double, 5>, 8>, 10> B;
for (int k=0; k<10; k++)
    for (int i=0; i<8; i++)
        for (int j=0; j<5; j++)
            do_something_on_B(B[k][i][j]);

// or, if you really don't like that, at least do this:

for (int k=0; k<10; k++) {
    for (int i=0; i<8; i++) {
        for (int j=0; j<5; j++) {
            do_something_on_B(B[k][i][j]);
        }
    }
}

তবে আরও ভাল, আপনি একটি সাধারণ 3 ডি ম্যাট্রিক্স শ্রেণি সংজ্ঞায়িত করতে পারেন:

#include <stdexcept>
#include <array>

using std::size_t;

template <size_t M, size_t N, size_t P>
class matrix3d {
    static_assert(M > 0 && N > 0 && P > 0,
                  "Dimensions must be greater than 0.");
    std::array<std::array<std::array<double, P>, N>, M> contents;
public:
    double& at(size_t i, size_t j, size_t k)
    { 
        if (i >= M || j >= N || k >= P)
            throw out_of_range("Index out of range.");
        return contents[i][j][k];
    }
    double& operator(size_t i, size_t j, size_t k)
    {
        return contents[i][j][k];
    }
};

int main()
{
    matrix3d<10, 8, 5> B;
        for (int k=0; k<10; k++)
            for (int i=0; i<8; i++)
                for (int j=0; j<5; j++)
                    do_something_on_B(B(i,j,k));
    return 0;
}

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

আপনি প্রক্সি অবজেক্টগুলিও যুক্ত করতে পারেন যাতে আপনি বি [i] বা বি [আই] [জে] করতে পারেন। তারা ভেক্টরগুলি (গাণিতিক অর্থে) এবং ডাবল এবং ম্যাট্রিকগুলি পূর্ণ, সম্ভাব্যভাবে ফিরে আসতে পারে?

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