C ++ functors සහ ඒවායේ භාවිතයන් මොනවාද?


894

මම C ++ හි විනෝදජනක ගැන බොහෝ දේ අසමින් සිටිමි. ඒවා මොනවාද සහ ඒවා ප්‍රයෝජනවත් වන්නේ කුමන අවස්ථා වලදීද යන්න පිළිබඳව දළ විශ්ලේෂණයක් මට ලබා දිය හැකිද?


4
මෙම ප්‍රශ්නයට ප්‍රතිචාර වශයෙන් මෙම විෂය ආවරණය කර ඇත: stackoverflow.com/questions/317450/why-override-operator#317528
Luc Touraille

2
එය C ++ හි වසා දැමීමක් නිර්මාණය කිරීමට භාවිතා කරයි.
copper.hat

පහත දැක්වෙන පිළිතුරු දෙස බලන විට, යමෙකු operator()(...)අදහස් කරන්නේ කුමක්දැයි කල්පනා කරන්නේ නම්: එය "ක්‍රියාකාරී ඇමතුම" ක්‍රියාකරුට අධික ලෙස පැටවීමකි. එය හුදෙක් ක්‍රියාකරුට අධික ලෙස ()පැටවීමකි. operator()නමින් හැඳින්වෙන ශ්‍රිතයක් ඇමතීමෙන් වැරැද්දක් නොකරන්න operator, නමුත් එය සාමාන්‍ය ක්‍රියාකරු අධික ලෙස පටවන සින්ටැක්ස් ලෙස බලන්න.
zardosht

Answers:


1049

Functor යනු ක්‍රියාකරු () යන්න නිර්වචනය කරන පන්තියකි. එමඟින් ශ්‍රිතයක් මෙන් පෙනෙන වස්තූන් නිර්මාණය කිරීමට ඔබට ඉඩ සලසයි:

// this is a functor
struct add_x {
  add_x(int val) : x(val) {}  // Constructor
  int operator()(int y) const { return x + y; }

private:
  int x;
};

// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument

std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element 
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1)); 
assert(out[i] == in[i] + 1); // for all i

Functors ගැන හොඳ දේවල් කිහිපයක් තිබේ. එකක් නම් නිත්‍ය කාර්යයන් මෙන් නොව ඒවාට රාජ්‍ය අඩංගු විය හැකි බවයි. ඉහත උදාහරණය මඟින් ඔබ ලබා දෙන ඕනෑම දෙයකට 42 ක් එකතු කරන ශ්‍රිතයක් නිර්මාණය කරයි. නමුත් එම අගය 42 දෘඩ කේතනය කර නැත, එය අපගේ ක්‍රියාකාරී අවස්ථාව නිර්මාණය කරන විට එය ඉදිකිරීම් තර්කයක් ලෙස නියම කර ඇත. මට තවත් ඇඩර් එකක් සෑදිය හැකිය, එය 27 ක් එකතු කළේය, වෙනත් අගයක් සහිත ඉදිකිරීම්කරු ඇමතීමෙන්. මෙමඟින් ඒවා මනාව රිසිකරණය කළ හැකිය.

අන්තිම පේළි පෙන්වන පරිදි, ඔබ බොහෝ විට std :: පරිණාමනය හෝ වෙනත් සම්මත පුස්තකාල ඇල්ගොරිතම වැනි වෙනත් කාර්යයන් සඳහා තර්ක ලෙස ක්‍රියා කරයි. මා ඉහත කී පරිදි, සාමාන්‍ය ක්‍රියාකාරී දර්ශකයක් සමඟ ඔබට එය කළ හැකිය, මා ඉහත කී පරිදි, ෆන්ක්ටර්ස් “අභිරුචිකරණය” කළ හැක්කේ ඒවායේ තත්වය අඩංගු වන නිසා ඒවා වඩාත් නම්‍යශීලී වන බැවිනි (මට ශ්‍රිත දර්ශකයක් භාවිතා කිරීමට අවශ්‍ය නම්, මට ශ්‍රිතයක් ලිවීමට සිදුවේ එය එහි තර්කයට හරියටම 1 ක් එක් කළේය.ප්‍රකාරකය සාමාන්‍යය වන අතර ඔබ එය ආරම්භ කළ ඕනෑම දෙයක් එකතු කරයි), තවද ඒවා වඩාත් කාර්යක්ෂම වේ. ඉහත උදාහරණයේ දී, කුමන ශ්‍රිතය std::transformඇමතිය යුතු දැයි සම්පාදකයා හරියටම දනී . එය ඇමතිය යුතුය add_x::operator(). ඒ කියන්නේ එයට එම ක්‍රියාකාරී ඇමතුම ලබා දිය හැකිය. එය දෛශිකයේ එක් එක් අගය මත ශ්‍රිතය මා විසින් අතින් හැඳින්වූවාක් මෙන් කාර්යක්ෂම කරයි.

මම ඒ වෙනුවට ශ්‍රිත දර්ශකයක් සම්මත කර ඇත්නම්, සම්පාදකයාට එය යොමු කරන්නේ කුමන ශ්‍රිතයට දැයි නොපෙනේ, එබැවින් එය තරමක් සංකීර්ණ ගෝලීය ප්‍රශස්තිකරණයන් සිදු නොකරන්නේ නම්, එය ක්‍රියාත්මක වන වේලාවේදී දර්ශකය අවලංගු කළ යුතු අතර පසුව ඇමතුම ලබා දෙන්න.


33
ඔබට මෙම රේඛාව පැහැදිලි කළ හැකිද, කරුණාකර std :: පරිණාමනය (in.begin (), in.end (), out.begin (), add_x (1)); ඔබ එහි ලියා ඇත්තේ add_x මිස add42 නොවේද?
ඇලෙක්ස්

