Std के लाभ :: for_each ओवर फॉर लूप


168

क्या std::for_eachओवर forलूप के कोई फायदे हैं ? मेरे लिए, std::for_eachकेवल कोड की पठनीयता में बाधा प्रतीत होती है। फिर कुछ कोडिंग मानक इसके उपयोग की सलाह क्यों देते हैं?


10
std::for_eachजब उपयोग किया जाता है boost.lambdaया boost.bindअक्सर पठनीयता में सुधार कर सकते हैं

: प्रश्न और स्वीकार किए जाते हैं जवाब 2010 में एक और अधिक अद्यतन है उत्तर के लिए (2018 से) से हैं, यहाँ देख fluentcpp.com/2018/03/30/is-stdfor_each-obsolete
Erel सहगल-Halevi

जवाबों:


181

C ++ 11 (जिसे पहले C ++ 0x कहा जाता था) के साथ अच्छी बात यह है कि इस थकाऊ बहस को सुलझा लिया जाएगा।

मेरा मतलब है, उनके दाहिने दिमाग में कोई भी, जो पूरे संग्रह पर पुनरावृति करना चाहता है, अभी भी इसका उपयोग करेगा

for(auto it = collection.begin(); it != collection.end() ; ++it)
{
   foo(*it);
}

या यह

for_each(collection.begin(), collection.end(), [](Element& e)
{
   foo(e);
});

जब रेंज-आधारित forलूप सिंटैक्स उपलब्ध होता है:

for(Element& e : collection)
{
   foo(e);
}

इस तरह का सिंटैक्स पिछले कुछ समय से जावा और सी # में उपलब्ध है, और वास्तव में हर हाल में जावा या सी # कोड में foreachक्लासिक forलूप्स की तुलना में अधिक लूप हैं ।


12
वास्तव में, स्कूप के साथ एक फॉरेक्स लूप को बढ़ावा देने के लिए लंबे समय से उपलब्ध है, और मैं अभी भी for_each और एक लंबो फ़ंक्शन के साथ चलना चाहता हूं।
विक्टर सेहर

19
संपूर्ण कंटेनर रेंज को चाहने के बारे में धारणा सवाल का हिस्सा नहीं है, इसलिए यह केवल एक आंशिक उत्तर है।
एड्रियन मैक्कार्थी

6
ध्यान दें कि एक तत्व पर लूपिंग करना केवल एक चीज नहीं है जिसे आप करना चाहते हैं, इसलिए for_each का उपयोग करना एक अच्छा विचार हो सकता है ताकि आप खोज / विभाजन / copy_replace_if और अन्य के बारे में सीख सकें, जो वास्तव में लूप के लिए बहुत कुछ है। करना।
मैके

10
रेंज-के लिए अच्छा है, सिवाय जब आपको वास्तव में पुनरावृत्ति की आवश्यकता होती है (तब इसे प्राप्त करने का कोई तरीका नहीं है)।
डेमोन

5
मैं भी (या ) बेहतर लग रहा है के Element & eरूप में उपयोग नहीं करेंगे । जब मैं निहित रूपांतरण चाहता हूं, तो मैं (संदर्भ के बिना) का उपयोग करूंगा, जब स्रोत अलग-अलग प्रकारों का एक संग्रह है, और मैं चाहता हूं कि वे रूपांतरण करें । auto & eauto const &eElement const eElement
नवाज

54

यहाँ कुछ कारण हैं:

  1. ऐसा लगता है कि यह पठनीयता में बाधा डालता है क्योंकि आप इसका उपयोग नहीं करते हैं और / या इसे वास्तव में आसान बनाने के लिए इसके आस-पास सही उपकरणों का उपयोग नहीं करते हैं। (देखें बढ़ावा :: सीमा और बढ़ावा :: बाँध / बढ़ावा :: मदद करने वालों के लिए मेमना। इनमें से कई C ++ 0x में जाएंगे और for_each और संबंधित कार्यों को अधिक उपयोगी बना देंगे।)

  2. यह आपको for_each के शीर्ष पर एक एल्गोरिथ्म लिखने की अनुमति देता है जो किसी भी पुनरावृत्त के साथ काम करता है।

  3. यह बेवकूफ टाइपिंग कीड़ों की संभावना को कम करता है।

  4. यह भी एसटीएल-एल्गोरिदम, जैसे के आराम करने के लिए अपने मन को खोलता है find_if, sort, replace, आदि और इन अब और इतनी अजीब नहीं लग रही होगी। यह बहुत बड़ी जीत हो सकती है।

अपडेट 1:

सबसे महत्वपूर्ण बात यह है कि यह आपको for_eachबनाम फॉर-लूप्स से परे जाने में मदद करता है जैसे कि सब कुछ है, और अन्य एसटीएल-अलॉग्स को देखें, जैसे / खोज / विभाजन / copy_replace_if, समानांतर निष्पादन .. या जो भी हो।

