C ++ 11 में लंबोदर अभिव्यक्ति क्या है? मैं कब एक का उपयोग करेगा? वे किस समस्या का समाधान करते हैं जो उनके परिचय से पहले संभव नहीं था?
कुछ उदाहरण, और उपयोग के मामले उपयोगी होंगे।
C ++ 11 में लंबोदर अभिव्यक्ति क्या है? मैं कब एक का उपयोग करेगा? वे किस समस्या का समाधान करते हैं जो उनके परिचय से पहले संभव नहीं था?
कुछ उदाहरण, और उपयोग के मामले उपयोगी होंगे।
जवाबों:
C ++ में उपयोगी जेनेरिक फ़ंक्शंस जैसे std::for_each
और शामिल हैं std::transform
, जो बहुत काम आ सकते हैं। दुर्भाग्य से वे भी उपयोग करने के लिए काफी बोझिल हो सकते हैं, खासकर यदि आप जिस फंक्टर को लागू करना चाहते हैं वह विशेष फ़ंक्शन के लिए अद्वितीय है।
#include <algorithm>
#include <vector>
namespace {
struct f {
void operator()(int) {
// do something
}
};
}
void func(std::vector<int>& v) {
f f;
std::for_each(v.begin(), v.end(), f);
}
यदि आप केवल f
एक बार और उस विशिष्ट स्थान पर उपयोग करते हैं, तो ऐसा लगता है कि यह कुछ तुच्छ और एक बंद करने के लिए पूरी कक्षा को लिख रहा है।
C ++ 03 में, आपको निम्नलिखित कुछ ऐसा लिखने का प्रलोभन दिया जा सकता है, जिसमें फ़नकार को स्थानीय रखा जा सकता है:
void func2(std::vector<int>& v) {
struct {
void operator()(int) {
// do something
}
} f;
std::for_each(v.begin(), v.end(), f);
}
हालाँकि, यह अनुमति नहीं है, C ++ 03 में f
एक टेम्पलेट फ़ंक्शन को पारित नहीं किया जा सकता है ।
सी ++ 11 लैम्बदास का परिचय देता है जो आपको इनलाइन लिखने की अनुमति देता है, अनाम फ़ंक्टर को बदलने के लिए struct f
। छोटे सरल उदाहरणों के लिए यह पढ़ने के लिए क्लीनर हो सकता है (यह सब कुछ एक ही स्थान पर रखता है) और बनाए रखने के लिए संभवतः सरल, उदाहरण के लिए सरलतम तरीके से:
void func3(std::vector<int>& v) {
std::for_each(v.begin(), v.end(), [](int) { /* do something here*/ });
}
अनाम फंक्शनलर्स के लिए लैम्ब्डा फ़ंक्शन सिंटैक्टिक शुगर हैं।
साधारण मामलों में मेमने का रिटर्न प्रकार आपके लिए घटाया जाता है, जैसे:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) { return d < 0.00001 ? 0 : d; }
);
}
हालाँकि जब आप अधिक जटिल लैम्ब्डा लिखना शुरू करते हैं, तो आप जल्दी से उन मामलों का सामना करेंगे जहां कंपाइलर द्वारा रिटर्न प्रकार काटा नहीं जा सकता है, जैसे:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
इसे हल करने के लिए आपको स्पष्ट रूप से एक लंबो फ़ंक्शन के लिए वापसी प्रकार निर्दिष्ट करने की अनुमति है, का उपयोग करते हुए -> T
:
void func4(std::vector<double>& v) {
std::transform(v.begin(), v.end(), v.begin(),
[](double d) -> double {
if (d < 0.0001) {
return 0;
} else {
return d;
}
});
}
अब तक हमने इसके अलावा लैम्ब्डा में जो कुछ भी किया गया था, उसके अलावा किसी भी चीज़ का उपयोग नहीं किया है, लेकिन हम लैम्ब्डा के भीतर अन्य वेरिएबल्स का भी उपयोग कर सकते हैं। यदि आप अन्य चर का उपयोग करना चाहते हैं, तो आप कैप्चर क्लॉज़ ( []
अभिव्यक्ति का) का उपयोग कर सकते हैं , जो अब तक इन उदाहरणों में अप्रयुक्त रहा है, जैसे:
void func5(std::vector<double>& v, const double& epsilon) {
std::transform(v.begin(), v.end(), v.begin(),
[epsilon](double d) -> double {
if (d < epsilon) {
return 0;
} else {
return d;
}
});
}
आप दोनों संदर्भ और मूल्य है, जो आप का उपयोग कर निर्दिष्ट कर सकते हैं द्वारा कब्जा कर सकते हैं &
और =
क्रमश:
[&epsilon]
संदर्भ द्वारा कैप्चर करें[&]
संदर्भ द्वारा लंबोदर में प्रयुक्त सभी चर को पकड़ता है[=]
मूल्य द्वारा लंबोदर में उपयोग किए गए सभी चर को कैप्चर करता है[&, epsilon]
वैरिएबल को [&] के साथ कैप्चर करता है, लेकिन वैल्यू द्वारा एप्सिलॉन[=, &epsilon]
[=] जैसे चर को कैप्चर करता है, लेकिन संदर्भ द्वारा एप्सिलॉनउत्पन्न डिफ़ॉल्ट रूप से होता operator()
है const
, इस निहितार्थ के साथ कि const
जब आप उन्हें डिफ़ॉल्ट रूप से एक्सेस करते हैं तो कैप्चर होगा । इसका प्रभाव यह होता है कि एक ही इनपुट वाली प्रत्येक कॉल एक ही परिणाम उत्पन्न करती है, हालाँकि आप लैम्बडा कोmutable
यह अनुरोध करने के लिए चिह्नित कर सकते हैं कि जो operator()
उत्पादन किया गया है वह नहीं है const
।
const
हमेशा होता है ...
()
- यह एक शून्य-तर्क वाले लंबो के रूप में पारित किया जाता है, लेकिन क्योंकि () const
यह लंबोदा से मेल नहीं खाता है, यह एक प्रकार के रूपांतरण की तलाश करता है जो इसे अनुमति देता है, जिसमें निहित-कास्ट शामिल है -टो-फ़ंक्शन-पॉइंटर, और उसके बाद कॉल करता है! ख़ुशामदी!
std::function<double(int, bool)> f = [](int a, bool b) -> double { ... };
लेकिन आमतौर पर, हम कंपाइलर को टाइप auto f = [](int a, bool b) -> double { ... };
करने के लिए #include <functional>
return d < 0.00001 ? 0 : d;
दोहरी वापसी की गारंटी क्यों दी जाती है, जब ऑपरेंड में से एक पूर्णांक स्थिरांक होता है (यह एक अंतर्निहित पदोन्नति नियम के कारण होता है ?: ऑपरेटर जहां 2 और 3 ऑपरेंड सामान्य आर्मीमेट्रिक के माध्यम से एक दूसरे के खिलाफ संतुलित होते हैं? कोई फर्क नहीं पड़ता कि कौन उठाता है)। बदलने के लिए 0.0 : d
शायद उदाहरण को समझना आसान हो जाएगा।
एक लंबो फंक्शन का C ++ कॉन्सेप्ट लैम्बडा कैलकुलस और फंक्शनल प्रोग्रामिंग में उत्पन्न होता है। एक लैम्ब्डा एक अनाम फ़ंक्शन है जो कोड के छोटे स्निपेट्स के लिए उपयोगी (वास्तविक प्रोग्रामिंग में, सिद्धांत नहीं) है जो पुन: उपयोग करना असंभव है और नामकरण के लायक नहीं हैं।
C ++ में एक लैम्बडा फ़ंक्शन को इस तरह परिभाषित किया गया है
[]() { } // barebone lambda
या इसकी महिमा में
[]() mutable -> T { } // T is the return type, still lacking throw()
[]
कैप्चर सूची, ()
तर्क सूची और {}
फ़ंक्शन बॉडी है।
कैप्चर सूची यह निर्धारित करती है कि लैम्बडा के बाहर से फ़ंक्शन बॉडी के अंदर और कैसे उपलब्ध होना चाहिए। यह या तो हो सकता है:
आप उपरोक्त में से कोई भी अल्पविराम से अलग की गई सूची में मिला सकते हैं [x, &y]
।
तर्क सूची किसी अन्य C ++ फ़ंक्शन के समान है।
उस कोड को निष्पादित किया जाएगा जब लैम्बडा वास्तव में कहा जाता है।
यदि एक लंबोदर के पास केवल एक रिटर्न स्टेटमेंट है, तो रिटर्न प्रकार छोड़ा जा सकता है और इसका निहित प्रकार है decltype(return_statement)
।
यदि एक लैम्ब्डा को म्यूटेबल (जैसे []() mutable { }
) चिह्नित किया जाता है, तो इसे उन मानों को म्यूट करने की अनुमति दी जाती है जिन्हें मूल्य द्वारा कब्जा कर लिया गया है।
आईएसओ मानक द्वारा परिभाषित पुस्तकालय को लंबोदर से भारी लाभ होता है और उपयोगिता को कई बार बढ़ाता है क्योंकि अब उपयोगकर्ताओं को अपने कोड को कुछ सुलभ दायरे में छोटे कार्यात्मक के साथ अव्यवस्थित नहीं करना पड़ता है।
C ++ में 14 लंबोदर को विभिन्न प्रस्तावों द्वारा बढ़ाया गया है।
कैप्चर सूची के एक तत्व के साथ अब आरंभ किया जा सकता है =
। यह चर का नाम बदलने और स्थानांतरित करके कब्जा करने की अनुमति देता है। मानक से लिया गया एक उदाहरण:
int x = 4;
auto y = [&r = x, x = x+1]()->int {
r += 2;
return x+2;
}(); // Updates ::x to 6, and initializes y to 7.
और विकिपीडिया से लिया गया, जिसमें दिखाया गया है कि कैसे कब्जा करना है std::move
:
auto ptr = std::make_unique<int>(10); // See below for std::make_unique
auto lambda = [ptr = std::move(ptr)] {return *ptr;};
लैम्ब्डा अब जेनेरिक हो सकता है ( यदि यहां
एक प्रकार का टेम्प्लेट तर्क कहीं आसपास के दायरे में होता है) के auto
बराबर होगा :T
T
auto lambda = [](auto x, auto y) {return x + y;};
C ++ 14 प्रत्येक फ़ंक्शन के लिए कम किए गए रिटर्न प्रकारों की अनुमति देता है और इसे फ़ॉर्म के कार्यों तक सीमित नहीं करता है return expression;
। यह भी लंबोदर तक विस्तारित है।
r = &x; r += 2;
लेकिन यह 4 के मूल मान से होता है
लैंबडा एक्सप्रेशंस का उपयोग आमतौर पर एल्गोरिदम को एनकैप्सुलेट करने के लिए किया जाता है ताकि उन्हें दूसरे फंक्शन में पास किया जा सके। हालांकि, परिभाषा के आधार पर एक लंबोदर को निष्पादित करना संभव है :
[&](){ ...your code... }(); // immediately executed lambda expression
कार्यात्मक रूप से समतुल्य है
{ ...your code... } // simple code block
यह लंबोदर अभिव्यक्तियों को जटिल कार्यों को फिर से तैयार करने के लिए एक शक्तिशाली उपकरण बनाता है । जैसा कि ऊपर दिखाया गया है, आप लैम्बडा फ़ंक्शन में एक कोड अनुभाग लपेटकर शुरू करते हैं। स्पष्ट पैरामीटर की प्रक्रिया को प्रत्येक चरण के बाद मध्यवर्ती परीक्षण के साथ धीरे-धीरे किया जा सकता है। एक बार जब आपके पास कोड-ब्लॉक पूरी तरह से मानकीकृत हो जाता है (जैसा कि हटाने के द्वारा प्रदर्शित किया जाता है &
), आप कोड को किसी बाहरी स्थान पर ले जा सकते हैं और इसे एक सामान्य फ़ंक्शन बना सकते हैं।
इसी प्रकार, आप एल्गोरिथम के परिणाम के आधार पर चर को इनिशियलाइज़ करने के लिए लैम्ब्डा एक्सप्रेशन का उपयोग कर सकते हैं ...
int a = []( int b ){ int r=1; while (b>0) r*=b--; return r; }(5); // 5!
अपने प्रोग्राम लॉजिक को विभाजित करने के एक तरीके के रूप में , आपको एक लंबोदर एक्सप्रेशन के लिए एक तर्क के रूप में एक लैंबडा एक्सप्रेशन पास करना भी उपयोगी हो सकता है ...
[&]( std::function<void()> algorithm ) // wrapper section
{
...your wrapper code...
algorithm();
...your wrapper code...
}
([&]() // algorithm section
{
...your algorithm code...
});
लैम्ब्डा एक्सप्रेशन से आपको नेस्टेड फंक्शंस बनाने की सुविधा मिलती है , जो डुप्लिकेट लॉजिक से बचने का एक सुविधाजनक तरीका हो सकता है। एक अन्य फ़ंक्शन के पैरामीटर के रूप में एक गैर-तुच्छ फ़ंक्शन को पारित करते समय नामांकित लैम्ब्डा का उपयोग करना भी आंखों पर थोड़ा आसान हो जाता है (अनाम इनलाइन लैम्ब्डा की तुलना में)। नोट: कर्ली ब्रेस को बंद करने के बाद अर्धविराम को न भूलें।
auto algorithm = [&]( double x, double m, double b ) -> double
{
return m*x+b;
};
int a=algorithm(1,2,3), b=algorithm(4,5,6);
यदि बाद के प्रोफाइलिंग से फ़ंक्शन ऑब्जेक्ट के लिए महत्वपूर्ण इनिशियलाइज़ेशन ओवरहेड का पता चलता है, तो आप इसे एक सामान्य फ़ंक्शन के रूप में फिर से लिखना चुन सकते हैं।
if
बयानों के लिए एक विरोधाभास के रूप में काम करता है : if ([i]{ for (char j : i) if (!isspace(j)) return false ; return true ; }()) // i is all whitespace
मान i
लेना एक हैstd::string
[](){}();
:।
(lambda: None)()
सिंटैक्स इतना अधिक सुपाठ्य है।
main() {{{{((([](){{}}())));}}}}
जवाब
प्रश्न: C ++ 11 में लंबोदर अभिव्यक्ति क्या है?
ए: हुड के तहत, यह ओवरलोडिंग ऑपरेटर () कॉन्स्ट के साथ एक ऑटोजेनरेटेड क्लास का ऑब्जेक्ट है । ऐसी वस्तु को क्लोजर कहा जाता है और संकलक द्वारा बनाया जाता है। यह 'क्लोजर' कॉन्सेप्ट C ++ 11 से बाइंड कॉन्सेप्ट के पास है। लेकिन लैम्ब्डा आमतौर पर बेहतर कोड उत्पन्न करते हैं। और बंद के माध्यम से कॉल पूर्ण इनलाइनिंग की अनुमति देता है।
प्रश्न: मैं कब एक का उपयोग करेगा?
A: "सरल और छोटे तर्क" को परिभाषित करने के लिए और पिछले प्रश्न से कंपाइलर प्रदर्शन करने के लिए कहें। आप कुछ संकलक देते हैं जो आप ऑपरेटर () के अंदर होना चाहते हैं। अन्य सभी सामान संकलक आपको उत्पन्न करेगा।
प्रश्न: वे किस वर्ग की समस्या को हल करते हैं जो उनके परिचय से पहले संभव नहीं था?
ए: यह कुछ प्रकार की सिंटैक्स चीनी है जैसे कि कस्टम ऐड, सबटैक्ट ऑपरेशंस के लिए फ़ंक्शंस के बजाय ऑपरेटर ओवरलोडिंग करते हैं ... लेकिन यह कुछ वर्गों के लिए वास्तविक तर्क के 1-3 लाइनों को लपेटने के लिए अनावश्यक कोड की अधिक लाइनें बचाते हैं, और आदि! कुछ इंजीनियरों को लगता है कि यदि लाइनों की संख्या छोटी है तो इसमें त्रुटियां होने की संभावना कम है (मुझे भी ऐसा लगता है)
उपयोग का उदाहरण
auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);
लैम्ब्डा के बारे में एक्स्ट्रा, सवाल द्वारा कवर नहीं किया गया। यदि आप रुचि नहीं रखते हैं तो इस अनुभाग को अनदेखा करें
1. कैद मान। जिसे आप कैद कर सकते हैं
1.1। आप लैम्बदास में स्थिर भंडारण अवधि के साथ एक चर का संदर्भ ले सकते हैं। वे सभी पकड़ लिए गए हैं।
1.2। आप "मूल्य द्वारा" कैप्चर मान के लिए लैम्ब्डा का उपयोग कर सकते हैं। ऐसे मामले में कैप्चर किए गए var को फ़ंक्शन ऑब्जेक्ट (क्लोजर) में कॉपी किया जाएगा।
[captureVar1,captureVar2](int arg1){}
1.3। आप संदर्भ पर कब्जा कर सकते हैं। & - इस संदर्भ में संदर्भ का मतलब है, न कि संकेत।
[&captureVar1,&captureVar2](int arg1){}
1.4। यह सभी गैर-स्थैतिक वर्जन को मान या संदर्भ द्वारा कैप्चर करने के लिए मौजूद है
[=](int arg1){} // capture all not-static vars by value
[&](int arg1){} // capture all not-static vars by reference
1.5। यह सभी गैर-स्थैतिक वर्जन को मान द्वारा, या संदर्भ द्वारा और स्मथ को निर्दिष्ट करने के लिए मौजूद है। अधिक। उदाहरण: मूल्य से सभी नहीं-स्थैतिक संस्करण कैप्चर करें, लेकिन संदर्भ कैप्चर Param2 द्वारा
[=,&Param2](int arg1){}
संदर्भ द्वारा सभी नहीं-स्थैतिक संस्करण कैप्चर करें, लेकिन मान कैप्चर करें Param2
[&,Param2](int arg1){}
2. वापसी प्रकार की कटौती
2.1। लैम्ब्डा एक अभिव्यक्ति है, तो लैम्ब्डा रिटर्न प्रकार काटा जा सकता है। या आप इसे स्पष्ट रूप से निर्दिष्ट कर सकते हैं।
[=](int arg1)->trailing_return_type{return trailing_return_type();}
यदि लैम्ब्डा में अधिक है तो एक एक्सप्रेशन, फिर रिटर्न टाइप को ट्रेलिंग रिटर्न टाइप के माध्यम से निर्दिष्ट किया जाना चाहिए। साथ ही, ऑटो फ़ंक्शन और सदस्य-फ़ंक्शन पर समान सिंटैक्स लागू किया जा सकता है
3. कैप्चर किया गया मान। जिसे आप कैद नहीं कर सकते
3.1। आप केवल स्थानीय वेरिएंट को कैप्चर कर सकते हैं, ऑब्जेक्ट के सदस्य चर को नहीं।
4. Сonversions
४.१ !! लैम्ब्डा एक फंक्शन पॉइंटर नहीं है और यह एक गुमनाम फंक्शन नहीं है, लेकिन कैप्चर-कम लैम्ब्डा को फंक्शन पॉइंटर में बदला जा सकता है।
ps
लैम्ब्डा व्याकरण के बारे में अधिक जानकारी प्रोग्रामिंग लैंग्वेज C ++ # 337, 2012-01-16, 5.1.2 के लिए वर्किंग ड्राफ्ट में मिल सकती है। लैम्ब्डा एक्सप्रेशंस, पृष्ठ 8
C ++ 14 में "इनइट कैप्चर" के रूप में नामित अतिरिक्त सुविधा को जोड़ा गया है। यह क्लोजर डेटा सदस्यों की मनमानी घोषणा करने की अनुमति देता है:
auto toFloat = [](int value) { return float(value);};
auto interpolate = [min = toFloat(0), max = toFloat(255)](int value)->float { return (value - min) / (max - min);};
[&,=Param2](int arg1){}
मान्य सिंटैक्स नहीं लगता है। सही रूप होगा[&,Param2](int arg1){}
लैंबडा फ़ंक्शन एक अनाम फ़ंक्शन है जिसे आप इन-लाइन बनाते हैं। यह चरों को पकड़ सकता है जैसा कि कुछ ने समझाया है, (उदाहरण के लिए http://www.stroustrup.com/C+11FAQ.html#lambda ) लेकिन कुछ सीमाएँ हैं। उदाहरण के लिए, यदि इस तरह का कॉलबैक इंटरफ़ेस है,
void apply(void (*f)(int)) {
f(10);
f(20);
f(30);
}
आप इसे लागू करने के लिए मौके पर एक फ़ंक्शन लिख सकते हैं जैसे नीचे लागू करने के लिए पास किया गया है:
int col=0;
void output() {
apply([](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
लेकिन आप ऐसा नहीं कर सकते:
void output(int n) {
int col=0;
apply([&col,n](int data) {
cout << data << ((++col % 10) ? ' ' : '\n');
});
}
सी ++ 11 मानक में सीमाओं के कारण। यदि आप कैप्चर का उपयोग करना चाहते हैं, तो आपको लाइब्रेरी पर निर्भर रहना होगा और
#include <functional>
(या कुछ अन्य एसटीएल लाइब्रेरी जैसे एल्गोरिथ्म इसे अप्रत्यक्ष रूप से प्राप्त करने के लिए) और फिर std :: फ़ंक्शन के साथ सामान्य कार्यों को पारित करने के बजाय इस तरह से कार्य करें:
#include <functional>
void apply(std::function<void(int)> f) {
f(10);
f(20);
f(30);
}
void output(int width) {
int col;
apply([width,&col](int data) {
cout << data << ((++col % width) ? ' ' : '\n');
});
}
apply
एक टेम्प्लेट था जो एक
उनकी पुस्तक अध्याय 11 ( आईएसबीएन -13: 978-0321563842 ) lambda expression
में C ++ बज़्ने स्ट्रॉस्ट्रुप के लेखक का सबसे अच्छा विवरण दिया गया है :***The C++ Programming Language***
What is a lambda expression?
एक लैम्ब्डा अभिव्यक्ति , कभी कभी भी करने के लिए एक के रूप में संदर्भित लैम्ब्डा समारोह (बोलचाल की भाषा सख्ती से गलत तरीके से बोल रहा है, लेकिन) एक के रूप में या लैम्ब्डा , परिभाषित करने और एक प्रयोग करने के लिए एक सरल अंकन है गुमनाम समारोह वस्तु । एक ऑपरेटर () के साथ एक नामित वर्ग को परिभाषित करने के बजाय, बाद में उस वर्ग की एक वस्तु बनाकर, और अंत में इसे लागू करते हुए, हम एक आशुलिपि का उपयोग कर सकते हैं।
When would I use one?
यह विशेष रूप से तब उपयोगी है जब हम एक एल्गोरिथ्म के तर्क के रूप में एक ऑपरेशन पास करना चाहते हैं। ग्राफिकल यूजर इंटरफेस (और अन्य जगहों पर) के संदर्भ में, इस तरह के ऑपरेशन को अक्सर कॉलबैक कहा जाता है ।
What class of problem do they solve that wasn't possible prior to their introduction?
यहाँ मुझे लगता है कि लैम्ब्डा अभिव्यक्ति के साथ की गई हर क्रिया को उनके बिना हल किया जा सकता है, लेकिन बहुत अधिक कोड और बहुत बड़ी जटिलता के साथ। लैम्ब्डा अभिव्यक्ति यह आपके कोड के लिए अनुकूलन का तरीका है और इसे और अधिक आकर्षक बनाने का तरीका है। स्ट्रॉस्टअप द्वारा दुखी:
अनुकूलन के प्रभावी तरीके
Some examples
लंबोदर अभिव्यक्ति के माध्यम से
void print_modulo(const vector<int>& v, ostream& os, int m) // output v[i] to os if v[i]%m==0
{
for_each(begin(v),end(v),
[&os,m](int x) {
if (x%m==0) os << x << '\n';
});
}
या फ़ंक्शन के माध्यम से
class Modulo_print {
ostream& os; // members to hold the capture list int m;
public:
Modulo_print(ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
या और भी
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
class Modulo_print {
ostream& os; // members to hold the capture list
int m;
public:
Modulo_print (ostream& s, int mm) :os(s), m(mm) {}
void operator()(int x) const
{
if (x%m==0) os << x << '\n';
}
};
for_each(begin(v),end(v),Modulo_print{os,m});
}
अगर यू की जरूरत हो तो आप lambda expression
नीचे जैसा नाम दे सकते हैं:
void print_modulo(const vector<int>& v, ostream& os, int m)
// output v[i] to os if v[i]%m==0
{
auto Modulo_print = [&os,m] (int x) { if (x%m==0) os << x << '\n'; };
for_each(begin(v),end(v),Modulo_print);
}
या एक और सरल नमूना मान लें
void TestFunctions::simpleLambda() {
bool sensitive = true;
std::vector<int> v = std::vector<int>({1,33,3,4,5,6,7});
sort(v.begin(),v.end(),
[sensitive](int x, int y) {
printf("\n%i\n", x < y);
return sensitive ? x < y : abs(x) < abs(y);
});
printf("sorted");
for_each(v.begin(), v.end(),
[](int x) {
printf("x - %i;", x);
}
);
}
आगे उत्पन्न होगा
0
1
0
1
0
1
0
1
0
1
0 सॉर्टेक्स - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;
[]
- यह कैप्चर सूची है या lambda introducer
: यदि lambdas
उनके स्थानीय वातावरण तक पहुंच की आवश्यकता नहीं है, तो हम इसका उपयोग कर सकते हैं।
पुस्तक का उद्धरण:
एक लंबोदर अभिव्यक्ति का पहला चरित्र हमेशा [है । एक लैम्ब्डा परिचयकर्ता विभिन्न रूप ले सकता है:
• [] : एक खाली कब्जा सूची। इसका तात्पर्य यह है कि मेमने के शरीर में आसपास के संदर्भ से कोई भी स्थानीय नाम इस्तेमाल नहीं किया जा सकता है। ऐसे लंबोदर भावों के लिए, डेटा तर्कों से या गैरलोकल चर से प्राप्त किया जाता है।
• [और] : संदर्भ द्वारा स्पष्ट रूप से कब्जा। सभी स्थानीय नामों का उपयोग किया जा सकता है। सभी स्थानीय चर संदर्भ द्वारा एक्सेस किए जाते हैं।
• [=] : मूल्य द्वारा स्पष्ट रूप से कब्जा। सभी स्थानीय नामों का उपयोग किया जा सकता है। सभी नाम लंबोदर अभिव्यक्ति के कॉल के स्थान पर ली गई स्थानीय चर की प्रतियों को संदर्भित करते हैं।
• [कब्जा-सूची]: स्पष्ट कब्जा; कैप्चर-लिस्ट स्थानीय चर के नामों की सूची है जिसे कैप्चर किया जाना चाहिए (यानी, ऑब्जेक्ट में संग्रहीत) संदर्भ या मूल्य से। संदर्भों से पहले वाले नामों के साथ चर और संदर्भ द्वारा कब्जा कर लिया जाता है। अन्य चर मूल्य द्वारा कैप्चर किए जाते हैं। एक कैप्चर सूची में यह भी हो सकता है और इसके बाद के नाम ... तत्व के रूप में।
• [&, कैप्चर-लिस्ट] : सभी स्थानीय चर को संदर्भ द्वारा कैप्चर करें, जिन नामों को सूची में शामिल नहीं किया गया है। कैप्चर सूची में यह शामिल हो सकता है। सूचीबद्ध नामों से पहले और नहीं हो सकता। कैप्चर सूची में नामित चर को मान द्वारा कैप्चर किया जाता है।
• [=, कैप्चर-लिस्ट] : सूची में उल्लेखित नामों के साथ सभी स्थानीय चर को मान द्वारा अनुमानित रूप से कैप्चर करें। कैप्चर सूची में यह शामिल नहीं हो सकता है। सूचीबद्ध नामों को पहले और उसके बाद होना चाहिए। वैरी- एबल्स को कैप्चर लिस्ट में नाम देकर रेफरेंस पर कब्जा कर लिया गया है।
ध्यान दें कि & से पहले एक स्थानीय नाम को हमेशा संदर्भ द्वारा कैप्चर किया जाता है और एक स्थानीय नाम जिसे प्री-बाय नहीं किया जाता है और हमेशा मूल्य द्वारा कब्जा कर लिया जाता है। केवल संदर्भ द्वारा कैप्चर करने से कॉलिंग वातावरण में चर को संशोधित करने की अनुमति मिलती है।
Additional
Lambda expression
प्रारूप
अतिरिक्त संदर्भ:
for (int x : v) { if (x % m == 0) os << x << '\n';}
खैर, एक व्यावहारिक उपयोग जो मुझे पता चला है कि बॉयलर प्लेट कोड कम हो रहा है। उदाहरण के लिए:
void process_z_vec(vector<int>& vec)
{
auto print_2d = [](const vector<int>& board, int bsize)
{
for(int i = 0; i<bsize; i++)
{
for(int j=0; j<bsize; j++)
{
cout << board[bsize*i+j] << " ";
}
cout << "\n";
}
};
// Do sth with the vec.
print_2d(vec,x_size);
// Do sth else with the vec.
print_2d(vec,y_size);
//...
}
लैम्ब्डा के बिना, आपको विभिन्न bsize
मामलों के लिए कुछ करने की आवश्यकता हो सकती है । बेशक आप एक फ़ंक्शन बना सकते हैं लेकिन क्या होगा यदि आप आत्मा उपयोगकर्ता फ़ंक्शन के दायरे में उपयोग को सीमित करना चाहते हैं? लैम्ब्डा की प्रकृति इस आवश्यकता को पूरा करती है और मैं इसका उपयोग उस मामले के लिए करता हूं।
लैम्बडा के सी ++ में "गो उपलब्ध फ़ंक्शन पर" के रूप में माना जाता है। हाँ इसके जाने पर शाब्दिक रूप से, आप इसे परिभाषित करते हैं; इसका इस्तेमाल करें; और पैरेंट फंक्शन स्कोप के खत्म होते ही लैम्बडा फंक्शन चला गया।
c ++ ने इसे c ++ 11 में पेश किया और सभी ने इसे हर संभव स्थान पर उपयोग करना शुरू कर दिया। उदाहरण और लैम्ब्डा क्या है यह यहां पाया जा सकता है https://en.cppreference.com/w/cpp/language/lambda
मैं वर्णन करूँगा जो कि नहीं है लेकिन हर c ++ प्रोग्रामर के लिए जानना आवश्यक है
लैंबडा हर जगह इस्तेमाल करने के लिए नहीं है और हर फंक्शन को लैम्ब्डा से बदला नहीं जा सकता है। यह सामान्य फ़ंक्शन की तुलना में सबसे तेज़ भी नहीं है। क्योंकि इसमें कुछ ओवरहेड होते हैं जिन्हें लैम्ब्डा द्वारा नियंत्रित करने की आवश्यकता होती है।
यह निश्चित रूप से कुछ मामलों में लाइनों की संख्या को कम करने में मदद करेगा। यह मूल रूप से कोड के अनुभाग के लिए उपयोग किया जा सकता है, जिसे एक या अधिक समय में एक ही फ़ंक्शन में बुलाया जा रहा है और कोड का टुकड़ा कहीं और की आवश्यकता नहीं है ताकि आप इसके लिए स्टैंडअलोन फ़ंक्शन बना सकें।
नीचे मेमने का मूल उदाहरण है और पृष्ठभूमि में क्या होता है।
उपयोगकर्ता कोड:
int main()
{
// Lambda & auto
int member=10;
auto endGame = [=](int a, int b){ return a+b+member;};
endGame(4,5);
return 0;
}
संकलन इसका विस्तार कैसे करता है:
int main()
{
int member = 10;
class __lambda_6_18
{
int member;
public:
inline /*constexpr */ int operator()(int a, int b) const
{
return a + b + member;
}
public: __lambda_6_18(int _member)
: member{_member}
{}
};
__lambda_6_18 endGame = __lambda_6_18{member};
endGame.operator()(4, 5);
return 0;
}
जैसा कि आप देख सकते हैं, जब आप इसका उपयोग करते हैं तो यह किस प्रकार का ओवरहेड जोड़ता है। इसलिए इसका हर जगह उपयोग करने के लिए अच्छा विचार नहीं है। इसका उपयोग उन स्थानों पर किया जा सकता है जहां वे लागू होते हैं।
एक समस्या यह हल करती है: कोड एक कंस्ट्रक्शन पैरामीटर फ़ंक्शन का उपयोग करता है जो एक कॉन्स्टेबल सदस्य को इनिशियलाइज़ करने के लिए आउटपुट पैरामीटर फंक्शन का उपयोग करता है।
आप अपनी कक्षा के एक कांस्टेबल सदस्य को एक फ़ंक्शन के लिए कॉल के साथ आरंभ कर सकते हैं, जो आउटपुट आउटपुट के रूप में अपना आउटपुट वापस देकर उसका मूल्य निर्धारित करता है।