103
Le ඇලෙක්ස් දෙකම වැඩ කිරීමට ඉඩ තිබුණි (නමුත් බලපෑම වෙනස් වනු ඇත). මම භාවිතා කළා නම් add42, මම කලින් නිර්මාණය කළ ෆන්ක්ටර් භාවිතා කර, එක් එක් අගයට 42 ක් එකතු කරමි. add_x(1)මම එක් එක් අගයට 1 ක් පමණක් එකතු කරන ෆන්ක්ටර් හි නව අවස්ථාවක් නිර්මාණය කිරීමත් සමඟ . එය බොහෝ විට පෙන්වීමට නම්, ඔබ එය අවශ්‍ය විටදී, එය මුලින්ම නිර්මාණය කරනවාට වඩා “පියාසර කරන විට” ක්ෂණිකව ක්‍රියාත්මක කරන අතර ඔබ එය ඕනෑම දෙයක් සඳහා භාවිතා කිරීමට පෙර එය වටා තබා ගන්න.
jalf

8
ඇත්තෙන්ම සාඩාන්. ඔවුන්ට එය තිබිය යුතුය operator(), මන්ද එය අමතන්නා අමතන්නේ එයයි. සාමාජික ක්‍රියාකාරිත්වයන්, ඉදිකිරීම්කරුවන්, ක්‍රියාකරුවන් සහ සාමාජික විචල්‍යයන් පිළිබඳ ක්‍රියාකාරීයාට ඇති වෙනත් දේ සම්පූර්ණයෙන්ම ඔබට භාරයි.
jalf

4
@ rikimaru2013 ක්‍රියාකාරී ක්‍රමලේඛයේ උපභාෂාවේදී, ඔබ නිවැරදියි, ශ්‍රිතයක් ද ක්‍රියාකාරී ය, නමුත් C ++ හි උපභාෂාව අනුව, Functor යනු ශ්‍රිතයක් ලෙස භාවිතා කරන පන්තියකි. මෙම පාරිභාෂිතය මුලදී මඳක් අපයෝජනයට ලක් වූ නමුත් බෙදීම ප්‍රයෝජනවත් වෙනසක් වන අතර එය අදටත් පවතී. ඔබ C ++ සන්දර්භය තුළ කාර්යයන් "functors" ලෙස හැඳින්වීමට පටන් ගන්නේ නම් ඔබ සංවාදය ව්‍යාකූල කරයි.
srm

6
එය පන්තියක් ද? බොහෝ මූලාශ්‍රවල, add42හැඳින්වෙන්නේ ෆන්ක්ටර් ලෙස මිස add_x(එය ෆන්ක්ටර්ගේ පන්තිය හෝ විනෝද පන්තිය නොවේ). මම එම පාරිභාෂිතය අනුකූල බව සලකන්නේ ක්‍රියාකාරී පන්ති ලෙස නොව ක්‍රියාකාරී වස්තු ලෙසිනි . ඔබට මෙම කරුණ පැහැදිලි කළ හැකිද?
සර්ජි ටචෙනොව්

122

කුඩා එකතු කිරීම. මෙවැනි boost::functionකාර්යයන් හා ක්‍රම වලින් විනෝදජනක නිර්මාණය කිරීමට ඔබට භාවිතා කළ හැකිය :

class Foo
{
public:
    void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"

ඔබට මෙම ෆන්ක්ටරයට තත්වය එක් කිරීමට බූස්ට් :: බයින්ඩ් භාවිතා කළ හැකිය

boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"

සහ වඩාත් ප්‍රයෝජනවත්, බූස්ට් :: බයින්ඩ් සහ බූස්ට් :: ශ්‍රිතය සමඟ ඔබට පන්ති ක්‍රමයෙන් ෆන්ක්ටර් නිර්මාණය කළ හැකිය, ඇත්ත වශයෙන්ම මෙය නියෝජිතයෙකි:

class SomeClass
{
    std::string state_;
public:
    SomeClass(const char* s) : state_(s) {}

    void method( std::string param )
    {
        std::cout << state_ << param << std::endl;
    }
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"

ඔබට ෆන්ක්ටර්ස් ලැයිස්තුවක් හෝ දෛශිකයක් සෑදිය හැකිය

std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
        events.begin(), events.end(), 
        boost::bind( boost::apply<void>(), _1, e));

මේ සියල්ල සමඟ එක් ගැටළුවක් ඇත, සම්පාදක දෝෂ පණිවිඩ මිනිසුන්ට කියවිය නොහැක :)


4
operator ()පංති පෙරනිමියෙන් පුද්ගලික වන බැවින් ඔබගේ පළමු උදාහරණයේ පොදු නොවිය යුතුද ?
නේතන් ඔලිවර්

4
සමහර විට මෙම පිළිතුර යාවත්කාලීනයකට සුදුසු විය හැකිය, මන්ද දැන් ඕනෑම
දෙයකින් විනෝදයක්

104

Functor යනු ශ්‍රිතයක් ලෙස ක්‍රියා කරන වස්තුවකි. මූලික වශයෙන්, අර්ථ දක්වන පන්තියක් operator().

class MyFunctor
{
   public:
     int operator()(int x) { return x * 2;}
}

MyFunctor doubler;
int x = doubler(5);

සැබෑ වාසිය නම්, ක්‍රියාකරුවෙකුට රාජ්‍යය රඳවා ගත හැකි වීමයි.

class Matcher
{
   int target;
   public:
     Matcher(int m) : target(m) {}
     bool operator()(int x) { return x == target;}
}

Matcher Is5(5);

if (Is5(n))    // same as if (n == 5)
{ ....}

12
ශ්‍රිත දර්ශකයක් මෙන් ඒවා භාවිතා කළ හැකි බව එක් කිරීමට අවශ්‍යය.
මාටින් යෝර්ක්

9
Ok ලොකි අස්ටාරි - සංකල්පයට අළුත් ඒවා සඳහා, එය ටිකක් නොමඟ යවන සුළු විය හැකිය. ශ්‍රිතයන් “මෙන්” භාවිතා කළ හැකි නමුත් සෑම විටම “ශ්‍රිත දර්ශක වෙනුවට” භාවිතා කළ නොහැක. නිදසුනක් ලෙස, ශ්‍රිත දර්ශකය ගන්නා ශ්‍රිතයකට ශ්‍රිත දර්ශකයට සමාන තර්ක හා ප්‍රතිලාභ අගය තිබුණත් එහි ස්ථානයේ ක්‍රියාකාරීත්වයක් ගත නොහැක. එහෙත්, සැලසුම් කිරීමේදී විශාල වශයෙන්, ෆන්ක්ටර් යනු වඩාත් කැමති හා න්‍යායාත්මකව “වඩා නවීන” යන්නයි.
මේසන් වින්සාවර්