बहुत सारे प्रसंस्करण को for_each के भाई-बहनों के "बाकी" का उपयोग करके बहुत ही स्पष्ट रूप से लिखा जा सकता है, लेकिन यदि आप सभी करते हैं तो विभिन्न आंतरिक तर्क के साथ फॉर-लूप लिखना चाहते हैं, तो आप कभी भी उन का उपयोग करना नहीं सीखेंगे, और आप पहिया को बार-बार ईजाद करना।

और (जल्द ही उपलब्ध होने वाली रेंज-स्टाइल for_each के लिए):

for_each(monsters, boost::mem_fn(&Monster::think));

या C ++ x11 लैम्ब्डा के साथ:

for_each(monsters, [](Monster& m) { m.think(); });

की तुलना में IMO अधिक पठनीय है:

for(Monsters::iterator i = monsters.begin(); i != monsters.end(); ++i) {
    i->think();
} 

यह भी (या लंबोदर के साथ, दूसरों को देखें):

for_each(bananas, boost::bind(&Monkey::eat, my_monkey, _1));

से अधिक संक्षिप्त है:

for(Bananas::iterator i = bananas.begin(); i != bananas.end(); ++i) {
    my_monkey->eat(*i);
} 

खासकर यदि आपके पास ऑर्डर करने के लिए कॉल करने के लिए कई कार्य हैं ... लेकिन शायद वह सिर्फ मैं ही हूं। ;)

अपडेट 2 : मैंने अपने स्वयं के एक-लाइनर रैपरों को स्टाल-अल्गोस के बारे में लिखा है जो कि पुनरावृत्तियों की जोड़ी के बजाय श्रेणियों के साथ काम करते हैं। बढ़ावा देना :: range_ex, एक बार जारी होने पर, इसमें शामिल होगा और शायद यह C ++ 0x में भी होगा?


+1, कई कार्य या नेस्टेड प्रकार: outer_class::inner_class::iteratorया वे टेम्पलेट तर्क हैं: typename std::vector<T>::iterator... निर्माण के लिए स्वयं में कई लाइन निर्माण में चला सकते हैं
डेविड रॉड्रिग्ज़ - dribeas

