क्या std::for_each
ओवर for
लूप के कोई फायदे हैं ? मेरे लिए, std::for_each
केवल कोड की पठनीयता में बाधा प्रतीत होती है। फिर कुछ कोडिंग मानक इसके उपयोग की सलाह क्यों देते हैं?
क्या std::for_each
ओवर for
लूप के कोई फायदे हैं ? मेरे लिए, std::for_each
केवल कोड की पठनीयता में बाधा प्रतीत होती है। फिर कुछ कोडिंग मानक इसके उपयोग की सलाह क्यों देते हैं?
जवाबों:
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
लूप्स की तुलना में अधिक लूप हैं ।
Element & e
रूप में उपयोग नहीं करेंगे । जब मैं निहित रूपांतरण चाहता हूं, तो मैं (संदर्भ के बिना) का उपयोग करूंगा, जब स्रोत अलग-अलग प्रकारों का एक संग्रह है, और मैं चाहता हूं कि वे रूपांतरण करें । auto & e
auto const &e
Element const e
Element
यहाँ कुछ कारण हैं:
ऐसा लगता है कि यह पठनीयता में बाधा डालता है क्योंकि आप इसका उपयोग नहीं करते हैं और / या इसे वास्तव में आसान बनाने के लिए इसके आस-पास सही उपकरणों का उपयोग नहीं करते हैं। (देखें बढ़ावा :: सीमा और बढ़ावा :: बाँध / बढ़ावा :: मदद करने वालों के लिए मेमना। इनमें से कई C ++ 0x में जाएंगे और for_each और संबंधित कार्यों को अधिक उपयोगी बना देंगे।)
यह आपको for_each के शीर्ष पर एक एल्गोरिथ्म लिखने की अनुमति देता है जो किसी भी पुनरावृत्त के साथ काम करता है।
यह बेवकूफ टाइपिंग कीड़ों की संभावना को कम करता है।
यह भी एसटीएल-एल्गोरिदम, जैसे के आराम करने के लिए अपने मन को खोलता है 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 में भी होगा?
outer_class::inner_class::iterator
या वे टेम्पलेट तर्क हैं: typename std::vector<T>::iterator
... निर्माण के लिए स्वयं में कई लाइन निर्माण में चला सकते हैं
for_each
दूसरे उदाहरण में गलत है (होना चाहिएfor_each( bananas.begin(), bananas.end(),...
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 साल में आपको अजीब नहीं लगेगा।
for ( int v : int_vector ) {
(भले ही इसे आज BOOST_FOREACH के साथ अनुकरण किया जा सके)
std::for_each(container, [](int& i){ ... });
:। मेरा मतलब है कि कोई दो बार कंटेनर लिखने के लिए मजबूर क्यों है?
container.each { ... }
शुरू करने और अंत करने वाले का उल्लेख किए बिना कुछ लिखें । मुझे यह थोड़ा बेमानी लगता है कि मुझे हर समय अंतिम पुनरावृत्ति निर्दिष्ट करना है।
व्यक्तिगत रूप से, किसी भी समय मुझे उपयोग करने के लिए अपने रास्ते से बाहर जाने की आवश्यकता होगी 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)));
इसकी बहुत व्यक्तिपरक, कुछ कहेंगे कि 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;
}
तो यह आप पर निर्भर है कि आपके लिए क्या सही है।
एल्गोरिथ्म के कई कार्यों की तरह, एक प्रारंभिक प्रतिक्रिया यह सोचने के लिए है कि लूप की तुलना में फोरच का उपयोग करना अधिक अपठनीय है। यह कई लौ युद्धों का विषय रहा है।
एक बार जब आप मुहावरे के लिए अभ्यस्त हो जाते हैं तो आपको यह उपयोगी लग सकता है। एक स्पष्ट लाभ यह है कि यह कोडर को वास्तविक पुनरावृत्ति कार्यक्षमता से लूप की आंतरिक सामग्री को अलग करने के लिए मजबूर करता है। (ठीक है, मुझे लगता है कि यह एक फायदा है। अन्य का कहना है कि आप कोड को बिना वास्तविक लाभ के काट रहे हैं)।
एक अन्य लाभ यह है कि जब मैं फॉरचेक देखता हूं, तो मुझे पता है कि या तो प्रत्येक आइटम को संसाधित किया जाएगा या एक अपवाद फेंक दिया जाएगा।
एक के लिए पाश पाश समाप्त के लिए कई विकल्प देता है। आप लूप को अपना पूरा कोर्स चलाने दे सकते हैं, या आप लूप से स्पष्ट रूप से कूदने के लिए ब्रेक कीवर्ड का उपयोग कर सकते हैं , या पूरे फ़ंक्शन मिड-लूप से बाहर निकलने के लिए रिटर्न कीवर्ड का उपयोग कर सकते हैं । इसके विपरीत, फोरचेक इन विकल्पों की अनुमति नहीं देता है, और यह इसे अधिक पठनीय बनाता है। आप केवल फ़ंक्शन नाम पर नज़र डाल सकते हैं और आप पुनरावृति की पूर्ण प्रकृति को जानते हैं।
यहाँ लूप के लिए एक भ्रामक उदाहरण दिया गया है :
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;
}
}
}
std::for_each()
पुराने मानक (इस पोस्ट के समय) का उपयोग करते हैं, तो आपको एक नामित फ़ंक्टर का उपयोग करना होगा, जो आपके कहने के अनुसार पठनीयता को प्रोत्साहित करता है और समय से पहले लूप से बाहर निकलने पर रोक लगाता है। लेकिन तब समतुल्य for
लूप के पास एक फ़ंक्शन कॉल के अलावा कुछ भी नहीं है, और वह भी समय से पहले टूटने पर रोक लगाता है। लेकिन तुलना में मुझे लगता है कि आपको लगता है कि कह में एक उत्कृष्ट मुद्दा बनाया अन्य std::for_each()
लागू करता है संपूर्ण रेंज से गुजर रही।
आप ज्यादातर सही हैं: ज्यादातर समय, std::for_each
एक शुद्ध नुकसान है। मैं तुलना for_each
करने के लिए इतनी दूर जाऊँगा goto
। goto
सबसे बहुमुखी प्रवाह-नियंत्रण संभव प्रदान करता है - आप इसका उपयोग लगभग किसी अन्य नियंत्रण संरचना को लागू करने के लिए कर सकते हैं जिसकी आप कल्पना कर सकते हैं। हालाँकि, यह बहुत ही चंचलता का अर्थ है कि goto
अलगाव को देखते हुए आपको इस स्थिति में क्या करना है, इसके बारे में लगभग कुछ भी नहीं बताता है। परिणामस्वरूप, उनके goto
अंतिम दिमाग में लगभग कोई भी अंतिम उपाय के रूप में उपयोग नहीं करता है ।
मानक एल्गोरिदम के बीच, for_each
एक ही तरीका है - इसका उपयोग वस्तुतः किसी भी चीज़ को लागू करने के लिए किया जा सकता है, जिसका अर्थ है कि देखने for_each
से आपको इस स्थिति में इसका उपयोग करने के बारे में वस्तुतः कुछ भी नहीं बताता है। दुर्भाग्य से, लोगों का रुख for_each
इस बारे में है कि उनका रुख goto
1970 (या) में कहां था - कुछ लोगों ने इस तथ्य को पकड़ा था कि इसका उपयोग केवल अंतिम उपाय के रूप में किया जाना चाहिए, लेकिन कई लोग इसे अभी भी प्राथमिक एल्गोरिथ्म मानते हैं, और शायद ही कभी किसी अन्य का उपयोग करें। अधिकांश समय, यहां तक कि एक त्वरित नज़र से पता चलता है कि विकल्पों में से एक काफी बेहतर था।
उदाहरण के लिए, मुझे पूरा यकीन है कि मैंने कितनी बार ट्रैक खो दिया है कि मैंने लोगों को एक संग्रह की सामग्री का उपयोग करने के लिए कोड लिखने के लिए देखा है 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
।
boost::mem_fn(&XXX::print)
बजाय होना चाहिएXXX::print
std::cout
इसे काम करने के लिए इसके तर्क के रूप में बांधने की भी जरूरत है )।
अधिक पठनीय मधुमक्खी के लिए कार्यात्मक लिखने का लाभ, जब 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);
}
और जो मुझे लगता है कि बहुत अच्छा है, फॉर-लूप्स को एक पंक्ति के कार्यों के लिए सामान्यीकृत करें =)
आसान: for_each
तब उपयोगी होता है जब आपके पास पहले से ही हर ऐरर आइटम को संभालने का फंक्शन होता है, इसलिए आपको लैम्बडा नहीं लिखना होता है। निश्चित रूप से, यह
for_each(a.begin(), a.end(), a_item_handler);
से बेहतर है
for(auto& item: a) {
a_item_handler(a);
}
इसके अलावा, for
लूप शुरू से अंत तक पूरे कंटेनरों से अधिक पुनरावृत्त होता है, जबकि for_each
अधिक लचीला होता है।
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 के साथ जा सकता हूं (एक सहकर्मी मुझे इसमें मिला)
पठनीयता और प्रदर्शन के अलावा, एक पहलू जिसे आमतौर पर अनदेखा किया जाता है, वह है स्थिरता। पुनरावृत्तियों पर (या जबकि) लूप के लिए लागू करने के कई तरीके हैं:
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 सुविधाओं का उपयोग करके लागू करते हैं? व्यक्तिगत रूप से, मैं इस बारे में चिंता करना चाहता हूं कि लूप के लिए / यादृच्छिक तरीके से कैसे लागू किया जाए या कैसे पढ़ा जाए।
मैं नापसंद करता था 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
आदि।
for
लूप के लिए है कि प्रत्येक तत्व या हर तीसरे आदि for_each
को पुनरावृत्त कर सकते हैं केवल प्रत्येक तत्व को पुनरावृत्त करने के लिए है। यह इसके नाम से स्पष्ट है। इसलिए यह अधिक स्पष्ट है कि आप अपने कोड में क्या करना चाहते हैं।
++
। असामान्य हो सकता है, लेकिन ऐसा करने के लिए एक लूप है।
transform
किसी को भ्रमित न करने के लिए इसका उपयोग करना बेहतर होगा ।
यदि आप अक्सर एसटीएल से अन्य एल्गोरिदम का उपयोग करते हैं, तो इसके कई फायदे हैं for_each
:
लूप के लिए एक पारंपरिक के विपरीत, for_each
आपको कोड लिखने के लिए मजबूर करता है जो किसी भी इनपुट इटेटर के लिए काम करेगा। इस तरह से प्रतिबंधित होना वास्तव में एक अच्छी बात हो सकती है क्योंकि:
for_each
।for_each
कभी-कभी उपयोग करने से यह अधिक स्पष्ट होता है कि आप एक ही कार्य करने के लिए अधिक विशिष्ट एसटीएल फ़ंक्शन का उपयोग कर सकते हैं। (जेरी कॉफिन के उदाहरण के रूप में, यह जरूरी नहीं है कि for_each
यह सबसे अच्छा विकल्प है, लेकिन लूप के लिए एकमात्र विकल्प नहीं है।)
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); }
अधिकतर आपको पूरे संग्रह पर पुनरावृति करनी होगी । इसलिए मेरा सुझाव है कि आप अपना स्वयं का for_each () संस्करण लिखें, केवल 2 मापदंडों को लेते हुए। यह आपको टेरी महाफ़ी के उदाहरण को फिर से लिखने की अनुमति देगा :
for_each(container, [](int& i) {
i += 10;
});
मुझे लगता है कि यह वास्तव में लूप के लिए अधिक पठनीय है। हालाँकि, इसके लिए C ++ 0x कंपाइलर एक्सटेंशन की आवश्यकता होती है।
मुझे लगता है कि 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
}
मुझे लूप क्लीयर पढ़ने के लिए एक स्पष्ट लगता है और प्रारंभ और अंत पुनरावृत्तियों के लिए नामित चर का उपयोग करते हुए अन्वेषण लूप के लिए अव्यवस्था को कम करता है।
बेशक मामलों में भिन्नता है, यह वही है जो मुझे आमतौर पर सबसे अच्छा लगता है।
आपके पास पुनरावृत्ति एक फ़ंक्शन के लिए कॉल हो सकता है जो लूप के माध्यम से प्रत्येक पुनरावृत्ति पर किया जाता है।
यहां देखें: http://www.cplusplus.com/reference/algorithm/for_each/
for_each
वह क्या करता है, इस मामले में, वह अपने फायदे के बारे में सवाल का जवाब नहीं देता है।
के लिए पाश टूट सकता है; मैं हर्ब सटर के लिए तोता नहीं बनना चाहता, इसलिए यहां उनकी प्रस्तुति की कड़ी है: http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T टिप्पणियों को भी अवश्य पढ़ें :)
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);
std::for_each
जब उपयोग किया जाता हैboost.lambda
याboost.bind
अक्सर पठनीयता में सुधार कर सकते हैं