දෙවැන්න නැවත පැමිණිය intයුත්තේ boolඇයි? මෙය සී ++ මිස සී නොවේ. මෙම පිළිතුර ලියන විට boolනොපවතිනවාද?
අරමුදල් මොනිකාගේ නඩුව

@QPaysTaxes මම අනුමාන කරන අකුරු වර්ගයක්. මම බොහෝ විට පළමු උදාහරණයෙන් කේතය පිටපත් කර එය වෙනස් කිරීමට අමතක කළෙමි. මම දැන් එය නිවැරදි කර ඇත්තෙමි.
ජේම්ස් කුරන්

1
IasRiasat මැචර් පුස්තකාලයක තිබේ නම්, Is5 () නිර්වචනය කිරීම ඉතා සරල ය. ඔබට Is7 (), Is32 () යනාදිය නිර්මාණය කළ හැකිය. තවද, එය උදාහරණයක් පමණි. මෙම ෆන්ක්ටර් වඩාත් සංකීර්ණ විය හැකිය.
ජේම්ස් කුරන්

53

නම "functor" traditionaly භාවිතා කර ඇත ප්රවර්ගය න්යාය C ++ එම ස්ථානයට පෙනී බොහෝ කලකට පෙර. මෙය C ++ සංකල්පය සමඟ සම්බන්ධයක් නැත. C ++ හි "functor" ලෙස අප හඳුන්වන දේ වෙනුවට නාම ශ්‍රිත වස්තුව භාවිතා කිරීම වඩා හොඳය . වෙනත් ක්‍රමලේඛන භාෂාවන් සමාන ඉදිකිරීම් ලෙස හඳුන්වන්නේ එලෙස ය.

සරල ශ්‍රිතය වෙනුවට භාවිතා වේ:

විශේෂාංග:

  • ක්‍රියාකාරී වස්තුවට තත්වය තිබිය හැක
  • ක්‍රියාකාරී වස්තුව OOP එකට ගැලපේ (එය අනෙක් සෑම වස්තුවක් මෙන් ක්‍රියා කරයි).

අවාසි:

  • වැඩසටහනට වඩාත් සංකීර්ණ බවක් ගෙන එයි.

ශ්‍රිත දර්ශකය වෙනුවට භාවිතා කරයි:

විශේෂාංග:

  • ක්‍රියාකාරී වස්තුව බොහෝ විට නැඹුරු විය හැකිය

අවාසි:

  • ක්‍රියාකාරී කාලය තුළ ක්‍රියාකාරී වස්තුව වෙනත් ක්‍රියාකාරී වස්තු වර්ගයක් සමඟ මාරු කළ නොහැක (අවම වශයෙන් එය කිසියම් මූලික පංතියක් විස්තාරණය නොකරන්නේ නම්, එමඟින් යම් පොදු කාර්යයක් ලබා දෙයි)

අතථ්‍ය ශ්‍රිතය වෙනුවට භාවිතා කරයි:

විශේෂාංග:

  • ක්‍රියාකාරී වස්තුවට (අථත්‍ය නොවන) vtable සහ ධාවන කාල යැවීම අවශ්‍ය නොවේ, එබැවින් එය බොහෝ අවස්ථාවන්හිදී වඩාත් කාර්යක්ෂම වේ

අවාසි:

  • ක්‍රියාකාරී කාලය තුළ ක්‍රියාකාරී වස්තුව වෙනත් ක්‍රියාකාරී වස්තු වර්ගයක් සමඟ මාරු කළ නොහැක (අවම වශයෙන් එය කිසියම් මූලික පංතියක් විස්තාරණය නොකරන්නේ නම්, එමඟින් යම් පොදු කාර්යයක් ලබා දෙයි)

1
ඔබට මෙම භාවිත අවස්ථා සැබෑ උදාහරණයෙන් පැහැදිලි කළ හැකිද? බහුමාපකය ඇඩ්න් ෆන්ෂන් පොයින්ටරය ලෙස අපට ෆන්ක්ටර් භාවිතා කරන්නේ කෙසේද?
මිලාඩ් කජාවි

1
ක්‍රියාකරුවෙකු රාජ්‍යයක් ඇති බව ඇත්ත වශයෙන්ම අදහස් කරන්නේ කුමක්ද?
erogol

යමෙකුට යම් ආකාරයක බහුමාපනයක් ලබා ගැනීමට මූලික පන්තියක් අවශ්‍ය බව පෙන්වා දීම ගැන ස්තූතියි. මට ඇත්තේ සරල ක්‍රියාකාරී දර්ශකයක් ලෙස එකම ස්ථානයක ෆන්ක්ටරයක් ​​භාවිතා කිරීමට ඇති ගැටලුවයි. මට හමු වූ එකම ක්‍රමය වූයේ ෆන්ක්ටර් බේස් පංතියක් ලිවීමයි (මට සී ++ 11 දේවල් භාවිතා කළ නොහැකි බැවින්). මම ඔබේ පිළිතුර කියවන තුරු මෙම පොදු කාර්යය අර්ථවත් වේදැයි විශ්වාස නැත.
idclev 463035818

1
Rog ඊරොගෝල් ෆන්ක්ටර් යනු වාක්‍ය ඛණ්ඩයට සහය දක්වන වස්තුවකි foo(arguments). එබැවින් එහි විචල්යයන් අඩංගු විය හැකිය; උදාහරණයක් ලෙස, ඔබට update_password(string)ශ්‍රිතයක් තිබේ නම්, එය කොපමණ වාර ගණනක් සිදුවී ඇත්දැයි සොයා බැලීමට ඔබට අවශ්‍ය විය හැකිය; private long timeඑය අවසන් වරට සිදු වූ කාලරාමුව නිරූපණය කළ හැකි ෆන්ක්ටර් සමඟ . ශ්‍රිත දර්ශකයක් හෝ සරල ශ්‍රිතයක් සමඟ, ඔබට එහි නාම අවකාශයෙන් පිටත විචල්‍යයක් භාවිතා කිරීමට අවශ්‍ය වනු ඇත, එය අර්ථ දැක්වීමකට වඩා ප්‍රලේඛන හා භාවිතයෙන් කෙලින්ම සම්බන්ධ වේ.
L