4
(btw: for_eachदूसरे उदाहरण में गलत है (होना चाहिएfor_each( bananas.begin(), bananas.end(),...
डेविड रॉड्रिग्ज़ - dribeas

मैंने ऐसे रैपर लिखे हैं जो दो पुनरावृत्तियों के बजाय श्रेणियों का उपयोग करते हैं। ये बाद में उपलब्ध होंगे (रेंज_एक्स देखें) लेकिन सभी को वैसे भी होना चाहिए। (उस पर अपडेट जोड़ा गया।)
मैके

1
समानांतर प्रसंस्करण समर्थन नहीं है। 1 कारण यहाँ। हम विषम-समानांतर कंप्यूटिंग के लिए cuda / gpu का उपयोग करने के लिए कार्यान्वयन जोड़ सकते हैं।
शुवा

25

for_eachअधिक सामान्य है। आप इसे किसी भी प्रकार के कंटेनर (आरंभ / अंत पुनरावृत्तियों में पास करके) पर पुनरावृति करने के लिए उपयोग कर सकते हैं। आप संभावित रूप से एक फ़ंक्शन के नीचे कंटेनरों को स्वैप कर सकते हैं जो for_eachपुनरावृत्ति कोड को अपडेट किए बिना उपयोग करता है । आपको यह विचार करने की आवश्यकता है कि दुनिया में अन्य कंटेनर हैं std::vectorऔर उनके फायदे देखने के लिए पुराने सी सरणियां हैं for_each

इसका बड़ा दोष for_eachयह है कि यह एक फन्नेकार लेता है, इसलिए वाक्यविन्यास स्पष्ट है। यह C ++ 11 (पूर्व में C ++ 0x) लैंबडास की शुरुआत के साथ तय किया गया है:

std::vector<int> container;
...
std::for_each(container.begin(), container.end(), [](int& i){
    i+= 10;
});

यह 3 साल में आपको अजीब नहीं लगेगा।


3
+1। विज्ञापन जब for_each दो पुनरावृत्तियों के बजाय एक कंटेनर / श्रेणी लेता है, तो यह कभी अधिक भयानक होता है।
मैके

2
@ मैर्कस: जो कि रंग-बिरंगा होगा और सिंटेक्स अपने आप में 'for_each' नहीं पढ़ेगा: for ( int v : int_vector ) {(भले ही इसे आज BOOST_FOREACH के साथ अनुकरण किया जा सके)
डेविड रॉड्रिग्ज - dribeas

@ डेविड: मैं रेंज-आधारित फ़ंक्शंस के सामान्य जोड़ का उल्लेख कर रहा हूं (ताकि आप इन सभी for_each, copy, remove_if, आदि फ़ंक्शंस के साथ रेंज का उपयोग कर सकें)
Macke

1
यह लिखना संभव क्यों नहीं है std::for_each(container, [](int& i){ ... });:। मेरा मतलब है कि कोई दो बार कंटेनर लिखने के लिए मजबूर क्यों है?
जियोर्जियो

1
@freitass: मेरी पिछली टिप्पणी के अनुसार एक बार कंटेनर लिखना, स्पष्ट रूप से कॉल किए बिना, शुरुआत के अंत का उपयोग करने के लिए डिफ़ॉल्ट रूप से हो सकता है। अधिकांश भाषाएं जो संग्रह पर उच्च-क्रम के कार्य प्रदान करती हैं (रूबी, स्काला, ...) container.each { ... }शुरू करने और अंत करने वाले का उल्लेख किए बिना कुछ लिखें । मुझे यह थोड़ा बेमानी लगता है कि मुझे हर समय अंतिम पुनरावृत्ति निर्दिष्ट करना है।
जियोर्जियो

18

व्यक्तिगत रूप से, किसी भी समय मुझे उपयोग करने के लिए अपने रास्ते से बाहर जाने की आवश्यकता होगी std::for_each(विशेष प्रयोजन के फंक्शंस / जटिल boost::lambdaएस लिखना ), मुझे लगता है BOOST_FOREACHऔर सी ++ 0x की सीमा-आधारित क्लीयर के लिए:

BOOST_FOREACH(Monster* m, monsters) {
     if (m->has_plan()) 
         m->act();
}

बनाम

std::for_each(monsters.begin(), monsters.end(), 
  if_then(bind(&Monster::has_plan, _1), 
    bind(&Monster::act, _1)));

12

इसकी बहुत व्यक्तिपरक, कुछ कहेंगे कि for_each कोड को अधिक पठनीय बनाया जाएगा, क्योंकि यह एक ही सम्मेलनों के साथ विभिन्न संग्रहों का इलाज करने की अनुमति देता है। for_eachइसके लूप को लूप के रूप में लागू किया जाता है

template<class InputIterator, class Function>
  Function for_each(InputIterator first, InputIterator last, Function f)
  {
    for ( ; first!=last; ++first ) f(*first);
    return f;
  }

तो यह आप पर निर्भर है कि आपके लिए क्या सही है।


11

एल्गोरिथ्म के कई कार्यों की तरह, एक प्रारंभिक प्रतिक्रिया यह सोचने के लिए है कि लूप की तुलना में फोरच का उपयोग करना अधिक अपठनीय है। यह कई लौ युद्धों का विषय रहा है।

एक बार जब आप मुहावरे के लिए अभ्यस्त हो जाते हैं तो आपको यह उपयोगी लग सकता है। एक स्पष्ट लाभ यह है कि यह कोडर को वास्तविक पुनरावृत्ति कार्यक्षमता से लूप की आंतरिक सामग्री को अलग करने के लिए मजबूर करता है। (ठीक है, मुझे लगता है कि यह एक फायदा है। अन्य का कहना है कि आप कोड को बिना वास्तविक लाभ के काट रहे हैं)।

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

एक के लिए पाश पाश समाप्त के लिए कई विकल्प देता है। आप लूप को अपना पूरा कोर्स चलाने दे सकते हैं, या आप लूप से स्पष्ट रूप से कूदने के लिए ब्रेक कीवर्ड का उपयोग कर सकते हैं , या पूरे फ़ंक्शन मिड-लूप से बाहर निकलने के लिए रिटर्न कीवर्ड का उपयोग कर सकते हैं । इसके विपरीत, फोरचेक इन विकल्पों की अनुमति नहीं देता है, और यह इसे अधिक पठनीय बनाता है। आप केवल फ़ंक्शन नाम पर नज़र डाल सकते हैं और आप पुनरावृति की पूर्ण प्रकृति को जानते हैं।

यहाँ लूप के लिए एक भ्रामक उदाहरण दिया गया है :

for(std::vector<widget>::iterator i = v.begin(); i != v.end(); ++i)
{
   /////////////////////////////////////////////////////////////////////
   // Imagine a page of code here by programmers who don't refactor
   ///////////////////////////////////////////////////////////////////////
   if(widget->Cost < calculatedAmountSofar)
   {
        break;
   }
   ////////////////////////////////////////////////////////////////////////
   // And then some more code added by a stressed out juniour developer
   // *#&$*)#$&#(#)$#(*$&#(&*^$#(*$#)($*#(&$^#($*&#)$(#&*$&#*$#*)$(#*
   /////////////////////////////////////////////////////////////////////////
   for(std::vector<widgetPart>::iterator ip = widget.GetParts().begin(); ip != widget.GetParts().end(); ++ip)
   {
      if(ip->IsBroken())
      {
         return false;
      }
   }
}

1
आप एक अच्छा बिंदु बनाते हैं, लेकिन आपका प्रेरणा उदाहरण पूरी तरह से उचित नहीं है। जब आप std::for_each()पुराने मानक (इस पोस्ट के समय) का उपयोग करते हैं, तो आपको एक नामित फ़ंक्टर का उपयोग करना होगा, जो आपके कहने के अनुसार पठनीयता को प्रोत्साहित करता है और समय से पहले लूप से बाहर निकलने पर रोक लगाता है। लेकिन तब समतुल्य forलूप के पास एक फ़ंक्शन कॉल के अलावा कुछ भी नहीं है, और वह भी समय से पहले टूटने पर रोक लगाता है। लेकिन तुलना में मुझे लगता है कि आपको लगता है कि कह में एक उत्कृष्ट मुद्दा बनाया अन्य std::for_each() लागू करता है संपूर्ण रेंज से गुजर रही।
विल्हेमटेल

10

आप ज्यादातर सही हैं: ज्यादातर समय, std::for_eachएक शुद्ध नुकसान है। मैं तुलना for_eachकरने के लिए इतनी दूर जाऊँगा gotogotoसबसे बहुमुखी प्रवाह-नियंत्रण संभव प्रदान करता है - आप इसका उपयोग लगभग किसी अन्य नियंत्रण संरचना को लागू करने के लिए कर सकते हैं जिसकी आप कल्पना कर सकते हैं। हालाँकि, यह बहुत ही चंचलता का अर्थ है कि gotoअलगाव को देखते हुए आपको इस स्थिति में क्या करना है, इसके बारे में लगभग कुछ भी नहीं बताता है। परिणामस्वरूप, उनके gotoअंतिम दिमाग में लगभग कोई भी अंतिम उपाय के रूप में उपयोग नहीं करता है ।

मानक एल्गोरिदम के बीच, for_eachएक ही तरीका है - इसका उपयोग वस्तुतः किसी भी चीज़ को लागू करने के लिए किया जा सकता है, जिसका अर्थ है कि देखने for_eachसे आपको इस स्थिति में इसका उपयोग करने के बारे में वस्तुतः कुछ भी नहीं बताता है। दुर्भाग्य से, लोगों का रुख for_eachइस बारे में है कि उनका रुख goto1970 (या) में कहां था - कुछ लोगों ने इस तथ्य को पकड़ा था कि इसका उपयोग केवल अंतिम उपाय के रूप में किया जाना चाहिए, लेकिन कई लोग इसे अभी भी प्राथमिक एल्गोरिथ्म मानते हैं, और शायद ही कभी किसी अन्य का उपयोग करें। अधिकांश समय, यहां तक ​​कि एक त्वरित नज़र से पता चलता है कि विकल्पों में से एक काफी बेहतर था।

उदाहरण के लिए, मुझे पूरा यकीन है कि मैंने कितनी बार ट्रैक खो दिया है कि मैंने लोगों को एक संग्रह की सामग्री का उपयोग करने के लिए कोड लिखने के लिए देखा है for_each। मैंने देखे गए पोस्टों के आधार पर, यह सबसे आम उपयोग हो सकता है for_each। वे कुछ इस तरह से समाप्त करते हैं:

class XXX { 
// ...
public:
     std::ostream &print(std::ostream &os) { return os << "my data\n"; }
};

और अपनी पोस्ट के किसी भी संयोजन के बारे में पूछ रहा है bind1st, mem_funआदि वे की तरह कुछ करने की जरूरत है:

std::vector<XXX> coll;

std::for_each(coll.begin(), coll.end(), XXX::print);

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

सौभाग्य से, एक बेहतर तरीका है। XXX के लिए एक सामान्य स्ट्रीम आवेषण अधिभार जोड़ें:

std::ostream &operator<<(std::ostream *os, XXX const &x) { 
   return x.print(os);
}

और उपयोग करें std::copy:

std::copy(coll.begin(), coll.end(), std::ostream_iterator<XXX>(std::cout, "\n"));

यह काम करता है - और यह पता लगाने के लिए कि यह की सामग्री को प्रिंट करता है वस्तुतः कोई काम नहीं लेता collहै std::cout


+1, हालांकि एक गलती है। पहले उदाहरण में यह boost::mem_fn(&XXX::print)बजाय होना चाहिएXXX::print
लापता

इसलिए मैंने कहा कि उदाहरण से काम नहीं चलेगा, और वे इसे काम में लाने के लिए मदद मांग रहे हैं (ओह, और आपको std::coutइसे काम करने के लिए इसके तर्क के रूप में बांधने की भी जरूरत है )।
जेरी कॉफिन

1
हालांकि यह आम तौर पर एक अच्छा जवाब है, यह सवाल for_each के मूल्य या अन्य std एल्गोरिदम की तुलना में इसके मूल्य के बारे में नहीं है, बल्कि लूप की तुलना में इसके मूल्य के बारे में है। आप उन मामलों में इसके बारे में सोच सकते हैं जहां कोई अन्य एसटीडी एल्गोरिथ्म लागू नहीं होता है। तब आप for_each या लूप के लिए उपयोग करेंगे? इसके बारे में सोचें और आप जो भी लेकर आएं, वही आपका जवाब होना चाहिए।
क्रिश्चियन राऊ

@ChristianRau: हमेशा पूछे गए सवाल का सही जवाब देने और उपयोगी जानकारी देने की कोशिश के बीच एक अच्छी लाइन होती है। उनके द्वारा पूछे गए प्रश्नों के सटीक उत्तर "संभवतः नहीं है। कौन जानता है?", लेकिन परेशान होने के लायक भी बेकार होगा। एक ही समय में बहुत दूर जा रहे हैं (उदाहरण के लिए, उपरोक्त में से किसी के बजाय हास्केल की सिफारिश करना) या तो बहुत अधिक उपयोग की संभावना नहीं है।
जेरी कॉफिन

3
@ChristianRau: आप कैसे आंकते हैं कि "... ज्यादातर समय, std :: for_each एक शुद्ध हानि है" क्या इस सवाल का समाधान नहीं है कि क्या std :: for_each एक लाभ प्रदान करता है?
जेरी कॉफिन

8

अधिक पठनीय मधुमक्खी के लिए कार्यात्मक लिखने का लाभ, जब for(...)और for_each(...) नहीं दिखा सकता है ।

यदि आप सभी एल्गोरिदम को कार्यात्मक में उपयोग करते हैं। इसके लिए, लूप का उपयोग करने के बजाय, कोड बहुत अधिक पठनीय हो जाता है;

iterator longest_tree = std::max_element(forest.begin(), forest.end(), ...);
iterator first_leaf_tree = std::find_if(forest.begin(), forest.end(), ...);
std::transform(forest.begin(), forest.end(), firewood.begin(), ...);
std::for_each(forest.begin(), forest.end(), make_plywood);

की तुलना में बहुत अधिक पठनीय है;

Forest::iterator longest_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (*it > *longest_tree) {
     longest_tree = it;
   }
}

Forest::iterator leaf_tree = it.begin();
for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
   if (it->type() == LEAF_TREE) {
     leaf_tree  = it;
     break;
   }
}

for (Forest::const_iterator it = forest.begin(), jt = firewood.begin(); 
     it != forest.end(); 
     it++, jt++) {
          *jt = boost::transformtowood(*it);
    }

for (Forest::const_iterator it = forest.begin(); it != forest.end(); ++it{
    std::makeplywood(*it);
}

और जो मुझे लगता है कि बहुत अच्छा है, फॉर-लूप्स को एक पंक्ति के कार्यों के लिए सामान्यीकृत करें =)


6

आसान: for_eachतब उपयोगी होता है जब आपके पास पहले से ही हर ऐरर आइटम को संभालने का फंक्शन होता है, इसलिए आपको लैम्बडा नहीं लिखना होता है। निश्चित रूप से, यह

for_each(a.begin(), a.end(), a_item_handler);

से बेहतर है

for(auto& item: a) {
    a_item_handler(a);
}

इसके अलावा, forलूप शुरू से अंत तक पूरे कंटेनरों से अधिक पुनरावृत्त होता है, जबकि for_eachअधिक लचीला होता है।


4

for_eachपाश उपयोगकर्ता कोड से iterators (कैसे एक पाश कार्यान्वित किया जाता है के विस्तार) छिपाने के लिए और संचालन पर स्पष्ट अर्थ विज्ञान को परिभाषित करने के लिए है: प्रत्येक तत्व ठीक एक बार दोहराया कर दिया जाएगा।

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

struct myfunctor {
   void operator()( int arg1 ) { code }
};
void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), myfunctor() );
   // more code
}

