2010-01-12 23 views
21

मैं सिर्फ std :: for_each के लिए कोड पढ़ें:std :: for_each (से, से, फ़ंक्शन) फ़ंक्शन फ़ंक्शन क्यों करता है?

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

और इनपुट समारोह वापस जाने के लिए इस टेम्पलेट समारोह के लिए किसी भी अच्छे कारणों को देखने नहीं कर सका। क्या किसी के पास कोई उदाहरण है कि यह उपयोगी होगा?

उत्तर

40

यह आपको अपने फ़ंक्शन में राज्य प्राप्त करने की अनुमति देता है और फिर इसे अपने कॉलिंग कोड पर वापस कर देता है। उदाहरण के लिए, आपके फ़ंक्शन (एक मज़ेदार वर्ग के रूप में) को सदस्य की संख्या को गिनने के लिए कितनी बार बुलाया जा सकता था। http://xenon.arcticus.com/c-morsels-std-for-each-functors-member-variables

+0

संभोग, मैंने इस बारे में क्यों नहीं सोचा? Thx :) – larsmoa

+0

मुझे यहां एक समय पर बताया गया था (चार्ल्स बेली द्वारा, मुझे लगता है), कि इस व्यवहार की गारंटी नहीं है। मैंने उसे विश्वास नहीं किया; मैंने मानक को सोचा, जबकि अस्पष्ट, ज्यादातर उस संदर्भ में समझ में आया। (हम एक समारोह क्यों प्राप्त करेंगे?) लेकिन सिर्फ एक चेतावनी, शायद यह सिर्फ "मानक" कंपाइलर व्यवहार है और मानक व्यवहार नहीं है। – GManNickG

+4

@GMan: फ़ंक्शन को वापस करने के लिए मानक (§25.1.1/2) आवश्यक है। –

1

यदि आप उदाहरण के लिए कॉल के बीच फ़ंक्चर स्थिति को सहेजना चाहते हैं (और बाद में उपयोग) करना चाहते हैं, तो संग्रह में तत्वों की संख्या की गणना करें या कुछ आंतरिक चर सेट करके किसी तत्व को संसाधित करने में विफलता को इंगित करें।

-3

कोई विशेष कारण मुझे लगता है कि:

यहाँ कुछ उदाहरण के साथ एक पृष्ठ है। यद्यपि आप क्या कर सकते हैं हालांकि किसी अन्य फ़ोरैच कॉल में लौटाए गए फ़ंक्शन का उपयोग कर रहा है, इस प्रकार फ़ंक्शन नाम को दो बार लिखना और संभवत: वहां कोई गलती करना है।

1

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

नोट ... यही कारण है कि आपको हमेशा प्रति-निर्माण, और राज्य के साथ कार्य वस्तुओं के लिए असाइनमेंट लागू करना होगा।

+3

आपको लागू करने की आवश्यकता नहीं है एक मज़ेदार के लिए कॉपी कन्स्ट्रक्टर या असाइनमेंट ऑपरेटर, किसी भी अन्य वर्ग के लिए आप जितना अधिक करते हैं - डिफ़ॉल्ट रूप से आवश्यक होने पर डिफ़ॉल्ट रूप से किया जाएगा। –

2

फ़ंक्शन लौटने से मूल रूप से std::for_eachstd::accumulate की औसत अनुकरण में बनाता है। यह आपको फ़ंक्शन/फ़ैक्टर में कुछ जमा करने देता है, और उसके बाद उस संचित मूल्य को पुनर्प्राप्त करने पर पुनर्प्राप्त करता है। लगभग किसी भी समय आपको लगता है कि यह एक उपयोगी काम हो सकता है, आपको शायद std::accumulate का उपयोग करने पर विचार करना चाहिए।

+0

वास्तव में std :: संचय बहुत बेकार है। – CashCow

7

हो सकता है कि एलेक्स Stepanov कार्यात्मक प्रोग्रामिंग प्रतिमान था, लेकिन आपको लगता है कि std::accumulate और std::for_each दोनों, मूल्य से चारों ओर उनके ऑपरेंड (समारोह और संचित मूल्य) पारित नहीं बल्कि संदर्भ द्वारा की तुलना में मिल जाएगा। इस प्रकार:

class MyFunctor 
{ 
    Y val; 
    public: 
    MyFunctor() : val() {} 

    void operator()(X const& x) 
    { 
     // do something to modify val based on x 
    } 

    Y getValue() const { return val; } 
}; 

अब अगर आप का प्रयास करें:

MyFunctor f; 
for_each(coll.begin(), coll.end(), f); 
Y y = f.getValue(); 

यह काम नहीं करेगा गया है क्योंकि for_eachf की प्रतियां के साथ काम कर दिया। बेशक आप आंतरिक रूप से shared_ptr<Y> का उदाहरण प्राप्त कर सकते हैं जो एक ही उदाहरण को इंगित करेगा। आप MyFunctor के अंदर वैल्यू भी एक संदर्भ बना सकते हैं, इसे लूप के बाहर बना सकते हैं और इसे MyFunctor में पास कर सकते हैं।

हालांकि भाषा आप की सुविधा देता है बस करो:

Y y = for_each(coll.begin(), coll.end(), MyFunctor()).getValue(); 

अच्छा और सुविधाजनक है, सभी एक पंक्ति में।

साथ std::accumulate इस तरह से किया जाना होता भी ऐसा ही करने के लिए:

class MyFunctor2 
{ 
public: 
     Y operator()(Y y, X const& x) const 
     { 
     // create a new Y based on the old one and x 
     ... 
     } 
}; 

Y y = std::accumulate(coll.begin(), coll.end(), Y(), MyFunctor2()); 

आप (या सी ++ 11 एक लैम्ब्डा में) एक समारोह का उपयोग एक functor के बजाय सकता है। ध्यान दें कि यहां मज़ेदार के पास कोई राज्य नहीं है, और आप अपने आरंभिक ऑब्जेक्ट को पैरामीटर के रूप में पास करते हैं, जो अस्थायी हो सकता है।

अब हम जानते हैं कि वाई कॉपी करने योग्य है। std::accumulate वाई पर by value का उपयोग करता है, न कि जगह में संशोधित। संयोग से जब यथा-स्थान वास्तव में, संशोधित करने, और अधिक कुशल है वहाँ एक नया एल्गोरिथ्म लिखे एक समाधान नहीं है (उदाहरण के लिए accumulate2 + = या संदर्भ संशोधन का उपयोग करता है) के एक समारोह हस्ताक्षर का उपयोग करके:

Y * func(Y* py, X const &); // function modifies *py in-place then returns py 
तो

बुला:

Y y; 
std::accumulate(coll.begin(), coll.end(), &y, func); 

हम वापसी मान & y हो जाएगा "पता"। यदि हम वाई के किसी सदस्य को एक स्थान पर एक्सेस करना चाहते हैं तो हम इसका उपयोग कर सकते हैं उदा।

Y y; 
Z z = std::accumulate(coll.begin(), coll.end(), &y, func)->getZ(); 

संयोग से, accumulate में for_each में कॉपी और नकल करने के लिए एक महत्वपूर्ण अंतर यह जटिलता/प्रतियां यह कर देगा की संख्या है। for_each के साथ आपके मज़ेदार से बने 2 प्रतियां होंगी: एक फ़ंक्शन में पैरामीटर और रिटर्न में एक के रूप में। मैं "सबसे अधिक" कहता हूं क्योंकि रिटर्न वैल्यू ऑप्टिमाइज़ेशन इन प्रतियों में से दूसरी को कम कर सकता है। accumulate के साथ यह संग्रह में हर तत्व के साथ प्रतिलिपि बनाता है, i.e O(N) निरंतर समय के बजाय। इसलिए यदि प्रतिलिपि महंगी महंगी है, तो मज़ेदार में दोहरी प्रति बड़ी संग्रहों पर थोड़ी-थोड़ी बार फिर से खर्च करने का एक बड़ा खर्च नहीं होगा, जबकि जमा करने के लिए यह होगा (और सुझाव पॉइंटर हैक होगा)।

संबंधित मुद्दे