4
. කිසිදු හේතුවක් නොමැතිව නම සෑදී ඇති බව සඳහන් කිරීම සඳහා. මම දැන් සොයන්නේ ගණිතමය (හෝ ඔබට අවශ්‍ය නම් ක්‍රියාකාරී ) ෆන්ක්ටර් සහ සී ++ අතර ඇති සම්බන්ධය කුමක්ද යන්නයි .
හායි-ඒන්ජල්

41

අනෙක් අය සඳහන් කර ඇති පරිදි, ෆන්ක්ටර් යනු ශ්‍රිතයක් ලෙස ක්‍රියා කරන වස්තුවකි, එනම් එය ක්‍රියාකාරී ඇමතුම් ක්‍රියාකරුට අධික ලෙස පටවයි.

STL ඇල්ගොරිතම වල කාර්යයන් බහුලව භාවිතා වේ. ක්‍රියාකාරී භාෂාවලින් වසා දැමීමක් වැනි ක්‍රියාකාරී ඇමතුම් වලට පෙර සහ ඒවා අතර තත්වය රඳවා තබා ගත හැකි බැවින් ඒවා ප්‍රයෝජනවත් වේ. නිදසුනක් ලෙස, MultiplyByඑහි තර්කය නිශ්චිත ප්‍රමාණයකින් ගුණ කරන ෆන්ක්ටරයක් ඔබට අර්ථ දැක්විය හැකිය :

class MultiplyBy {
private:
    int factor;

public:
    MultiplyBy(int x) : factor(x) {
    }

    int operator () (int other) const {
        return factor * other;
    }
};

එවිට ඔබට MultiplyBystd :: පරිණාමනය වැනි ඇල්ගොරිතමයකට වස්තුවක් යැවිය හැකිය .

int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}

ශ්‍රිතයක් සඳහා දර්ශකයක් හරහා ක්‍රියා කරන්නෙකුගේ තවත් වාසියක් නම්, ඇමතුම තවත් අවස්ථා වලදී යොමු කළ හැකිය. ඔබ ශ්‍රිත දර්ශකයක් වෙත යොමු කළේ transformනම්, එම ඇමතුම ආදාන වී ඇත්නම් සහ ඔබ සැමවිටම එකම ශ්‍රිතය එයට යොමු කරන බව සම්පාදකයා දන්නේ නම් මිස, එමඟින් ඇමතුම දර්ශකය හරහා යොමු කළ නොහැක.


37

අප අතර සිටින නවක වදය සඳහා: කුඩා පර්යේෂණයකින් පසුව, ජල්ෆ් විසින් පළ කරන ලද කේතය කුමක්දැයි මම සොයා ගතිමි.

Functor යනු ශ්‍රිතයක් මෙන් “හැඳින්විය හැකි” පන්ති හෝ ව්‍යුහාත්මක වස්තුවකි. මෙය අධික ලෙස පැටවීමෙන් කළ හැකි ය () operator. මෙම () operator(එහි කැඳවා නොවන වග) තර්ක ඕනෑම අංකය ගත හැක. අනෙක් ක්‍රියාකරුවන් ගන්නේ දෙකක් පමණි, එනම් + operatorකළ හැක්කේ අගයන් දෙකක් පමණි (ක්‍රියාකරුගේ එක් පැත්තක එකක්) සහ ඔබ එය අධික ලෙස පටවා ඇති ඕනෑම වටිනාකමක් ආපසු එවන්න. ඔබට () operatorඑහි ඇති නම්යශීලීභාවය ලබා දෙන ඕනෑම තර්ක ගණනක් සවි කළ හැකිය .

මුලින්ම ඔබේ පන්තිය සාදන්න. ඉන්පසු ඔබ තෝරාගත් වර්ගය සහ නම පරාමිතියක් සමඟ පන්තියට ඉදිකිරීම්කරුවෙකු සාදයි. මෙම ප්‍රකාශයේම ආරම්භක ලැයිස්තුවක් (තනි කොලන් ක්‍රියාකරුවෙකු භාවිතා කරයි, එය මටද අලුත් දෙයක් විය), එය පන්ති සාමාජික වස්තූන් කලින් ප්‍රකාශිත පරාමිතිය සමඟ ඉදිකිරීම්කරුට සාදයි. එවිට () operatorඑය අධික ලෙස පටවනු ලැබේ. අවසාන වශයෙන් ඔබ විසින් නිර්මාණය කරන ලද පන්තියේ හෝ ව්‍යුහයේ පුද්ගලික වස්තු ප්‍රකාශ කරන්න.

මගේ කේතය (ජල්ෆ්ගේ විචල්‍ය නම් අවුල් සහගත බව මට පෙනී ගියේය)

class myFunctor
{ 
    public:
        /* myFunctor is the constructor. parameterVar is the parameter passed to
           the constructor. : is the initializer list operator. myObject is the
           private member object of the myFunctor class. parameterVar is passed
           to the () operator which takes it and adds it to myObject in the
           overloaded () operator function. */
        myFunctor (int parameterVar) : myObject( parameterVar ) {}

        /* the "operator" word is a keyword which indicates this function is an 
           overloaded operator function. The () following this just tells the
           compiler that () is the operator being overloaded. Following that is
           the parameter for the overloaded operator. This parameter is actually
           the argument "parameterVar" passed by the constructor we just wrote.
           The last part of this statement is the overloaded operators body
           which adds the parameter passed to the member object. */
        int operator() (int myArgument) { return myObject + myArgument; }

    private: 
        int myObject; //Our private member object.
}; 

මේ කිසිවක් සාවද්‍ය නම් හෝ සරල වැරැද්දක් නම් මාව නිවැරදි කිරීමට නිදහස් වන්න!


1
() ක්‍රියාකරු ක්‍රියාකාරී ඇමතුම් ක්‍රියාකරු ලෙස හැඳින්වේ. මම හිතන්නේ ඔබට එය වරහන් ක්‍රියාකරු ලෙසද හැඳින්විය හැකිය.
ගෞතම්