ध्यान दें कि यदि आप प्रत्येक ऑब्जेक्ट पर एक विशिष्ट ऑपरेशन करना चाहते हैं, तो आप इसे सरल बनाने के लिए उपयोग कर सकते हैं std::mem_fn, या boost::bind( std::bindअगले मानक में), या boost::lambda(अगले मानक में लंब्बास):

void function( int value );
void apply( std::vector<X> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), boost::bind( function, _1 ) );
   // code
}

यदि आप हाथ से रोल किए गए संस्करण की तुलना में कम पठनीय और अधिक कॉम्पैक्ट नहीं हैं, अगर आपके पास जगह में कॉल करने के लिए फ़ंक्शन / विधि है। कार्यान्वयन for_eachलूप के अन्य कार्यान्वयन प्रदान कर सकता है (समानांतर प्रसंस्करण सोचें)।

आगामी मानक अलग-अलग तरीकों से कुछ कमियों का ध्यान रखता है, यह स्थानीय स्तर पर परिभाषित वर्गों को तर्कों के तर्क के रूप में अनुमति देगा:

void apply( std::vector<int> const & v ) {
   // code
   struct myfunctor {
      void operator()( int ) { code }
   };
   std::for_each( v.begin(), v.end(), myfunctor() );
   // code
}

