සී ++ 11 හි ලැම්බඩා ප්රකාශනය යනු කුමක්ද? මම එකක් භාවිතා කරන්නේ කවදාද? ඔවුන් හඳුන්වාදීමට පෙර කළ නොහැකි වූ ගැටලුව කුමක්ද?
උදාහරණ කිහිපයක්, සහ භාවිතා කිරීමේ අවස්ථා ප්රයෝජනවත් වනු ඇත.
සී ++ 11 හි ලැම්බඩා ප්රකාශනය යනු කුමක්ද? මම එකක් භාවිතා කරන්නේ කවදාද? ඔවුන් හඳුන්වාදීමට පෙර කළ නොහැකි වූ ගැටලුව කුමක්ද?
උදාහරණ කිහිපයක්, සහ භාවිතා කිරීමේ අවස්ථා ප්රයෝජනවත් වනු ඇත.
Answers:
C ++ වැනි ප්රයෝජනවත් Generic බලපත්රය යටතේ අවසර ලබා ඇත කාර්යයන් ඇතුළත් 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
ඔබ පෙරනිමියෙන් ප්රවේශ විට. එකම ආදානය සහිත සෑම ඇමතුමක්ම එකම ප්රති result ලයක් ලබාmutable
දෙන බවට මෙය බලපෑමක් ඇති කරයි, කෙසේ වෙතත් ඔබට ලැම්බඩා සලකුණු කළ හැක්කේ operator()
නිපදවන ලද ඉල්ලීම නොවේ const
.
const
සැමවිටම වේ ...
()
- එය ශුන්ය-තර්ක ලැම්බඩා ලෙස සම්මත වේ, නමුත් ලැම්බඩා සමඟ () const
නොගැලපෙන නිසා , එය ඉඩ දෙන ආකාරයේ පරිවර්තනයක් සොයයි, එයට ව්යංග-වාත්තු ඇතුළත් වේ -To-function-pointer, ඉන්පසු එය අමතන්න! හොර රහසේ!
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;
එක් ඔපෙරන්ඩ් එකක් නිඛිල නියතයක් වන විට දෙගුණයක් ආපසු ලබා දීමට සහතික වන්නේ මන්දැයි සෑම කෙනෙකුම තේරුම් නොගනී යැයි මම සිතමි (එයට හේතුව: පරිවර්තනයන් තෝරාගනු ලැබුවද කමක් නැත). වෙනස් කිරීම 0.0 : d
සමහර විට උදාහරණය තේරුම් ගැනීමට පහසු වනු ඇත.
ලැම්බඩා ශ්රිතයක C ++ සංකල්පය ආරම්භ වන්නේ ලැම්බඩා කැල්කියුලස් සහ ක්රියාකාරී වැඩසටහන්කරණයෙනි. ලැම්බඩා යනු නම් නොකළ ශ්රිතයක් වන අතර එය නැවත භාවිතා කිරීමට නොහැකි සහ නම් කිරීමට තරම් වටිනා නොවන කෙටි කේත කුඩා කොටස් සඳහා ප්රයෝජනවත් වේ (සත්ය ක්රමලේඛනයේදී, න්යායට නොවේ).
C ++ හි ලැම්බඩා ශ්රිතයක් මේ ආකාරයෙන් අර්ථ දක්වා ඇත
[]() { } // barebone lambda
නැතහොත් එහි සියලු තේජසින්
[]() mutable -> T { } // T is the return type, still lacking throw()
[]
අල්ලා ගැනීමේ ලැයිස්තුව, ()
තර්ක ලැයිස්තුව සහ {}
ක්රියාකාරී ශරීරය වේ.
ග්රහණ ලැයිස්තුව මඟින් ලැම්බඩාවේ පිටත සිට ක්රියාකාරී ශරීරය තුළ තිබිය යුතු දේ සහ කෙසේද යන්න නිර්වචනය කරයි. එය එක්කෝ විය හැකිය:
ඉහත සඳහන් ඕනෑම එකක් කොමාවකින් වෙන් කළ ලැයිස්තුවකට ඔබට මිශ්ර කළ හැකිය [x, &y]
.
තර්ක ලැයිස්තුව වෙනත් ඕනෑම C ++ ශ්රිතයකට සමාන වේ.
ලැම්බඩා සැබවින්ම හැඳින්වූ විට ක්රියාත්මක වන කේතය.
ලැම්බඩා සතුව ඇත්තේ එක් ආපසු ප්රකාශයක් පමණක් නම්, ආපසු පැමිණීමේ වර්ගය මඟ හැරිය හැකි අතර එයින් ගම්ය වන ආකාරයේ වේ decltype(return_statement)
.
ලැම්බඩා විකෘති ලෙස සලකුණු කර ඇත්නම් (උදා []() mutable { }
) එය අගය මගින් ග්රහණය කරගත් අගයන් විකෘති කිරීමට අවසර දෙනු ලැබේ.
අයිඑස්ඕ ප්රමිතියෙන් අර්ථ දක්වා ඇති පුස්තකාලය ලැම්බඩාස් වෙතින් විශාල වශයෙන් ප්රතිලාභ ලබා දෙන අතර බාර් කිහිපයක් භාවිතා කිරීමේ හැකියාව ඉහළ නංවයි.
සී ++ දී ලැම්බඩා 14 විවිධ යෝජනා මගින් දීර් extended කර ඇත.
අල්ලා ගැනීමේ ලැයිස්තුවේ අංගයක් දැන් ආරම්භ කළ =
හැකිය. මෙය විචල්යයන් නැවත නම් කිරීමට සහ චලනය කිරීමෙන් අල්ලා ගැනීමට ඉඩ සලසයි. සම්මතයෙන් ගත් උදාහරණයක්:
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
මෙය ලැම්බඩා ප්රකාශන සංකීර්ණ කාර්යයන් නැවත ප්රතිනිර්මාණය කිරීම සඳහා ප්රබල මෙවලමක් බවට පත් කරයි . ඉහත දැක්වෙන පරිදි ලැම්බඩා ශ්රිතයක කේත කොටසක් ඔතා ඔබ ආරම්භ කරන්න. පැහැදිලි පරාමිතිකරණ ක්රියාවලිය පසුව එක් එක් පියවරෙන් පසු අතරමැදි පරීක්ෂණ මගින් ක්රමයෙන් සිදු කළ හැකිය. ඔබ කේත-කොටස සම්පූර්ණයෙන් පරාමිතිකරණය කළ පසු (ඉවත් කිරීමෙන් පෙන්නුම් කරන පරිදි &
), ඔබට කේතය බාහිර ස්ථානයකට ගෙන ගොස් එය සාමාන්ය කාර්යයක් බවට පත් කළ හැකිය.
ඒ හා සමානව, ඇල්ගොරිතමයක ප්රති result ලය මත පදනම්ව විචල්යයන් ආරම්භ කිරීම සඳහා ඔබට ලැම්බඩා ප්රකාශන භාවිතා කළ හැකිය ...
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() {{{{((([](){{}}())));}}}}
පිළිතුරු
ප්ර: සී ++ 11 හි ලැම්බඩා ප්රකාශනය යනු කුමක්ද?
පිළිතුර: කබාය යටතේ, එය අධි බර පැටවීමේ ක්රියාකරු () const සහිත ස්වයංක්රීය උත්පාදක පන්තියක වස්තුවයි . එවැනි වස්තුවක් වසා දැමීම ලෙස හැඳින්වෙන අතර එය සම්පාදකයා විසින් නිර්මාණය කරනු ලැබේ. මෙම 'වසා දැමීමේ' සංකල්පය C ++ 11 හි බන්ධන සංකල්පය සමඟ සමීප වේ. නමුත් ලැම්බඩාස් සාමාන්යයෙන් වඩා හොඳ කේත ජනනය කරයි. සහ වසා දැමීම් හරහා කෙරෙන ඇමතුම් පූර්ණ පේළිගත කිරීමට ඉඩ දෙයි.
ප්ර: මම එකක් භාවිතා කරන්නේ කවදාද?
පිළිතුර: “සරල හා කුඩා තර්කනය” නිර්වචනය කිරීම සහ පෙර ප්රශ්නයෙන් සම්පාදක කාර්ය සාධනය විමසන්න. ඔබ ක්රියාකරු () තුළ සිටීමට අවශ්ය ප්රකාශන කිහිපයක් සම්පාදකයෙකුට ලබා දේ. අනෙක් සියලුම දේවල් සම්පාදකයා ඔබට ජනනය කරනු ඇත.
ප්රශ්නය: ඔවුන් හඳුන්වාදීමට පෙර කළ නොහැකි වූ ගැටලුව කුමක්ද?
පිළිතුර : එය අභිරුචි එකතු කිරීම්, උපසිරැසි මෙහෙයුම් සඳහා කාර්යයන් වෙනුවට ක්රියාකරුවන් අධික ලෙස පැටවීම වැනි සින්ටැක්ස් සීනි වර්ගයකි ... නමුත් සමහර පංතිවලට සැබෑ තර්කනයේ පේළි 1-3 ක් එතීමට අනවශ්ය කේත පේළි කිහිපයක් ඉතිරි කරයි, සහ යනාදිය! සමහර ඉංජිනේරුවන් සිතන්නේ රේඛා ගණන කුඩා නම් එහි දෝෂ ඇතිවීමට ඇති ඉඩකඩ අඩු බවයි (මමත් එසේ සිතමි)
භාවිතයේ උදාහරණය
auto x = [=](int arg1){printf("%i", arg1); };
void(*f)(int) = x;
f(1);
x(1);
ලැම්බඩාස් පිළිබඳ අමතර කරුණු, ප්රශ්න වලින් ආවරණය නොවේ. ඔබ උනන්දුවක් නොදක්වන්නේ නම් මෙම කොටස නොසලකා හරින්න
1. අල්ලා ගත් අගයන්. අල්ලා ගැනීමට ඔබට කළ හැකි දේ
1.1. ලැම්බඩාස් හි ස්ථිතික ගබඩා කාලය සහිත විචල්යයක් ඔබට යොමු කළ හැකිය. ඔවුන් සියල්ලන්ම අල්ලා ගනු ලැබේ.
1.2. "අගය අනුව" අල්ලා ගැනීමේ අගයන් සඳහා ඔබට ලැම්බඩා භාවිතා කළ හැකිය. එවැනි අවස්ථාවකදී අල්ලා ගන්නා ලද විචල්යයන් ශ්රිත වස්තුවට (වසා දැමීම) පිටපත් කරනු ලැබේ.
[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. සියලු ස්ථිතික නොවන විචල්යයන් අගය අනුව ග්රහණය කර ගැනීම සඳහා අංකනය පවතී, නැතහොත් යොමු කිරීම සහ smth නියම කිරීම. තවත්. උදාහරණ: ස්ථිතික නොවන සියලු විචල්යයන් අගය අනුව ග්රහණය කරගන්න
[=,&Param2](int arg1){}
සියලු ස්ථිතික නොවන විචල්යයන් යොමු කිරීමෙන් අල්ලා ගන්න, නමුත් අගය ග්රහණය කර ගැනීම Param2
[&,Param2](int arg1){}
2. ආපසු වර්ගය අඩු කිරීම
2.1. ලැම්බඩා එක් ප්රකාශනයක් නම් ලැම්බඩා ආපසු පැමිණීමේ වර්ගය අඩු කළ හැකිය. නැතහොත් ඔබට එය පැහැදිලිව සඳහන් කළ හැකිය.
[=](int arg1)->trailing_return_type{return trailing_return_type();}
ලැම්බඩාට වඩා වැඩි ප්රකාශනයක් තිබේ නම්, ආපසු එන වර්ගය පසුපස වර්ගය නියම කළ යුතුය. එසේම, ස්වයංක්රීය ශ්රිත සහ සාමාජික ක්රියාකාරකම් සඳහා සමාන සින්ටැක්ස් යෙදිය හැකිය
3. අල්ලා ගත් අගයන්. ඔබට අල්ලා ගත නොහැකි දේ
3.1. ඔබට ග්රහණය කරගත හැක්කේ දේශීය විචල්යයන් මිස වස්තුවෙහි සාමාජික විචල්යය නොවේ.
4. පරිවර්තනයන්
4.1 !! ලැම්බඩා යනු ශ්රිත දර්ශකයක් නොවන අතර එය නිර්නාමික ශ්රිතයක් නොවේ, නමුත් ග්රහණයෙන් අඩු ලැම්බඩාස් ව්යංගයෙන් ශ්රිත දර්ශකයක් බවට පරිවර්තනය කළ හැකිය.
ps
ලැම්බඩා ව්යාකරණ තොරතුරු පිළිබඳ වැඩි විස්තර ක්රමලේඛන භාෂාව සඳහා වන වැඩ කෙටුම්පත C ++ # 337, 2012-01-16, 5.1.2. ලැම්බඩා ප්රකාශන, පි .88
C ++ 14 හි "init capture" ලෙස නම් කර ඇති අතිරේක අංගය එකතු කර ඇත. සංවෘත දත්ත සාමාජිකයින්ගේ අත්තනෝමතික ලෙස ප්රකාශ කිරීමට එය ඉඩ දෙයි:
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');
});
}
C ++ 11 ප්රමිතියේ සීමාවන් නිසා. ඔබට ග්රහණයන් භාවිතා කිරීමට අවශ්ය නම්, ඔබ පුස්තකාලය මත විශ්වාසය තැබිය යුතුය
#include <functional>
(හෝ එය වක්රව ලබා ගැනීම සඳහා ඇල්ගොරිතම වැනි වෙනත් STL පුස්තකාලයක්) ඉන්පසු සාමාන්ය ශ්රිතයන් මෙවැනි පරාමිතීන් ලෙස සම්මත කරනවා වෙනුවට std :: function සමඟ වැඩ කරන්න:
#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
වූ functor පිළිගත් සැකිල්ලක් විය, ඒක වැඩ කරනවා
lambda expression
C ++ Bjarne Stroustrup හි ***The C++ Programming Language***
11 වන පරිච්ඡේදයේ ( ISBN-13: 978-0321563842 ) කතුවරයාගෙන් හොඳම පැහැදිලි කිරීමක් ලබා දී ඇත .
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?
මෙහිදී මම අනුමාන කරන්නේ ලැම්බඩා ප්රකාශනය සමඟ කරන සෑම ක්රියාවක්ම ඒවා නොමැතිව විසඳා ගත හැකි නමුත් බොහෝ කේත සහ වඩා විශාල සංකීර්ණතාවයකින්. ලැම්බඩා ප්රකාශනය මෙය ඔබේ කේතය ප්රශස්තිකරණය කිරීමේ ක්රමය සහ එය වඩාත් ආකර්ෂණීය කිරීමේ ක්රමයකි. Stroustup විසින් කනගාටුදායක ලෙස:
ප්රශස්තිකරණය කිරීමේ ways ලදායී ක්රම
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 sortedx - 1; x - 3; x - 4; x - 5; x - 6; x - 7; x - 33;
[]
- මෙය අල්ලා ගැනීමේ ලැයිස්තුව හෝ lambda introducer
: lambdas
ඔවුන්ගේ දේශීය පරිසරයට ප්රවේශයක් අවශ්ය නොවන්නේ නම් අපට එය භාවිතා කළ හැකිය.
පොතෙන් උපුටා ගැනීම:
ලැම්බඩා ප්රකාශනයක පළමු චරිතය සැමවිටම [ . ලැම්බඩා හඳුන්වාදියෙකුට විවිධ ස්වරූප ගත හැකිය:
• [] : හිස් ග්රහණ ලැයිස්තුවක්. මෙයින් ගම්ය වන්නේ අවට සන්දර්භයෙන් දේශීය නම් කිසිවක් ලැම්බඩා ශරීරය තුළ භාවිතා කළ නොහැකි බවයි. එවැනි ලැම්බඩා ප්රකාශන සඳහා, දත්ත ලබා ගන්නේ තර්ක වලින් හෝ ස්ථානීය නොවන විචල්යයන්ගෙන් ය.
• [&] : යොමු දැක්වීමෙන් ව්යංගයෙන් අල්ලා ගැනීම. සියලුම දේශීය නම් භාවිතා කළ හැකිය. සියලුම දේශීය විචල්යයන් ප්රවේශයෙන් ප්රවේශ වේ.
• [=] : ව්යංගයෙන් අගය අනුව අල්ලා ගැනීම. සියලුම දේශීය නම් භාවිතා කළ හැකිය. සියලුම නම් වලින් ලැම්බඩා ප්රකාශනයේ ඇමතුම ලබා ගත් දේශීය විචල්යයන්ගේ පිටපත් වෙත යොමු වේ.
• [අල්ලා ගැනීමේ ලැයිස්තුව]: පැහැදිලි ග්රහණය; ග්රහණය-ලැයිස්තුව යනු ග්රහණය කර ගත යුතු දේශීය විචල්යයන්ගේ නම් ලැයිස්තුවයි (එනම්, වස්තුව තුළ ගබඩා කර ඇත) යොමු හෝ අගය අනුව ය. පෙර සහ නම් සහිත විචල්යයන් යොමු කර ඇත. වෙනත් විචල්යයන් අගය මගින් අල්ලා ගනු ලැබේ. ග්රහණය කිරීමේ ලැයිස්තුවකට මෙය අඩංගු විය හැකි අතර පසුව එන ... මූලද්රව්ය ලෙස නම් කළ හැකිය.
• [සහ, ග්රහණ-ලැයිස්තුව] : නිසැකයෙන්ම සඳහන් විසින් men- ලැයිස්තුවේ tioned නොවේ නම් සමග සියලු දේශීය විචල්ය අල්ලා. අල්ලා ගැනීමේ ලැයිස්තුවට මෙය අඩංගු විය හැකිය. ලැයිස්තුගත කර ඇති නම් & ට පෙර කළ නොහැක. ග්රහණ ලැයිස්තුවේ නම් කර ඇති විචල්යයන් අගය අනුව ග්රහණය කරගනු ලැබේ.
• [=, ග්රහණය-ලැයිස්තුව] : ලැයිස්තුවේ සඳහන් කර නැති නම් සහිත සියලුම දේශීය විචල්යයන් අගය අනුව ව්යංගයෙන් ග්රහණය කරගන්න. අල්ලා ගැනීමේ ලැයිස්තුවේ මෙය අඩංගු විය නොහැක. ලැයිස්තුගත නම් වලට පෙර &. ග්රහණ ලැයිස්තුවේ නම් කර ඇති විචල්යයන් යොමු කිරීම මගින් ග්රහණය කරගනු ලැබේ.
ඊට පෙර සහ සැමවිටම දේශීය නාමයක් යොමු කිරීම මගින් ග්රහණය කර ඇති බවත්, කලින් සඳහන් නොකළ දේශීය නමක් සැමවිටම අගය මගින් ග්රහණය කර ගන්නා බවත් සලකන්න. ඇමතුම් පරිසරය තුළ විචල්යයන් වෙනස් කිරීමට ඉඩ දෙන්නේ යොමු කිරීම මඟින් පමණි.
Additional
Lambda expression
ආකෘතිය
අමතර යොමු:
for (int x : v) { if (x % m == 0) os << x << '\n';}
C ++ හි ඇති ලැම්බඩා "ගමනේ දී ලබා ගත හැකි ශ්රිතය" ලෙස සලකනු ලැබේ. ඔව්, එය වචනයේ පරිසමාප්ත අර්ථයෙන්ම ගමනේ, ඔබ එය අර්ථ දක්වයි; එය භාවිතා කරන්න; සහ මව් ශ්රිත විෂය පථය අවසන් වන විට ලැම්බඩා ශ්රිතය නැති වී යයි.
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;
}
ඔබට පෙනෙන පරිදි, ඔබ එය භාවිතා කරන විට එය කුමන ආකාරයේ පොදු කාර්යයක් එකතු කරයි. එබැවින් ඒවා සෑම තැනකම භාවිතා කිරීම හොඳ අදහසක් නොවේ. ඒවා අදාළ වන ස්ථානවල භාවිතා කළ හැකිය.
හොඳයි, මම සොයාගත් එක් ප්රායෝගික භාවිතයක් වන්නේ බොයිලර් තහඩු කේතය අඩු කිරීමයි. උදාහරණයක් වශයෙන්:
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
අවස්ථා සඳහා යමක් කිරීමට අවශ්ය විය හැකිය . ඇත්ත වශයෙන්ම ඔබට ශ්රිතයක් නිර්මාණය කළ හැකි නමුත් ආත්ම පරිශීලක ශ්රිතයේ විෂය පථය තුළ භාවිතය සීමා කිරීමට ඔබට අවශ්ය නම් කුමක් කළ යුතුද? ලැම්බඩා වල ස්වභාවය මෙම අවශ්යතාවය සපුරාලන අතර මම එය එම අවස්ථාව සඳහා භාවිතා කරමි.
එය විසඳන එක් ගැටළුවක්: කොන්ස්ට් සාමාජිකයෙකු ආරම්භ කිරීම සඳහා ප්රතිදාන පරාමිති ශ්රිතයක් භාවිතා කරන ඉදිකිරීම්කරුගේ ඇමතුමක් සඳහා ලැම්බඩාට වඩා සරල කේතය
ප්රතිදාන පරාමිතියක් ලෙස එහි ප්රතිදානය ආපසු ලබා දීමෙන් එහි අගය නියම කරන ශ්රිතයකට ඇමතුමක් ලබා දීමෙන් ඔබේ පන්තියේ නියත සාමාජිකයෙකු ආරම්භ කළ හැකිය.