4
"මෙම පරාමිතිය ඇත්ත වශයෙන්ම අප විසින් ලියන ලද ඉදිකිරීම්කරු විසින් සම්මත කරන ලද" පරාමිතිවර් "තර්කයයි" හහ්?
කක්ෂයේ සැහැල්ලු ධාවන තරඟ

23

Functor යනු පරාමිතිගත (එනම් සැකසූ ) වර්ග වලට ශ්‍රිතයක් අදාළ කරන ඉහළ පෙළේ ශ්‍රිතයකි . එය සිතියමේ ඉහළ පෙළේ ශ්‍රිතය සාමාන්‍යකරණය කිරීමකි . උදාහරණයක් ලෙස, අපට std::vectorමේ සඳහා විනෝදකාරකයක් අර්ථ දැක්විය හැකිය :

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
    std::vector<U> result;
    std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
    return result;
}

මෙම ක්රියාව යම් යම් std::vector<T>ප්රතිලාභ හා std::vector<U>උත්සවයකට ලබා විට Fඑය ගත වන බව Tසහ නැවත U. බහාලුම් වර්ග වලට වඩා විනෝදයක් නිර්වචනය කළ යුතු නැත, එය ඕනෑම සැකසූ වර්ගයකට අර්ථ දැක්විය හැකිය std::shared_ptr:

template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
    if (p == nullptr) return nullptr;
    else return std::shared_ptr<U>(new U(f(*p)));
}

වර්ගය a බවට පරිවර්තනය කරන සරල උදාහරණයක් මෙන්න double:

double to_double(int x)
{
    return x;
}

std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);

std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);

Functors අනුගමනය කළ යුතු නීති දෙකක් තිබේ. පළමුවැන්න අනන්‍යතා නීතියයි, එහි සඳහන් වන්නේ, ක්‍රියාකරුට අනන්‍යතා ශ්‍රිතයක් ලබා දෙන්නේ නම්, එය වර්ගයට අනන්‍යතා ශ්‍රිතය යෙදීමට fmap(identity, x)සමාන විය යුතු බවත්, එය සමාන විය යුතු බවත් ය identity(x).

struct identity_f
{
    template<class T>
    T operator()(T x) const
    {
        return x;
    }
};
identity_f identity = {};

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);

ඊළඟ නියමය වන්නේ සංයුතියේ නීතියයි, එහි සඳහන් වන්නේ, ශ්‍රිතයට ශ්‍රිත දෙකක සංයුතියක් ලබා දෙන්නේ නම්, එය පළමු ශ්‍රිතය සඳහා ෆන්ක්ටර් යෙදීම හා දෙවන ශ්‍රිතය සඳහා නැවත යෙදීම හා සමාන විය යුතු බවයි. එබැවින්, පහත පරිදි fmap(std::bind(f, std::bind(g, _1)), x)විය යුතුය fmap(f, fmap(g, x)):

double to_double(int x)
{
    return x;
}

struct foo
{
    double x;
};

foo to_foo(double x)
{
    foo r;
    r.x = x;
    return r;
}

std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));

2
මෙම අර්ථය සඳහා ෆන්ක්ටර් නිවැරදිව භාවිතා කළ යුතු බවට තර්ක කරන ලිපිය ( en.wikipedia.org/wiki/Functor ද බලන්න ), සහ ක්‍රියාකාරී වස්තු සඳහා එය භාවිතා කිරීම හුදෙක් අලස බව : jackieokay.com/2017/01/26/functors.html එය ශ්‍රිත වස්තු අර්ථය පමණක් සලකා බලන පිළිතුරු ගණන සැලකිල්ලට ගෙන ඒ සඳහා ප්‍රමාද විය හැකිය.
armb

2
මෙම පිළිතුර> Upvotes 700 සහිත පිළිතුර විය යුතුය. යමෙකු C ++ ට වඩා හොඳින් හස්කල්ව දන්නා නිසා, C ++ භාෂාව මට නිතරම ප්‍රහේලිකාවක් විය.
mschmidt

කාණ්ඩ න්‍යාය සහ සී ++? මෙය බාර්ටෝස් මිලේව්ස්කිගේ රහස් SO ගිණුමද?
මාටීන් උල්හාක්

1
සම්මත අංකනයෙන් ෆන්ක්ටර් නීති සාරාංශ කිරීම ප්‍රයෝජනවත් විය හැකිය: fmap(id, x) = id(x)සහ fmap(f ◦ g, x) = fmap(f, fmap(g, x)).
මාටීන් උල්හාක්

chmschmidt සහ functor ද මෙයින් අදහස් කරන අතර, C ++ විසින් "ක්‍රියාකාරී වස්තුව" ලෙස අර්ථ දැක්වීම සඳහා නම අධික ලෙස
පටවනු ලැබේ

9

මෙන්න මගේ ගැටලුව විසඳීම සඳහා Functor භාවිතා කිරීමට මට බල කළ සත්‍ය තත්වයක්:

මට ශ්‍රිත සමූහයක් ඇත (කියන්න, ඒවායින් 20 ක්), ඒවා සියල්ලම සමාන වේ, එක් එක් විශේෂිත ස්ථාන 3 කින් වෙනස් විශේෂිත ශ්‍රිතයක් ඇමතීම හැර.

මෙය ඇදහිය නොහැකි අපද්‍රව්‍ය සහ කේත අනුපිටපත් කිරීමකි. සාමාන්‍යයෙන් මම ශ්‍රිත දර්ශකයකින් සමත් වන අතර එය ස්ථාන 3 කින් අමතන්න. (එබැවින් කේතය විසි වාරයක් වෙනුවට එක් වරක් පමණක් දර්ශනය විය යුතුය.)

නමුත් එක් එක් අවස්ථාවෙහිදී, විශේෂිත ශ්‍රිතයට සම්පූර්ණයෙන්ම වෙනස් පරාමිති පැතිකඩක් අවශ්‍ය බව මම තේරුම් ගතිමි! සමහර විට පරාමිති 2 ක්, සමහර විට පරාමිති 5 ක් යනාදිය.