कोड की स्थानीयता में सुधार: जब आप ब्राउज़ करते हैं तो आप देखते हैं कि यह वहीं क्या कर रहा है। तथ्य की बात के रूप में, आपको फ़ंफ़र को परिभाषित करने के लिए वर्ग सिंटैक्स का उपयोग करने की भी आवश्यकता नहीं है, लेकिन ठीक वहां लैम्बडा का उपयोग करें:

void apply( std::vector<int> const & v ) {
   // code
   std::for_each( v.begin(), v.end(), 
      []( int ) { // code } );
   // code
}

यहां तक ​​कि अगर for_eachवहाँ के मामले के लिए एक विशिष्ट निर्माण होगा जो इसे और अधिक प्राकृतिक बना देगा:

void apply( std::vector<int> const & v ) {
   // code
   for ( int i : v ) {
      // code
   }
   // code
}

मैं for_eachहाथ से रोल किए गए छोरों के साथ निर्माण को मिलाता हूं । जब किसी मौजूदा फ़ंक्शन या विधि के लिए केवल एक कॉल की आवश्यकता होती है, तो मुझे for_each( v.begin(), v.end(), boost::bind( &Type::update, _1 ) )उस for_eachनिर्माण के लिए जाना जाता है जो कोड से बहुत सारे बॉयलर प्लेट इट्रेटर सामान को दूर ले जाता है। जब मुझे कुछ अधिक जटिल की आवश्यकता होती है और मैं वास्तविक उपयोग के ऊपर सिर्फ एक-दो पंक्तियों में एक फ़नकार को लागू नहीं कर सकता, तो मैं अपने लूप को रोल करता हूं (ऑपरेशन चालू रखता है)। कोड के गैर-महत्वपूर्ण अनुभागों में मैं BOOST_FOREACH के साथ जा सकता हूं (एक सहकर्मी मुझे इसमें मिला)


3

पठनीयता और प्रदर्शन के अलावा, एक पहलू जिसे आमतौर पर अनदेखा किया जाता है, वह है स्थिरता। पुनरावृत्तियों पर (या जबकि) लूप के लिए लागू करने के कई तरीके हैं:

for (C::iterator iter = c.begin(); iter != c.end(); iter++) {
    do_something(*iter);
}

सेवा:

C::iterator iter = c.begin();
C::iterator end = c.end();
while (iter != end) {
    do_something(*iter);
    ++iter;
}

दक्षता और बग क्षमता के विभिन्न स्तरों के बीच कई उदाहरणों के साथ।

हालाँकि, for_each का उपयोग करना लूप को अमूर्त करके स्थिरता को लागू करता है:

for_each(c.begin(), c.end(), do_something);

अब आपको केवल एक चीज की चिंता करनी है: क्या आप लूप बॉडी को फंक्शन, एक फनकार, या एक लैंबडा को बूस्ट या सी ++ 0x सुविधाओं का उपयोग करके लागू करते हैं? व्यक्तिगत रूप से, मैं इस बारे में चिंता करना चाहता हूं कि लूप के लिए / यादृच्छिक तरीके से कैसे लागू किया जाए या कैसे पढ़ा जाए।


3

मैं नापसंद करता था std::for_eachऔर सोचता था कि मेमने के बिना यह सरासर गलत था। हालांकि मैंने कुछ समय पहले अपना विचार बदल दिया था, और अब मैं वास्तव में इसे प्यार करता हूं। और मुझे लगता है कि यह पठनीयता को भी बेहतर बनाता है, और अपने कोड को TDD तरीके से जांचना आसान बनाता है।

std::for_eachएल्गोरिथ्म के रूप में पढ़ा जा सकता है रेंज में सभी तत्वों के साथ क्या कुछ है, जो कर सकते हैं पठनीयता में सुधार। उस क्रिया को कहें, जिसे आप करना चाहते हैं, 20 रेखाएँ लंबी हैं और वह क्रिया जहाँ क्रिया की जाती है वह भी लगभग 20 रेखाएँ लंबी होती हैं। यह लूप के लिए एक पारंपरिक के साथ 40 पंक्तियों की लंबी और 20 के साथ केवल एक फ़ंक्शन std::for_eachबनाता है, इस प्रकार समझ में आसान होने की संभावना है।

std::for_eachअधिक जेनेरिक होने की संभावना के लिए फ़नकार , और इस प्रकार पुन: प्रयोज्य हैं, जैसे:

struct DeleteElement
{
    template <typename T>
    void operator()(const T *ptr)
    {
        delete ptr;
    }
};

और कोड में आपके पास केवल एक-लाइनर होगा std::for_each(v.begin(), v.end(), DeleteElement())जो स्पष्ट लूप की तुलना में थोड़ा बेहतर आईएमओ है।

उन सभी फंक्शनलर्स को लंबे फ़ंक्शन के बीच लूप के लिए स्पष्ट रूप से इकाई परीक्षणों के तहत प्राप्त करना आसान होता है, और अकेले ही मेरे लिए एक बड़ी जीत है।

std::for_each आम तौर पर अधिक विश्वसनीय भी है, क्योंकि आप सीमा के साथ गलती करने की संभावना कम है।

और अंत में, संकलक std::for_eachलूप के लिए हाथ से तैयार किए गए कुछ प्रकारों की तुलना में थोड़ा बेहतर कोड का उत्पादन कर सकता है , क्योंकि यह (for_each) हमेशा संकलक के लिए समान दिखता है, और संकलक लेखक अपने सभी ज्ञान डाल सकते हैं, जैसा कि वे इसे अच्छा बनाते हैं। कर सकते हैं।

उसी तरह अन्य std एल्गोरिदम पर लागू होता है find_if, transformआदि।


2

forलूप के लिए है कि प्रत्येक तत्व या हर तीसरे आदि for_eachको पुनरावृत्त कर सकते हैं केवल प्रत्येक तत्व को पुनरावृत्त करने के लिए है। यह इसके नाम से स्पष्ट है। इसलिए यह अधिक स्पष्ट है कि आप अपने कोड में क्या करना चाहते हैं।


4
यदि आप इसे एक पुनरावृत्ति पास नहीं करते हैं जो प्रत्येक के साथ 3 से आगे बढ़ता है ++। असामान्य हो सकता है, लेकिन ऐसा करने के लिए एक लूप है।
पोटाटोज़वाटर

उस स्थिति में शायद transformकिसी को भ्रमित न करने के लिए इसका उपयोग करना बेहतर होगा ।
किरिल वी। ल्याडविंस्की

2

यदि आप अक्सर एसटीएल से अन्य एल्गोरिदम का उपयोग करते हैं, तो इसके कई फायदे हैं for_each:

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

    1. आपको बाद में एक अलग कंटेनर के लिए काम करने के लिए कोड को अनुकूलित करने की आवश्यकता हो सकती है।
    2. शुरुआत में, यह आपको कुछ सिखा सकता है और / या बेहतर के लिए अपनी आदतों को बदल सकता है।
    3. यहां तक ​​कि अगर आप हमेशा लूप के लिए लिखेंगे, जो पूरी तरह से समकक्ष हैं, तो समान कोड को संशोधित करने वाले अन्य लोग बिना उपयोग किए जाने के लिए ऐसा नहीं कर सकते हैं for_each
  4. for_eachकभी-कभी उपयोग करने से यह अधिक स्पष्ट होता है कि आप एक ही कार्य करने के लिए अधिक विशिष्ट एसटीएल फ़ंक्शन का उपयोग कर सकते हैं। (जेरी कॉफिन के उदाहरण के रूप में, यह जरूरी नहीं है कि for_eachयह सबसे अच्छा विकल्प है, लेकिन लूप के लिए एकमात्र विकल्प नहीं है।)


2

C ++ 11 और दो सरल टेम्पलेट्स के साथ, आप लिख सकते हैं

        for ( auto x: range(v1+4,v1+6) ) {
                x*=2;
                cout<< x <<' ';
        }

for_eachया पाश के प्रतिस्थापन के रूप में । क्यों यह संक्षिप्तता और सुरक्षा के लिए उबलता है, एक अभिव्यक्ति में त्रुटि का कोई मौका नहीं है जो वहां नहीं है।

मेरे लिए, for_eachहमेशा उसी आधार पर बेहतर था जब लूप बॉडी पहले से ही एक फ़नकार है, और मैं कोई भी लाभ ले सकता हूं जो मुझे मिल सकता है।

आप अभी भी तीन-अभिव्यक्ति का उपयोग करते हैं for, लेकिन अब जब आप एक को देखते हैं तो आप जानते हैं कि वहां कुछ समझने के लिए है, यह बॉयलरप्लेट नहीं है। मुझे बॉयलरप्लेट से नफरत है । मुझे इसके अस्तित्व पर नाराजगी है। यह वास्तविक कोड नहीं है, इसे पढ़ने से कुछ भी सीखने को नहीं है, यह सिर्फ एक और चीज है जिसे जांचना आवश्यक है। मानसिक जाँच से मापा जा सकता है कि इसकी जाँच में जंग लगना कितना आसान है।

टेम्प्लेट हैं

template<typename iter>
struct range_ { 
                iter begin() {return __beg;}    iter end(){return __end;}
            range_(iter const&beg,iter const&end) : __beg(beg),__end(end) {}
            iter __beg, __end;
};

template<typename iter>
range_<iter> range(iter const &begin, iter const &end)
    { return range_<iter>(begin,end); }

1

अधिकतर आपको पूरे संग्रह पर पुनरावृति करनी होगी । इसलिए मेरा सुझाव है कि आप अपना स्वयं का for_each () संस्करण लिखें, केवल 2 मापदंडों को लेते हुए। यह आपको टेरी महाफ़ी के उदाहरण को फिर से लिखने की अनुमति देगा :

for_each(container, [](int& i) {
    i += 10;
});

मुझे लगता है कि यह वास्तव में लूप के लिए अधिक पठनीय है। हालाँकि, इसके लिए C ++ 0x कंपाइलर एक्सटेंशन की आवश्यकता होती है।


1

मुझे लगता है कि for_each पठनीयता के लिए बुरा है। अवधारणा एक अच्छी है, लेकिन सी ++ पढ़ने में लिखने के लिए बहुत मुश्किल है, कम से कम मेरे लिए। c ++ 0x लाम्दा के भाव मदद करेंगे। मुझे वास्तव में लामाओं का विचार पसंद है। हालाँकि, पहली नज़र में मुझे लगता है कि वाक्य रचना बहुत बदसूरत है और मुझे 100% यकीन नहीं है कि मुझे कभी इसकी आदत होगी। हो सकता है 5 साल में मुझे इसकी आदत हो गई हो और इसे दूसरा विचार न दिया हो, लेकिन शायद नहीं। समय बताएगा :)

मैं उपयोग करना पसंद करता हूं

vector<thing>::iterator istart = container.begin();
vector<thing>::iterator iend = container.end();
for(vector<thing>::iterator i = istart; i != iend; ++i) {
  // Do stuff
}

मुझे लूप क्लीयर पढ़ने के लिए एक स्पष्ट लगता है और प्रारंभ और अंत पुनरावृत्तियों के लिए नामित चर का उपयोग करते हुए अन्वेषण लूप के लिए अव्यवस्था को कम करता है।

बेशक मामलों में भिन्नता है, यह वही है जो मुझे आमतौर पर सबसे अच्छा लगता है।


0

आपके पास पुनरावृत्ति एक फ़ंक्शन के लिए कॉल हो सकता है जो लूप के माध्यम से प्रत्येक पुनरावृत्ति पर किया जाता है।

यहां देखें: http://www.cplusplus.com/reference/algorithm/for_each/


2
लिंक-केवल पोस्ट अच्छे उत्तर नहीं देते हैं, और वैसे भी, जहां उस लिंक में यह कॉल करने योग्य पुनरावृत्ति जैसा दिखता है? मुझे पूरा यकीन है कि अवधारणा का कोई मतलब नहीं है। हो सकता है कि आप सिर्फ संक्षेप में बता रहे हों कि for_eachवह क्या करता है, इस मामले में, वह अपने फायदे के बारे में सवाल का जवाब नहीं देता है।
अंडरस्कोर_ड 13

0

के लिए पाश टूट सकता है; मैं हर्ब सटर के लिए तोता नहीं बनना चाहता, इसलिए यहां उनकी प्रस्तुति की कड़ी है: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T टिप्पणियों को भी अवश्य पढ़ें :)


0

for_eachहमें फोर्क-जॉइन पैटर्न को लागू करने की अनुमति दें । इसके अलावा यह धाराप्रवाह-इंटरफ़ेस का समर्थन करता है

कांटा-जुड़ाव पैटर्न

हम gpu::for_eachकई श्रमिकों में लंबो कार्य को कॉल करके विषम-समानांतर कंप्यूटिंग के लिए क्यूडा / जीपीयू का उपयोग करने के लिए कार्यान्वयन जोड़ सकते हैं ।

gpu::for_each(users.begin(),users.end(),update_summary);
// all summary is complete now
// go access the user-summary here.

और gpu::for_eachअगले बयानों को निष्पादित करने से पहले सभी लंबोदर कार्यों को पूरा करने के लिए श्रमिकों की प्रतीक्षा कर सकते हैं।

धाराप्रवाह इंटरफ़ेस

यह हमें संक्षिप्त तरीके से मानव-पठनीय कोड लिखने की अनुमति देता है।

accounts::erase(std::remove_if(accounts.begin(),accounts.end(),used_this_year));
std::for_each(accounts.begin(),accounts.end(),mark_dormant);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.