एल्गोरिथ्म समाधान:
std::generate(numbers.begin(), numbers.end(), rand);
रेंज आधारित लूप समाधान के लिए:
for (int& x : numbers) x = rand();
मैं std::generate
C ++ 11 में श्रेणी-आधारित फ़ोर -लूप से अधिक क्रिया का उपयोग क्यों करना चाहूंगा ?
एल्गोरिथ्म समाधान:
std::generate(numbers.begin(), numbers.end(), rand);
रेंज आधारित लूप समाधान के लिए:
for (int& x : numbers) x = rand();
मैं std::generate
C ++ 11 में श्रेणी-आधारित फ़ोर -लूप से अधिक क्रिया का उपयोग क्यों करना चाहूंगा ?
begin()
और end()
?
range
अब तक अपने टूलबॉक्स में एक समारोह है। (यानी for(auto& x : range(first, last))
)
boost::generate(numbers, rand); // ♪
जवाबों:
पहला संस्करण
std::generate(numbers.begin(), numbers.end(), rand);
हमें बताता है कि आप मूल्यों का एक क्रम उत्पन्न करना चाहते हैं।
दूसरे संस्करण में पाठक को यह पता लगाना होगा कि वह खुद बाहर है।
टाइपिंग पर बचत आमतौर पर सबॉप्टीमल है, क्योंकि यह अक्सर पढ़ने के समय में खो जाता है। अधिकांश कोड को टाइप किए जाने की तुलना में बहुत अधिक पढ़ा जाता है।
numbers
बिना किसी कारण के दो बार निर्दिष्ट करना होगा । इसलिए: boost::range::generate(numbers, rand);
। कोई कारण नहीं है कि आप अच्छी तरह से निर्मित पुस्तकालय में कम और अधिक सुपाठ्य दोनों कोड नहीं रख सकते।
std::generate(number.begin(), numbers.begin()+3, rand)
यह नहीं कर सकते हैं ? इसलिए मुझे लगता है कि number
दो बार निर्दिष्ट करना कभी-कभी उपयोगी हो सकता है।
std::generate()
, तो आप इसके बजाय std::generate(slice(number.begin(), 3), rand)
एक काल्पनिक सीमा स्लाइसिंग सिंटैक्स के साथ कर सकते हैं या बेहतर भी कर सकते हैं जैसे कि सीमा के हिस्से के लचीले विनिर्देश को अनुमति देते समय std::generate(number[0:3], rand)
दोहराव को हटा number
देता है। तीन तर्क से शुरू होने वाला उल्टा करना std::generate()
अधिक कठिन है।
लूप के लिए सीमा आधारित है या नहीं, इससे कोई फर्क नहीं पड़ता है, यह केवल कोष्ठक के अंदर कोड को सरल करता है। एल्गोरिदम स्पष्ट हैं कि वे इरादे दिखाते हैं ।
व्यक्तिगत रूप से, मेरा प्रारंभिक पढ़ना:
std::generate(numbers.begin(), numbers.end(), rand);
"हम एक सीमा में सब कुछ करने के लिए असाइन कर रहे हैं। सीमा है numbers
। असाइन किए गए मान यादृच्छिक हैं"।
मेरी प्रारंभिक रीडिंग:
for (int& x : numbers) x = rand();
"हम एक श्रेणी में सब कुछ के लिए कुछ कर रहे हैं। सीमा है numbers
। हम जो करते हैं वह एक यादृच्छिक मूल्य प्रदान करता है।"
वे बहुत समान हैं, लेकिन समान नहीं हैं। एक प्रशंसनीय कारण जो मैं पहली बार पढ़ने के लिए उकसाना चाहता हूं, वह यह है कि मुझे लगता है कि इस कोड के बारे में सबसे महत्वपूर्ण तथ्य यह है कि यह सीमा तक असाइन होता है। तो आपका "मैं क्यों चाहूंगा ..." है। मैं उपयोग करता हूं generate
क्योंकि C ++ का std::generate
अर्थ है "रेंज असाइनमेंट"। जैसा कि btw करता है std::copy
, दोनों के बीच का अंतर वह है जो आप से असाइन कर रहे हैं।
हालांकि, इस बारे में भ्रमित करने वाले कारक हैं। लूप्स के लिए रेंज-आधारित में व्यक्त करने का एक अधिक प्रत्यक्ष तरीका है कि रेंज है numbers
, इट्रेटर-आधारित एल्गोरिदम की तुलना में। यही कारण है कि लोग रेंज-आधारित एल्गोरिथ्म लाइब्रेरी पर काम करते हैं: संस्करण की boost::range::generate(numbers, rand);
तुलना में बेहतर दिखता है std::generate
।
जैसा कि उस के खिलाफ, int&
लूप के लिए आपके रेंज-आधारित में एक शिकन है। क्या होगा यदि रेंज का मान प्रकार नहीं है int
, तो हम यहां कुछ कष्टप्रद सूक्ष्म रूप से कर रहे हैं जो इस पर निर्भर करता है कि यह परिवर्तनीय है int&
, जबकि generate
कोड केवल rand
तत्व के लिए असाइन किए जाने से वापसी पर निर्भर करता है । भले ही मूल्य प्रकार है int
, मैं अभी भी यह सोचने के लिए रुक सकता हूं कि यह है या नहीं। इसलिए auto
, जो प्रकारों के बारे में सोचने से चूक जाता है जब तक कि मुझे नहीं दिखता कि क्या सौंपा गया है - auto &x
मैं कहता हूं "रेंज तत्व का संदर्भ लें, जो भी प्रकार हो सकता है"। C ++ 03 में वापस, एल्गोरिदम (क्योंकि वे फ़ंक्शन टेम्पलेट हैं) सटीक प्रकार छिपाने का तरीका था, अब वे एक तरीका है।
मुझे लगता है कि यह हमेशा ऐसा होता रहा है कि सरलतम एल्गोरिदम का समकक्ष छोरों पर केवल मामूली लाभ होता है। लूप्स के लिए रेंज-आधारित लूप में सुधार होता है (मुख्य रूप से बॉयलरप्लेट के अधिकांश को हटाकर, हालांकि उनसे थोड़ा अधिक है)। इसलिए मार्जिन तंग होता है और शायद आप कुछ विशिष्ट मामलों में अपना दिमाग बदलते हैं। लेकिन वहाँ अभी भी एक शैली अंतर है।
operator int&()
? :)
int&
साथ बदल जाता है SomeClass&
और अब आपको रूपांतरण ऑपरेटरों और एकल-पैरामीटर निर्माणकर्ताओं के बारे में चिंता करनी होगी जो चिह्नित नहीं हैं explicit
।
operator int&()
और operator int const &() const
, लेकिन फिर यह ओवरलोडिंग operator int() const
और काम कर सकता है operator=(int)
।
मेरी राय में, प्रभावी एसटीएल आइटम 43: "एल्गोरिथ्म को हाथ से लिखे गए छोरों को कॉल करना पसंद है।" अभी भी एक अच्छी सलाह है।
मैं आमतौर पर begin()
/ end()
नरक से छुटकारा पाने के लिए आवरण कार्य लिखता हूं । यदि आप ऐसा करते हैं, तो आपका उदाहरण इस तरह दिखाई देगा:
my_util::generate(numbers, rand);
मेरा मानना है कि यह मंशा को संप्रेषित करने और पठनीयता में लूप के आधार पर सीमा को पार करता है।
यह कहने के बाद, मुझे यह स्वीकार करना होगा कि C ++ 98 में कुछ STL एल्गोरिथ्म में असंबद्ध कोड की उपज होती है और "Prefer एल्गोरिथ्म कॉल टू हैंड-राइट्ड लूप्स" का पालन करना एक अच्छा विचार नहीं था। सौभाग्य से, लैम्ब्डा ने इसे बदल दिया है।
हर्ब सटर से निम्नलिखित उदाहरण पर विचार करें : लैंबडास, लैंबडस एवरीवेयर ।
कार्य: v में पहला तत्व खोजें जो है > x
और < y
।
लंबोदर के बिना:
auto i = find_if( v.begin(), v.end(),
bind( logical_and<bool>(),
bind(greater<int>(), _1, x),
bind(less<int>(), _1, y) ) );
लंबोदर के साथ
auto i=find_if( v.begin(), v.end(), [=](int i) { return i > x && i < y; } );
में मेरी राय, मैनुअल पाश, हालांकि शब्दाडंबर को कम कर सकते हैं, readabitly का अभाव है:
for (int& x : numbers) x = rand();
मैं इस लूप का उपयोग संख्याओं द्वारा परिभाषित 1 रेंज को इनिशियलाइज़ करने के लिए नहीं करूंगा , क्योंकि जब मैं इसे देखता हूं, तो मुझे लगता है कि यह संख्याओं की संख्या से अधिक है, लेकिन वास्तविकता में यह (संक्षेप में) नहीं है, अर्थात रेंज से पढ़ना , यह रेंज के लिए लिख रहा है।
जब आप उपयोग करते हैं तो आशय बहुत स्पष्ट होता है std::generate
।
1. इस संदर्भ में प्रारंभिक का मतलब कंटेनर के तत्वों को सार्थक मूल्य देना है ।
std::generate
, जिसे C ++ प्रोग्रामर माना जा सकता है (यदि वे परिचित नहीं हैं, तो वे इसे देखेंगे, वही परिणाम)।
&
पात्र को भूल जाते हैं ?) एल्गोरिदम का लाभ यह है कि वे इरादे दिखाते हैं, जबकि छोरों के साथ आपको यह अनुमान लगाना होगा। यदि लूप के कार्यान्वयन में एक बग है, तो यह स्पष्ट नहीं है कि यह बग है या यह जानबूझकर था।
std::generate
केवल देखने के मामले में , कोई कह सकता है कि इस फ़ंक्शन द्वारा कुछ उत्पन्न किया जा रहा है; क्या कुछ है कि समारोह में तीसरे तर्क से जवाब दिया है। मुझे लगता है कि यह बहुत बेहतर है।
कुछ चीजें हैं जो आप (बस) रेंज-आधारित छोरों के साथ नहीं कर सकते हैं जो एल्गोरिदम इनपुट के रूप में पुनरावृत्तियों को लेते हैं। उदाहरण के लिए std::generate
:
एक वितरण से चर के साथ limit
(अपवर्जित, limit
एक मान्य पुनरावृत्ति है numbers
) को दूसरे वितरण से चर के साथ और शेष भरें ।
std::generate(numbers.begin(), limit, rand1);
std::generate(limit, numbers.end(), rand2);
Iterator- आधारित एल्गोरिदम आपको उस सीमा पर बेहतर नियंत्रण देता है जिस पर आप काम कर रहे हैं।
के विशेष मामले के लिए std::generate
, मैं पठनीयता / इरादे के मुद्दे पर पिछले उत्तरों से सहमत हूं। std :: उत्पन्न मुझे एक अधिक स्पष्ट संस्करण लगता है। लेकिन मैं मानता हूं कि यह एक तरह से स्वाद का मामला है।
उस ने कहा, मेरे पास एक और कारण है कि एसटीडी को न फेंकना :: एल्गोरिथ्म - कुछ एल्गोरिदम हैं जो कुछ डेटा प्रकारों के लिए विशिष्ट हैं।
सबसे सरल उदाहरण होगा std::fill
। सामान्य संस्करण को प्रदान की गई सीमा से अधिक लूप के रूप में लागू किया गया है, और इस संस्करण का उपयोग टेम्पलेट को तत्काल करते समय किया जाएगा। लेकिन हमेशा नहीं। उदाहरण के लिए, यदि आप इसे एक सीमा प्रदान करते हैं, जो एक है std::vector<int>
- अक्सर यह वास्तव memset
में हुड के तहत कॉल करेगा , एक बहुत तेज और बेहतर कोड उपज।
इसलिए मैं यहां एक दक्षता कार्ड खेलने की कोशिश कर रहा हूं।
आपका हाथ से लिखा लूप एक std :: एल्गोरिथ्म संस्करण जितना तेज़ हो सकता है, लेकिन यह शायद ही तेज़ हो। और इससे भी अधिक, std :: एल्गोरिथ्म विशेष कंटेनरों और प्रकारों के लिए विशेष हो सकता है और यह साफ एसटीएल इंटरफेस के तहत किया जाता है।
मेरा जवाब शायद और नहीं होगा। यदि हम C ++ 11 के बारे में बात कर रहे हैं, तो हो सकता है (अधिक पसंद नहीं)। उदाहरण के लिएstd::for_each
लैम्ब्डा के साथ भी उपयोग करने के लिए वास्तव में कष्टप्रद है:
std::for_each(c.begin(), c.end(), [&](ExactTypeOfContainedValue& x)
{
// do stuff with x
});
लेकिन इसके लिए रेंज-आधारित का उपयोग करना बेहतर है:
for (auto& x : c)
{
// do stuff with x
}
दूसरी ओर, यदि हम C ++ 1y के बारे में बात कर रहे हैं, तो मैं तर्क दूंगा कि नहीं, एल्गोरिदम को सीमा के आधार पर नहीं माना जाएगा। C ++ मानक समिति में एक अध्ययन समूह है जो C ++ में सीमाएँ जोड़ने के प्रस्ताव पर काम कर रहा है, और वहाँ भी पॉलीमोर्फिक लैम्ब्डा पर काम किया जा रहा है। रेंजर्स जोड़ीदार और पॉलिमॉर्फिक लैम्ब्डा की जोड़ी का उपयोग करने की आवश्यकता को हटा देंगे जिससे आप लैम्बडा के सटीक तर्क प्रकार को निर्दिष्ट नहीं कर पाएंगे। इसका मतलब यह है कि std::for_each
इस तरह इस्तेमाल किया जा सकता है (इसे एक कठिन तथ्य के रूप में न लें, यह सिर्फ आज के सपने दिखते हैं):
std::for_each(c.range(), [](x)
{
// do stuff with x
});
[]
लैम्बडा के साथ लिखकर आप शून्य-कैप्चर निर्दिष्ट करते हैं? यह है कि सिर्फ एक लूप बॉडी लिखने के साथ तुलना करके, आपने चर लुकअप संदर्भ से कोड का एक हिस्सा अलग कर दिया है जो कि यह शाब्दिक रूप से प्रकट होता है। अलगाव आमतौर पर पाठक के लिए सहायक होता है, पढ़ने के बारे में सोचने के लिए कम।
for_each
एक लंबोदर के साथ उपयोग किए जाने पर भी अभी भी व्यर्थ है। foreach + कैप्चरिंग लैम्ब्डा वर्तमान में लूप के लिए रेंज-आधारित लिखने का एक क्रियात्मक तरीका है, और यह थोड़ा कम वर्बोज़ तरीका बन जाता है, लेकिन लूप की तुलना में अभी भी अधिक है। ऐसा नहीं है कि मुझे लगता है कि आपको बचाव करना चाहिए for_each
, निश्चित रूप से, लेकिन आपके उत्तर को देखने से पहले मैं सोच रहा था कि यदि प्रश्नकर्ता एल्गोरिदम को मारना चाहता था, तो वह for_each
सभी संभावित लक्ष्यों में से सबसे नरम के रूप में चुना जा सकता था ;-)
for_each
, लेकिन इसके लिए रेंज-आधारित पर एक छोटा सा फायदा है - आप इसे समानांतर रूप से आसानी से समानांतर बना सकते हैं इसे समानांतर में बदल सकते हैं parallel_for_each
(यदि आप पीपीएल का उपयोग करते हैं, और ऐसा करने के लिए थ्रेड-सुरक्षित मानते हैं) । :-D
std::algorithm
एस 'कार्यान्वयन उनके इंटरफ़ेस के पीछे छिपा हुआ है और मनमाने ढंग से जटिल हो सकता है (या मनमाने ढंग से अनुकूलित)।
एक बात पर ध्यान दिया जाना चाहिए कि एक एल्गोरिथ्म व्यक्त करता है कि क्या किया जाता है, कैसे नहीं।
रेंज-आधारित लूप में जिस तरह से चीजें की जाती हैं, उनमें शामिल हैं: पहले से शुरू करें, लागू करें और अंत तक अगले तत्व पर जाएं। यहां तक कि एक साधारण एल्गोरिथ्म चीजों को अलग तरह से कर सकता है (विशिष्ट कंटेनरों के लिए कम से कम कुछ अधिभार, भयानक वेक्टर के बारे में भी नहीं सोच रहा है), और कम से कम जिस तरह से किया जाता है वह लेखक का व्यवसाय नहीं है।
मेरे लिए यह बहुत अंतर है, जितना हो सके उतना एनकैप्सुलेट करें और जब आप यह कर सकें तो वाक्य को सही ठहराते हैं, एल्गोरिदम का उपयोग करें।
रेंज-लूप के लिए बस इतना ही है। जब तक पाठ्यक्रम में बदलाव नहीं किया जाता है।
एल्गोरिथम एक फ़ंक्शन है। एक फ़ंक्शन जो अपने मापदंडों पर कुछ आवश्यकताओं को डालता है। आवश्यकताओं को एक मानक में उदाहरण के कार्यान्वयन के लिए अनुमति दी जाती है जो सभी उपलब्ध निष्पादन थ्रेड्स का लाभ उठाती है और आपको स्वचालित रूप से गति प्रदान करेगी।