තවත් විසඳුමක් වනුයේ මූලික පන්තියක් තිබීමයි, එහිදී නිශ්චිත ශ්‍රිතය ව්‍යුත්පන්න පන්තියක අභිබවා යන ක්‍රමයකි. නමුත් මට ඇත්තටම මේ සියලු උරුමය ගොඩනඟා ගැනීමට අවශ්‍යද, එවිට මට ශ්‍රිත දර්ශකයක් සමත් විය හැකිද ????

විසඳුම: ඉතින් මම කළේ, මම අවශ්‍ය ඕනෑම කාර්යයක් ඇමතීමට හැකි වන පරිදි එතීමේ පන්තියක් ("ෆන්ක්ටර්") සෑදුවෙමි. මම එය කල්තියාම සකසා ඇත (එහි පරාමිතීන් යනාදිය සමඟ) පසුව මම එය ක්‍රියාකාරී දර්ශකයක් වෙනුවට සම්මත කරමි. දැන් කැඳවූ කේතය මඟින් අභ්‍යන්තරයේ සිදුවන්නේ කුමක්ද යන්න නොදැන Functor අවුලුවාලිය හැකිය. එයට කිහිප වතාවක්ම ඇමතිය හැකිය (මට එය 3 වතාවක් ඇමතීමට අවශ්‍ය විය.)


එය එයයි - ක්‍රියාකාරී උදාහරණයක් පැහැදිලි හා පහසු විසඳුමක් බවට පත් වූ අතර එමඟින් කේත අනුපිටපත් ශ්‍රිත 20 සිට 1 දක්වා අඩු කිරීමට මට ඉඩ ලබා දුන්නේය.


3
ඔබේ ක්‍රියාකාරීත්වය විවිධ විශේෂිත ශ්‍රිතයන් නම් කර ඇති අතර, මෙම අනෙකුත් කාර්යයන් ඔවුන් පිළිගන්නා පරාමිති ගණන අනුව වෙනස් වේ නම්, මෙයින් අදහස් කරන්නේ ඔබේ ශ්‍රිතයා මෙම අනෙකුත් ශ්‍රිත වෙත යැවීම සඳහා විචල්‍ය තර්ක ගණනාවක් පිළිගෙන ඇති බවයි?
ජෝන්බකර්ස්

4
කේතයේ යම් කොටසක් උපුටා දක්වමින් කරුණාකර ඔබට ඉහත තත්වය පැහැදිලි කළ හැකිද, මම සී ++ ට අලුත් ය මෙම සංකල්පය තේරුම් ගැනීමට අවශ්‍යයි ..
සංජීව්

3

සමහර GUI බොත්තම සත්‍ය C ++ ශ්‍රිතයකට හෝ ක්‍රමයකට සම්බන්ධ කිරීම සඳහා gtkmm හි ශ්‍රිත භාවිතා කරනු ලැබේ.


ඔබගේ යෙදුම බහු-ත්‍රෙඩ් කිරීමට ඔබ pthread පුස්තකාලය භාවිතා කරන්නේ නම්, Functors ඔබට උදව් කළ හැකිය.
ත්‍රෙඩ් එකක් ආරම්භ කිරීම සඳහා, pthread_create(..)ඔහුගේම නූල් මත ක්‍රියාත්මක කළ යුතු ශ්‍රිත දර්ශකය වේ.
නමුත් එක් අපහසුතාවයක් තිබේ. එය වේ නම් මිස මෙම පහිටුම් දක්වනය, ගැනීමට ක්රමයක් අවධානය යොමුකළ යුතු නො හැකි ස්ථිතික ක්රමය නම්, හෝ ඔබ විසින් නම් එය විසින් පන්ති නියම මෙන් class::method. තවත් දෙයක් නම්, ඔබේ ක්‍රමයේ අතුරු මුහුණත විය හැක්කේ:

void* method(void* something)

එබැවින් ඔබට අමතර දෙයක් නොකර ඔබේ පන්තියේ සිට නූල් ආකාරයෙන් ධාවනය කළ නොහැක (සරල පැහැදිලි ආකාරයකින්).

C ++ හි ඇති නූල් සමඟ ගනුදෙනු කිරීමේ ඉතා හොඳ ක්‍රමයක් වන්නේ ඔබේම Threadපන්තියක් නිර්මාණය කිරීමයි . ඔබට MyClassපන්තියේ සිට ක්‍රම ක්‍රියාත්මක කිරීමට අවශ්‍ය නම් , මා කළේ එම ක්‍රම බවට පරිවර්තනය කිරීමයිFunctor ව්‍යුත්පන්න පංති .

එසේම, Threadපන්ති මෙම ක්රමය ඇත: static void* startThread(void* arg)
මෙම ක්රමය කිරීමට අවධානය යොමුකළ ඇමතුමක් කිරීමට තර්කයක් ලෙස භාවිතා කරනු ඇත pthread_create(..). startThread(..)ආග් වලින් ලැබිය යුතු දෙය void*නම්, ඕනෑම Functorව්‍යුත්පන්න පංතියක ගොඩගැසී ඇති නිදසුනක් වන අතර Functor*එය ක්‍රියාත්මක කරන විට නැවත දමනු ලැබේ, පසුව එය run()ක්‍රමවේදය ලෙස හැඳින්වේ .


උදාහරණයක් තිබේද?
OS2

3

ඇමතුම් ලබා ගැනීමේදී භාවිතා කිරීම හැරුණු විට, C ++ functors හට මැට්ලැබ් කැමති ප්‍රවේශ ශෛලියක් අනුකෘති පන්තියකට ලබා දිය හැකිය. ඒ නිසා එය උදාහරණයක් .


මෙය (අනුකෘති උදාහරණය) යනු operator()ක්‍රියාකාරී වස්තු ගුණාංග සරල ලෙස භාවිතා කිරීමකි .
රෙනාර්ඩෙස්ක්

3

පුනරාවර්තනය වී ඇති ආකාරයටම, ෆන්ක්ටර්ස් යනු කාර්යයන් ලෙස සැලකිය හැකි පන්ති වේ (අධි බර ක්‍රියාකරු ()).

සමහර දත්ත නැවත නැවත හෝ ප්‍රමාද වූ ඇමතුම් සමඟ ශ්‍රිතයක් සමඟ සම්බන්ධ කිරීමට අවශ්‍ය අවස්ථාවන් සඳහා ඒවා වඩාත් ප්‍රයෝජනවත් වේ.

නිදසුනක් ලෙස, මූලික අඩු-පොදු සමමුහුර්ත කොරොටින් පද්ධතියක්, කාර්යය පිටත් කරුවෙකු හෝ බාධා කළ හැකි ගොනු විග්‍රහ කිරීම ක්‍රියාත්මක කිරීම සඳහා සම්බන්ධක ලැයිස්තුවක් භාවිතා කළ හැකිය. උදාහරණ:

/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
    std::string output;
    Functor(const std::string& out): output(out){}
    operator()() const
    {
        std::cout << output << " ";
    }
};

int main(int argc, char **argv)
{
    std::list<Functor> taskQueue;
    taskQueue.push_back(Functor("this"));
    taskQueue.push_back(Functor("is a"));
    taskQueue.push_back(Functor("very simple"));
    taskQueue.push_back(Functor("and poorly used"));
    taskQueue.push_back(Functor("task queue"));
    for(std::list<Functor>::iterator it = taskQueue.begin();
        it != taskQueue.end(); ++it)
    {
        *it();
    }
    return 0;
}

/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
    std::cout << "i = " << i << std::endl;
    std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
    std::cin >> should_increment;
    return 2;
}
void doSensitiveWork()
{
     ++i;
     should_increment = false;
}
class BaseCoroutine
{
public:
    BaseCoroutine(int stat): status(stat), waiting(false){}
    void operator()(){ status = perform(); }
    int getStatus() const { return status; }
protected:
    int status;
    bool waiting;
    virtual int perform() = 0;
    bool await_status(BaseCoroutine& other, int stat, int change)
    {
        if(!waiting)
        {
            waiting = true;
        }
        if(other.getStatus() == stat)
        {
            status = change;
            waiting = false;
        }
        return !waiting;
    }
}

class MyCoroutine1: public BaseCoroutine
{
public:
    MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
    BaseCoroutine& partner;
    virtual int perform()
    {
        if(getStatus() == 1)
            return doSomeWork();
        if(getStatus() == 2)
        {
            if(await_status(partner, 1))
                return 1;
            else if(i == 100)
                return 0;
            else
                return 2;
        }
    }
};

class MyCoroutine2: public BaseCoroutine
{
public:
    MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
    bool& work_signal;
    virtual int perform()
    {
        if(i == 100)
            return 0;
        if(work_signal)
        {
            doSensitiveWork();
            return 2;
        }
        return 1;
    }
};

int main()
{
     std::list<BaseCoroutine* > coroutineList;
     MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
     MyCoroutine1 *printer = new MyCoroutine1(incrementer);

     while(coroutineList.size())
     {
         for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
             it != coroutineList.end(); ++it)
         {
             *it();
             if(*it.getStatus() == 0)
             {
                 coroutineList.erase(it);
             }
         }
     }
     delete printer;
     delete incrementer;
     return 0;
}

ඇත්ත වශයෙන්ම, මෙම උදාහරණ තමන් තුළ එතරම් ප්‍රයෝජනවත් නොවේ. ඔවුන් පෙන්වන්නේ ෆන්ක්ටර්ස් ප්‍රයෝජනවත් වන්නේ කෙසේද යන්න පමණක් වන අතර, ෆන්ක්ටර්ස් ඉතා මූලික හා නම්‍යශීලී වන අතර මෙය ඒවා ප්‍රයෝජනවත් නොවන දේට වඩා අඩු ප්‍රයෝජනවත් කරයි.


2

එකතු කිරීම සඳහා, විධාන රටාවට පවත්නා උරුම ක්‍රමයක් ගැලපීම සඳහා මම ක්‍රියාකාරී වස්තු භාවිතා කර ඇත; (OO පරමාදර්ශයේ සැබෑ OCP හි සුන්දරත්වය මට දැනුණු එකම ස්ථානය); අදාළ ශ්‍රිත ඇඩැප්ටර රටාව ද මෙහි එක් කරයි.

ඔබේ ක්‍රමයට අත්සන තිබේ යැයි සිතමු:

int CTask::ThreeParameterTask(int par1, int par2, int par3)

විධාන රටාව සඳහා අපට එය ගැලපෙන්නේ කෙසේදැයි අපි බලමු - මේ සඳහා, පළමුව, ඔබ සාමාජික ශ්‍රිත ඇඩැප්ටරයක් ​​ලිවිය යුතු අතර එමඟින් එය ක්‍රියාකාරී වස්තුවක් ලෙස හැඳින්විය හැකිය.

සටහන - මෙය කැතයි, ඔබට බූස්ට් බයින්ඩ් උදව්කරුවන් ආදිය භාවිතා කළ හැකිය, නමුත් ඔබට නොහැකි නම් හෝ අවශ්‍ය නොවන්නේ නම් මෙය එක් ක්‍රමයකි.

// a template class for converting a member function of the type int        function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
  public:
explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
    :m_Ptr(_Pm) //okay here we store the member function pointer for later use
    {}

//this operator call comes from the bind method
_Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
{
    return ((_P->*m_Ptr)(arg1,arg2,arg3));
}
private:
_Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};

එසේම, ඇමතීමට සහාය වීම සඳහා ඉහත පන්තියට අපට උපකාරක ක්‍රමයක් mem_fun3 අවශ්‍ය වේ.

template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3> mem_fun3 ( _Ret (_Class::*_Pm)          (_arg1,_arg2,_arg3) )
{
  return (mem_fun3_t<_Ret,_Class,_arg1,_arg2,_arg3>(_Pm));

}

දැන්, පරාමිතීන් බන්ධනය කිරීම සඳහා, අපට බන්ධන ශ්‍රිතයක් ලිවිය යුතුය. ඉතින්, මෙන්න එය යන්නේ:

template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
//This is the constructor that does the binding part
binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
    :m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}

 //and this is the function object 
 void operator()() const
 {
        m_fn(m_ptr,m1,m2,m3);//that calls the operator
    }
private:
    _Ptr m_ptr;
    _Func m_fn;
    _arg1 m1; _arg2 m2; _arg3 m3;
};

තවද, බින්ඩර් 3 පන්තිය භාවිතා කිරීම සඳහා උපකාරක ශ්‍රිතයක් - bind3:

//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
    return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}

දැන්, අපි මෙය විධාන පන්තිය සමඟ භාවිතා කළ යුතුය; පහත සඳහන් යතුරු ලියනය භාවිතා කරන්න:

typedef binder3<mem_fun3_t<int,T,int,int,int> ,T* ,int,int,int> F3;
//and change the signature of the ctor
//just to illustrate the usage with a method signature taking more than one parameter
explicit Command(T* pObj,F3* p_method,long timeout,const char* key,
long priority = PRIO_NORMAL ):
m_objptr(pObj),m_timeout(timeout),m_key(key),m_value(priority),method1(0),method0(0),
method(0)
{
    method3 = p_method;
}

මෙන්න ඔබ එය හඳුන්වන ආකාරය:

F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3( 
      &CTask::ThreeParameterTask), task1,2122,23 );

සටහන: f3 (); මෙම ක්‍රමය task1-> ThreeParameterTask (21,22,23) ලෙස හඳුන්වනු ඇත.

මෙම රටාවේ සම්පූර්ණ සන්දර්භය පහත සබැඳියෙන්


2

කාර්යයන් ක්‍රියාකාරීව ක්‍රියාත්මක කිරීමේ විශාල වාසියක් නම්, ඇමතුම් අතර තත්වය පවත්වා ගැනීමට සහ නැවත භාවිතා කිරීමට ඔවුන්ට හැකි වීමයි. උදාහරණයක් ලෙස, වැග්නර්-ෆිෂර් ඇල්ගොරිතම වැනි බොහෝ ගතික ක්‍රමලේඛන ඇල්ගොරිතම ලෙවන්ස්ටයින් දුර ගණනය සඳහා නූල් අතර වගුවක් පුරවා ක්‍රියා කරයි. ශ්‍රිතය හැඳින්වෙන සෑම අවස්ථාවකම මෙම වගුව වෙන් කිරීම ඉතා අකාර්යක්ෂම වේ, එබැවින් ශ්‍රිතය ක්‍රියාකාරී ලෙස ක්‍රියාත්මක කිරීම සහ වගුව සාමාජික විචල්‍යයක් බවට පත් කිරීම මඟින් කාර්ය සාධනය වැඩි දියුණු කළ හැකිය.

පහත දැක්වෙන්නේ වැග්නර්-ෆිෂර් ඇල්ගොරිතම ක්‍රියාකාරී ලෙස ක්‍රියාත්මක කිරීමේ උදාහරණයකි. ඉදිකිරීම්කරු තුළ වගුව වෙන් කර ඇති ආකාරය සැලකිල්ලට ගෙන අවශ්‍ය ප්‍රමාණයට වඩා operator()ප්‍රමාණය වෙනස් කර නැවත භාවිතා කරන්න .

#include <string>
#include <vector>
#include <algorithm>

template <typename T>
T min3(const T& a, const T& b, const T& c)
{
   return std::min(std::min(a, b), c);
}

class levenshtein_distance 
{
    mutable std::vector<std::vector<unsigned int> > matrix_;

public:
    explicit levenshtein_distance(size_t initial_size = 8)
        : matrix_(initial_size, std::vector<unsigned int>(initial_size))
    {
    }

    unsigned int operator()(const std::string& s, const std::string& t) const
    {
        const size_t m = s.size();
        const size_t n = t.size();
        // The distance between a string and the empty string is the string's length
        if (m == 0) {
            return n;
        }
        if (n == 0) {
            return m;
        }
        // Size the matrix as necessary
        if (matrix_.size() < m + 1) {
            matrix_.resize(m + 1, matrix_[0]);
        }
        if (matrix_[0].size() < n + 1) {
            for (auto& mat : matrix_) {
                mat.resize(n + 1);
            }
        }
        // The top row and left column are prefixes that can be reached by
        // insertions and deletions alone
        unsigned int i, j;
        for (i = 1;  i <= m; ++i) {
            matrix_[i][0] = i;
        }
        for (j = 1; j <= n; ++j) {
            matrix_[0][j] = j;
        }
        // Fill in the rest of the matrix
        for (j = 1; j <= n; ++j) {
            for (i = 1; i <= m; ++i) {
                unsigned int substitution_cost = s[i - 1] == t[j - 1] ? 0 : 1;
                matrix_[i][j] =
                    min3(matrix_[i - 1][j] + 1,                 // Deletion
                    matrix_[i][j - 1] + 1,                      // Insertion
                    matrix_[i - 1][j - 1] + substitution_cost); // Substitution
            }
        }
        return matrix_[m][n];
    }
};

1

ශ්‍රිතයක් තුළ දේශීය ශ්‍රිතයක් අර්ථ දැක්වීම අනුකරණය කිරීමට ද Functor භාවිතා කළ හැකිය. ප්‍රශ්නය සහ තවත් එකක් වෙත යොමු වන්න .

නමුත් දේශීය විනෝදකරුවෙකුට පිටත ස්වයංක්‍රීය විචල්‍යයන්ට ප්‍රවේශ විය නොහැක. ලැම්බඩා (සී ++ 11) ශ්‍රිතය වඩා හොඳ විසඳුමකි.


-10

මම ඉතා රසවත් විනෝදකාමී භාවිතයක් "සොයාගෙන" ඇත: එක් ක්‍රමයක් සඳහා හොඳ නමක් නොමැති විට මම ඒවා භාවිතා කරමි.


විනෝදකාමී යන්ත්‍රයක් “නමක් නැති ක්‍රමයක්” ලෙස ඔබ විස්තර කරන්නේ ඇයි?
ඇන්ඩර්සන් ග්‍රීන්

5
නමක් නොමැති ශ්‍රිතයක් ලැම්බඩා ලෙස හැඳින්වේ.
පෝල් ෆුල්ට්ස් II
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.