C ++ 11 में पुनरावर्ती लैम्ब्डा कार्य करता है


143

मैं C ++ 11 में नया हूं। मैं निम्नलिखित पुनरावर्ती लंबा फ़ंक्शन लिख रहा हूं, लेकिन यह संकलन नहीं करता है।

sum.cpp

#include <iostream>
#include <functional>

auto term = [](int a)->int {
  return a*a;
};

auto next = [](int a)->int {
  return ++a;
};

auto sum = [term,next,&sum](int a, int b)mutable ->int {
  if(a>b)
    return 0;
  else
    return term(a) + sum(next(a),b);
};

int main(){
  std::cout<<sum(1,10)<<std::endl;
  return 0;
}

संकलन त्रुटि:

विमल @ linux-718q: ~ / अध्ययन / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp

sum.cpp: लंबो फ़ंक्शन में: sum.cpp: 18: 36: error: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum' को फ़ंक्शन के रूप में उपयोग नहीं किया जा सकता है

जीसीसी संस्करण

gcc संस्करण 4.5.0 20091231 (प्रायोगिक) (GCC)

लेकिन अगर मैं sum()नीचे की घोषणा को बदल देता हूं , तो यह काम करता है:

std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
   if(a>b)
     return 0;
   else
     return term(a) + sum(next(a),b);
};

क्या कोई इस पर प्रकाश डाल सकता है?


क्या यह स्थैतिक बनाम अंतर्निहित गतिशील घोषणाएं हो सकती हैं?
हमीश ग्रुबीजन

3
क्या है mutableकीवर्ड वहाँ क्या कर रही?
चीयर्स एंड हीथ। - अल्फ

गैर-स्वचालित संग्रहण अवधि वाले चर को कैप्चर करने की अनुमति नहीं है। आपको इसे इस तरह करना चाहिए: chat.stackoverflow.com/transcript/message/39298544#39298544
यूरी पिनहोल

बस एक FYI करें, अपने दूसरे कोड में आपका std::function<int(int,int)> sum = [&](int a, int b) {
लंबू

जवाबों:


189

ऑटो संस्करण और पूरी तरह से निर्दिष्ट प्रकार संस्करण के बीच अंतर के बारे में सोचें । ऑटो कीवर्ड infers अपने प्रकार जो कुछ भी से इसके साथ प्रारंभ है, लेकिन क्या आप को पता है कि अपने प्रकार है जरूरतों के साथ यह आरंभ कर रहे हैं (इस मामले में, लैम्ब्डा बंद जरूरतों प्रकार यह खींचती है पता करने के लिए)। चिकन और अंडे की समस्या के कुछ।

दूसरी ओर, पूरी तरह से निर्दिष्ट फ़ंक्शन ऑब्जेक्ट के प्रकार को इसके बारे में कुछ भी "पता" करने की आवश्यकता नहीं है कि इसे क्या सौंपा जा रहा है, और इसलिए लंबोदर के बंद होने के प्रकार पूरी तरह से इसके कैप्चरिंग के बारे में सूचित किया जा सकता है।

अपने कोड के इस मामूली संशोधन पर विचार करें और यह अधिक समझ में आ सकता है:

std::function<int(int,int)> sum;
sum = [term,next,&sum](int a, int b)->int {
if(a>b)
    return 0;
else
    return term(a) + sum(next(a),b);
};

जाहिर है, यह ऑटो के साथ काम नहीं करेगा । रिकर्सिव लैम्ब्डा फ़ंक्शन पूरी तरह से अच्छी तरह से काम करते हैं (कम से कम वे एमएसवीसी में करते हैं, जहां मुझे उनके साथ अनुभव है), यह सिर्फ इतना है कि वे वास्तव में टाइप इंट्रेंस के साथ संगत नहीं हैं।


3
मैं इससे सहमत नहीं हूं। लंबोदा का प्रकार अच्छी तरह से जाना जाता है जैसे ही फ़ंक्शन बॉडी में प्रवेश किया जाता है- कोई कारण नहीं है कि तब तक इसे घटाया नहीं जाना चाहिए।
पिल्ला

16
@DeadMG लेकिन इसके आरंभक में autoचर का जिक्र करते हुए युक्ति निषिद्ध है। ऑटो वैरिएबल का प्रकार अभी तक ज्ञात नहीं है जब इनिशलाइज़र को संसाधित किया जा रहा है।
जोहान्स स्काउब -

1
आश्चर्य है कि इसे 'उत्तर' के रूप में चिह्नित क्यों नहीं किया गया है, और उस पायथन को 'उत्तर' के रूप में वर्गीकृत किया गया है?
अजय

1
@Puppy: एक अंतर्निहित कैप्चर के मामले में, हालांकि, दक्षता के लिए केवल संदर्भित चर वास्तव में कैप्चर किए जाते हैं, इसलिए शरीर को पार्स किया जाना चाहिए।
23

क्या इसके sumअलावा अन्य के लिए एक वैध व्याख्या है std::function<int(int, int)>, या सी ++ कल्पना है जो इसे पता लगाने के लिए परेशान नहीं है?
मतीन उल्हाक

79

चाल लंबर कार्यान्वयन में एक पैरामीटर के रूप में खुद को खिलाने के लिए है , न कि कैप्चर द्वारा।

const auto sum = [term,next](int a, int b) {
  auto sum_impl=[term,next](int a,int b,auto& sum_ref) mutable {
    if(a>b){
      return 0;
    }
    return term(a) + sum_ref(next(a),b,sum_ref);
  };
  return sum_impl(a,b,sum_impl);
};

कंप्यूटर विज्ञान में सभी समस्याओं को एक अन्य स्तर के अप्रत्यक्ष रूप से हल किया जा सकता है । मैंने पहली बार http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/ पर यह आसान ट्रिक पाया।

यह है सी ++ 14 की आवश्यकता होती है, जबकि प्रश्न सी ++ 11 पर है, लेकिन शायद सबसे दिलचस्प।

के माध्यम से std::functionजाना भी संभव है लेकिन धीमी कोड में परिणाम हो सकता है । लेकिन हमेशा नहीं। Std :: function बनाम टेम्पलेट के उत्तरों पर एक नज़र डालें


यह सिर्फ C ++ के बारे में एक ख़ासियत नहीं है, यह सीधे लैम्ब्डा कैलकुलस के गणित की मैपिंग है। से विकिपीडिया :

Lambda calculus cannot express this as directly as some other notations:
all functions are anonymous in lambda calculus, so we can't refer to a
value which is yet to be defined, inside the lambda term defining that
same value. However, recursion can still be achieved by arranging for a
lambda expression to receive itself as its argument value

3
यह स्पष्ट रूप से उपयोग करने की तुलना में बहुत बुरा लगता है function<>। मैं यह नहीं देख सकता कि कोई इसे क्यों पसंद करेगा। संपादित करें: यह स्पष्ट रूप से तेज़ है।
टिम्मम

17
यह std से बेहतर है :: 3 कारणों से कार्य: इसे प्रकार के क्षरण या स्मृति आवंटन की आवश्यकता नहीं होती है, यह बाधा हो सकती है और यह ऑटो (टेम्पर्ड) मापदंडों / रिटर्न प्रकार के साथ ठीक से काम करती है
इवान सनज़-कारसा

3
संभवत: इस समाधान का भी std के बिना प्रतिलिपि योग्य होने का लाभ है :: फ़ंक्शन संदर्भ दायरे से बाहर जा रहा है?
उड़ी ग्रांट

3
एचएम, जब कोशिश कर रहे हैं, जीसीसी 8.1 (लिनक्स) ने शिकायत की: error: use of ‘[...]’ before deduction of ‘auto’- स्पष्ट रूप से रिटर्न प्रकार (दूसरी ओर, पारस्परिक रूप से आवश्यक नहीं है) को निर्दिष्ट करने की आवश्यकता है।
एकांकागुआ

@ Xcon10 के साथ ही यहाँ एस्कैगुआ और मैंने C ++ मानक को 17 तक सेट किया है
IceFire

39

C ++ 14 के साथ, अब अतिरिक्त ओवरहेड को उकेरने के बिना एक कुशल पुनरावर्ती लंबोदा बनाना काफी आसान है std::function, कोड की कुछ पंक्तियों में (मूल से एक छोटे से संपादन के साथ उपयोगकर्ता को आकस्मिक प्रतिलिपि लेने से रोकने के लिए ):

template <class F>
struct y_combinator {
    F f; // the lambda will be stored here

    // a forwarding operator():
    template <class... Args>
    decltype(auto) operator()(Args&&... args) const {
        // we pass ourselves to f, then the arguments.
        // [edit: Barry] pass in std::ref(*this) instead of *this
        return f(std::ref(*this), std::forward<Args>(args)...);
    }
};

// helper function that deduces the type of the lambda:
template <class F>
y_combinator<std::decay_t<F>> make_y_combinator(F&& f) {
    return {std::forward<F>(f)};
}

जिसके साथ आपका मूल sumप्रयास बन जाता है:

auto sum = make_y_combinator([term,next](auto sum, int a, int b) {
  if (a>b) {
    return 0;
  }
  else {
    return term(a) + sum(next(a),b);
  }
});

C ++ 17 में, CTAD के साथ, हम एक कटौती गाइड जोड़ सकते हैं:

template <class F> y_combinator(F) -> y_combinator<F>;

जो सहायक कार्य की आवश्यकता को पूरा करता है। हम बस y_combinator{[](auto self, ...){...}}सीधे लिख सकते हैं।


C ++ 20 में, समग्र के लिए CTAD के साथ, कटौती गाइड आवश्यक नहीं होगा।


यह बहुत अच्छा है, लेकिन अंतिम पंक्ति के std::forward<decltype(sum)>(sum)बजाय कोई भी विचार कर सकता है sum
जोहान लुंडबर्ग

@ जोहान नहीं, केवल एक ही है operator()ताकि फॉरवर्ड करने से कुछ हासिल न होsum
बैरी

ओह, यह सच है। बिना फॉरवर्ड के रेफरेंस का उपयोग करने के लिए उपयोग नहीं किया जाता है।
जोहान लुंडबर्ग

वाई-कॉम्बिनेटर निश्चित रूप से जाने का रास्ता है। लेकिन आपको वास्तव में एक गैर- constअधिभार जोड़ना चाहिए , बशर्ते कि फ़ंक्शन-ऑब्जेक्ट में गैर const-कॉल-ऑपरेटर हो। और SFINAE का उपयोग करें और noexceptदोनों के लिए गणना करें । इसके अलावा, C ++ 17 में अब मेकर-फंक्शन की कोई आवश्यकता नहीं है।
Deduplicator

2
@minex हां, auto sumप्रतियां ... लेकिन यह एक कॉपी करता है reference_wrapper, जो संदर्भ लेने के समान ही है। कार्यान्वयन में एक बार ऐसा करने का मतलब है कि कोई भी उपयोग कभी भी गलती से कॉपी नहीं होगा।
बैरी

22

मेरे पास एक और उपाय है, लेकिन केवल स्टेटलेस लैम्ब्डा के साथ काम करें:

void f()
{
    static int (*self)(int) = [](int i)->int { return i>0 ? self(i-1)*i : 1; };
    std::cout<<self(10);
}

यहां ट्रिक यह है कि लैम्ब्डा स्टेटिक वैरिएबल तक पहुंच सकता है और आप स्टेटलेस को फंक्शन पॉइंटर में बदल सकते हैं।

आप इसे मानक लंबोदा के साथ उपयोग कर सकते हैं:

void g()
{
    int sum;
    auto rec = [&sum](int i) -> int
    {
        static int (*inner)(int&, int) = [](int& _sum, int i)->int 
        {
            _sum += i;
            return i>0 ? inner(_sum, i-1)*i : 1; 
        };
        return inner(sum, i);
    };
}

जीसीसी 4.7 में इसका काम


3
इसका std :: function से बेहतर प्रदर्शन होना चाहिए, इसलिए विकल्प के लिए +1। लेकिन वास्तव में, इस बिंदु पर मुझे आश्चर्य है कि अगर लैम्ब्डा का उपयोग करना सबसे अच्छा विकल्प है;)
एंटोनी

यदि आपके पास एक स्टेटलेस लैम्ब्डा है, तो आप इसे केवल एक पूर्ण कार्य कर सकते हैं।
टिममम

1
@Timmmm लेकिन फिर आप बाहरी शब्द पर अमल का हिस्सा लीक कर देते हैं, आमतौर पर लंबोदर को पैरेंट फंक्शन (आउट कैप्टर्स के साथ भी) के साथ जोड़ा जाता है। यदि यह मामला नहीं था, तो आपको पहले स्थान पर लैम्ब्डा का उपयोग नहीं करना चाहिए और फंक्शनलर्स के सामान्य कार्यों का उपयोग करना चाहिए।
यैंकेज

10

आप एक लंबो फंक्शन कॉल को अपने आप को पुनरावर्ती बना सकते हैं। केवल एक चीज जो आपको करने की ज़रूरत है वह यह है कि इसे एक फ़ंक्शन रैपर के माध्यम से संदर्भित किया जाए ताकि कंपाइलर जानता है कि यह रिटर्न और तर्क प्रकार है (आप एक चर को पकड़ नहीं सकते हैं - लैम्ब्डा ही - जिसे अभी तक परिभाषित नहीं किया गया है) ।

  function<int (int)> f;

  f = [&f](int x) {
    if (x == 0) return 0;
    return x + f(x-1);
  };

  printf("%d\n", f(10));

बहुत सावधान रहें कि रैपर f के दायरे से बाहर न भागें।


3
लेकिन, यह स्वीकृत उत्तर के समान है, और एसटीडी फ़ंक्शन का उपयोग करने के लिए जुर्माना हो सकता है।
जोहान लुंडबर्ग

9

बाहरी वर्गों और कार्यों (जैसे std::functionया फिक्स्ड-पॉइंट कॉम्बिनेटर) का उपयोग किए बिना लैम्ब्डा को पुनरावर्ती बनाने के लिए , C ++ 14 ( लाइव उदाहरण ) में निम्न निर्माण का उपयोग कर सकते हैं :

#include <utility>
#include <list>
#include <memory>
#include <iostream>

int main()
{
    struct tree
    {
        int payload;
        std::list< tree > children = {}; // std::list of incomplete type is allowed
    };
    std::size_t indent = 0;
    // indication of result type here is essential
    const auto print = [&] (const auto & self, const tree & node) -> void
    {
        std::cout << std::string(indent, ' ') << node.payload << '\n';
        ++indent;
        for (const tree & t : node.children) {
            self(self, t);
        }
        --indent;
    };
    print(print, {1, {{2, {{8}}}, {3, {{5, {{7}}}, {6}}}, {4}}});
}

प्रिंट:

1
 2
  8
 3
  5
   7
  6
 4

ध्यान दें, परिणाम प्रकार के लंबो को स्पष्ट रूप से निर्दिष्ट किया जाना चाहिए।


6

मैंने std::function<>कैप्चर विधि का उपयोग करके एक पुनरावर्ती फ़ंक्शन बनाम एक पुनरावर्ती लंबोदा फ़ंक्शन की तुलना करने वाले बेंचमार्क को चलाया । क्लैंग संस्करण 4.1 पर सक्षम पूर्ण अनुकूलन के साथ, लैम्ब्डा संस्करण काफी धीमा चला गया।

#include <iostream>
#include <functional>
#include <chrono>

uint64_t sum1(int n) {
  return (n <= 1) ? 1 : n + sum1(n - 1);
}

std::function<uint64_t(int)> sum2 = [&] (int n) {
  return (n <= 1) ? 1 : n + sum2(n - 1);
};

auto const ITERATIONS = 10000;
auto const DEPTH = 100000;

template <class Func, class Input>
void benchmark(Func&& func, Input&& input) {
  auto t1 = std::chrono::high_resolution_clock::now();
  for (auto i = 0; i != ITERATIONS; ++i) {
    func(input);
  }
  auto t2 = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(t2-t1).count();
  std::cout << "Duration: " << duration << std::endl;
}

int main() {
  benchmark(sum1, DEPTH);
  benchmark(sum2, DEPTH);
}

परिणाम उत्पन्न करता है:

Duration: 0 // regular function
Duration: 4027 // lambda function

(नोट: मैंने एक ऐसे संस्करण से भी पुष्टि की है जो सिनेमा से इनपुट लेता है, ताकि संकलन समय मूल्यांकन समाप्त हो सके)

क्लैंग एक संकलक चेतावनी भी पैदा करता है:

main.cc:10:29: warning: variable 'sum2' is uninitialized when used within its own initialization [-Wuninitialized]

जो अपेक्षित है, और सुरक्षित है, लेकिन ध्यान दिया जाना चाहिए।

हमारे टूलबेल में समाधान होना बहुत अच्छा है, लेकिन मुझे लगता है कि इस मामले को संभालने के लिए भाषा को एक बेहतर तरीके की आवश्यकता होगी यदि प्रदर्शन को वर्तमान तरीकों के साथ तुलना करना है।

ध्यान दें:

जैसा कि एक टिप्पणीकार ने कहा है, ऐसा लगता है कि VC ++ के नवीनतम संस्करण ने समान प्रदर्शन के बिंदु पर इसे अनुकूलित करने का एक तरीका खोज लिया है। हो सकता है कि हमें इसे संभालने के लिए एक बेहतर तरीके की आवश्यकता नहीं है, सब के बाद (सिंटैक्टिक शुगर को छोड़कर)।

इसके अलावा, जैसा कि हाल के सप्ताहों में कुछ अन्य एसओ पोस्टों की रूपरेखा तैयार की गई है, std::function<>स्वयं का प्रदर्शन मंदी बनाम कॉलिंग फ़ंक्शन का कारण हो सकता है, कम से कम जब लैंबडा कैप्चर बहुत बड़ा हो तो कुछ लाइब्रेरी-ऑप्टिमाइज़्ड स्पेस में std::functionछोटे-फंक्शनलर्स के लिए फिट होने के लिए (मुझे लगता है कि थोड़े अलग स्ट्रिंग अनुकूलन की तरह थोड़े?)।


2
-1। ध्यान दें कि "लैम्ब्डा" संस्करण का एकमात्र कारण अधिक समय लगता है क्योंकि आप इसे एक std :: function से बाँधते हैं, जो ऑपरेटर () को एक वर्चुअल कॉल करता है, और यह स्पष्ट रूप से अधिक समय लेगा। उसके शीर्ष पर, VS2012 रिलीज़ मोड में, आपके कोड ने दोनों मामलों में समान समय लिया।
यम मारकोविच

@YamMarcovic क्या है? यह वर्तमान में एक पुनरावर्ती लंबोदा लिखने का एकमात्र ज्ञात तरीका है (यह उदाहरण का बिंदु था)। मुझे यह जानकर बहुत खुशी हुई कि VS2012 ने इस उपयोग के मामले को अनुकूलित करने का एक तरीका खोज लिया है (हालांकि हाल ही में इस विषय पर और अधिक घटनाक्रम हुए हैं, जाहिर है कि अगर मेरा लंबोदर ने अधिक कब्जा कर लिया होता, तो यह std के भीतर फिट नहीं होता :: फ़ंक्शन छोटा- मेमोरी फ़न्टर ऑप्टिमाइज़ेशन या व्हाट्सएप)।
mmocny

2
को स्वीकार किया। मैंने आपकी पोस्ट को गलत समझा। फिर +1। यदि आप इस उत्तर को संपादित करते हैं तो Gah, केवल उत्थान कर सकता है। तो क्या आप इसे थोड़ा और जोर दे सकते हैं, जैसे कि टिप्पणी में?
यम मार्कोविच

1
@YamMarcovic ने किया। मैं प्रतिक्रिया देने और जरूरत पड़ने पर इसे परिष्कृत करने की आपकी इच्छा की सराहना करता हूं। आप को अच्छा, सर।
mmocny

0 बार आम तौर पर इसका मतलब है "पूरे ऑपरेशन को दूर अनुकूलित किया गया था"। यदि संकलक साबित होता है कि आप अपनी गणना के पुनर्विक्रय के साथ कुछ नहीं करते हैं, तो सिनेमा से इनपुट लेना कुछ भी नहीं है।
यक्क - एडम नेवरामोंट

1

यह फिक्सपॉइंट ऑपरेटर का थोड़ा सरल कार्यान्वयन है जो इसे थोड़ा और स्पष्ट करता है कि वास्तव में क्या चल रहा है।

#include <iostream>
#include <functional>

using namespace std;

template<typename T, typename... Args>
struct fixpoint
{
    typedef function<T(Args...)> effective_type;
    typedef function<T(const effective_type&, Args...)> function_type;

    function_type f_nonr;

    T operator()(Args... args) const
    {
        return f_nonr(*this, args...);
    }

    fixpoint(const function_type& p_f)
        : f_nonr(p_f)
    {
    }
};


int main()
{
    auto fib_nonr = [](const function<int(int)>& f, int n) -> int
    {
        return n < 2 ? n : f(n-1) + f(n-2);
    };

    auto fib = fixpoint<int,int>(fib_nonr);

    for (int i = 0; i < 6; ++i)
    {
        cout << fib(i) << '\n';
    }
}

मुझे लगता है कि यदि आप std::functionफ़ंक्शन पॉइंटर के साथ प्रतिस्थापित करते हैं (तो कोर केवल यह सामान्य फ़ंक्शन, और स्टेटलेस लैम्ब्डा के साथ काम करेगा ) आप अपने उत्तर (प्रदर्शन वार) में सुधार कर सकते हैं । Btw fib_nonrको स्वीकार करना चाहिए fixpoint<int,int>, यदि आप std::functionइसकी आवश्यकता का उपयोग करते हुए क्रेटिंग से नई प्रति प्राप्त करते हैं *this
येंकेस

1

यहाँ @Barry द्वारा प्रस्तावित एक पर आधारित वाई-कॉम्बिनेटर समाधान का एक परिष्कृत संस्करण है।

template <class F>
struct recursive {
  F f;
  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  const { return f(std::ref(*this), std::forward<Ts>(ts)...); }

  template <class... Ts>
  decltype(auto) operator()(Ts&&... ts)  { return f(std::ref(*this), std::forward<Ts>(ts)...); }
};

template <class F> recursive(F) -> recursive<F>;
auto const rec = [](auto f){ return recursive{std::move(f)}; };

इसका उपयोग करने के लिए, कोई निम्नलिखित कर सकता है

auto fib = rec([&](auto&& fib, int i) {
// implementation detail omitted.
});

यह let recOCaml में कीवर्ड के समान है , हालांकि समान नहीं है।


0

C ++ 14: यहाँ एक पुनरावर्ती अनाम स्टेटलेस / लैम्ब्डा का कोई कैप्चर जेनेरिक सेट है जो 1, 20 से सभी नंबरों को आउटपुट करता है।

([](auto f, auto n, auto m) {
    f(f, n, m);
})(
    [](auto f, auto n, auto m) -> void
{
    cout << typeid(n).name() << el;
    cout << n << el;
    if (n<m)
        f(f, ++n, m);
},
    1, 20);

अगर मैं सही ढंग से समझूं तो यह वाई-कॉम्बिनेटर समाधान का उपयोग कर रहा है

और यहाँ योग (n, m) संस्करण है

auto sum = [](auto n, auto m) {
    return ([](auto f, auto n, auto m) {
        int res = f(f, n, m);
        return res;
    })(
        [](auto f, auto n, auto m) -> int
        {
            if (n > m)
                return 0;
            else {
                int sum = n + f(f, n + 1, m);
                return sum;
            }
        },
        n, m); };

auto result = sum(1, 10); //result == 55

-1

यहाँ ओपी के लिए अंतिम जवाब है। वैसे भी, Visual Studio 2010 वैश्विक चर कैप्चरिंग का समर्थन नहीं करता है। और आपको उन्हें पकड़ने की आवश्यकता नहीं है क्योंकि वैश्विक चर परिभाषित द्वारा विश्व स्तर पर पहुंच योग्य है। निम्न उत्तर इसके बजाय स्थानीय चर का उपयोग करता है।

#include <functional>
#include <iostream>

template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V1, typename V2>
struct fixpoint
{
    typedef std::function<R (V1, V2)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V1 Parameter1_t;
        typedef V2 Parameter2_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V1, typename V2>
typename fixpoint<R, V1, V2>::yfunc_t fixpoint<R, V1, V2>::fix = [](tfunc_t f) -> func_t {
    return [f](fixpoint<R, V1, V2>::loopfunc_t x){  return f(x(x)); }
    ([f](fixpoint<R, V1, V2>::loopfunc_t x) -> fixpoint<R, V1, V2>::func_t{
        auto &ff = f;
        return [ff, x](t2t<decltype(x)>::t::Parameter1_t v1, 
            t2t<decltype(x)>::t::Parameter1_t v2){
            return ff(x(x))(v1, v2);
        }; 
    });
};

int _tmain(int argc, _TCHAR* argv[])
{
    auto term = [](int a)->int {
      return a*a;
    };

    auto next = [](int a)->int {
      return ++a;
    };

    auto sum = fixpoint<int, int, int>::fix(
    [term,next](std::function<int (int, int)> sum1) -> std::function<int (int, int)>{
        auto &term1 = term;
        auto &next1 = next;
        return [term1, next1, sum1](int a, int b)mutable ->int {
            if(a>b)
                return 0;
        else
            return term1(a) + sum1(next1(a),b);
        };
    });

    std::cout<<sum(1,10)<<std::endl; //385

    return 0;
}

क्या इस उत्तर संकलनकर्ता को अज्ञेय बनाना संभव है?
रेय्यरेंग

-2

आप परिभाषित करने के बीच में एक चर (राशि) को पकड़ने की कोशिश कर रहे हैं। यह अच्छा नहीं हो सकता।

मुझे नहीं लगता कि वास्तव में आत्म-पुनरावर्ती C ++ 0x लंब्बास संभव हैं। यद्यपि आपको अन्य लंबोदर को पकड़ने में सक्षम होना चाहिए, हालांकि।


3
लेकिन यह काम करता है अगर राशि की घोषणा 'ऑटो' से बदलकर std :: function <int (int, int)> पर कब्जा सूची को बदले बिना।
वीमा

क्योंकि यह अब एक लंबोदर नहीं है, लेकिन एक समारोह जिसका उपयोग लंबोदर के स्थान पर किया जा सकता है?
हमीश ग्रुबीजन

-2

यह उत्तर याँक्स के एक के लिए अवर है, लेकिन फिर भी, यहाँ यह जाता है:

using dp_type = void (*)();

using fp_type = void (*)(dp_type, unsigned, unsigned);

fp_type fp = [](dp_type dp, unsigned const a, unsigned const b) {
  ::std::cout << a << ::std::endl;
  return reinterpret_cast<fp_type>(dp)(dp, b, a + b);
};

fp(reinterpret_cast<dp_type>(fp), 0, 1);

मुझे लगता है कि आपको बचना चाहिए reinterpret_cast। संभवतः आपके मामले में सबसे अच्छा तरीका कुछ संरचना है जो प्रतिस्थापित करता है dp_type। इसमें फील्ड होना चाहिए fp_type, से निर्माण किया जा सकता है fp_typeऔर ()जैसे तर्कों के साथ ऑपरेटर हो सकता है fp_type। यह पास std::functionहोगा, लेकिन स्व-संदर्भित तर्क की अनुमति देगा।
येंके

मैं एक न्यूनतम उदाहरण पोस्ट करना चाहता था, एक संरचना के बिना, मेरे उत्तर को संपादित करने और अधिक संपूर्ण समाधान प्रदान करने के लिए स्वतंत्र महसूस करें। ए structभी एक अतिरिक्त स्तर को अप्रत्यक्ष रूप से जोड़ देगा। उदाहरण काम करता है और कास्ट मानक-अनुरूप है, मुझे नहीं पता कि इसके -1लिए क्या था।
user1095108

नहीं, संरचना केवल सूचक के लिए कंटेनर के रूप में काम करेगी और मूल्य के रूप में पारित हो जाएगी। यह सूचक की तुलना में अधिक अप्रत्यक्ष या उपरि नहीं होगा। और -1मेरे बारे में मुझे नहीं पता था कि यह आपको कौन देता है, लेकिन मुझे लगता है कि इसका reinterpret_castउपयोग अंतिम उपाय के रूप में किया जाना चाहिए।
23

castमाना जाता है कि c ++ 11 मानक से काम करने के लिए गारंटी है। एक का उपयोग करते हुए structमेरी आँखों में, एक लैम्ब्डा वस्तु के उपयोग को हराने कर सकते हैं। आखिरकार, structआप प्रस्ताव करते हैं कि एक फनकार, एक लैम्ब्डा ऑब्जेक्ट का उपयोग कर रहा है।
user1095108

@Pseudonym solution को देखें, केवल हटा दें std::functionऔर आपके पास कुछ ऐसा होगा जो मेरे मन में था। यह संभवतः आपके समाधान के समान प्रदर्शन होगा।
येंकेस

-3

आपको एक निश्चित बिंदु कॉम्बिनेटर की आवश्यकता है। देखें इस

या निम्नलिखित कोड को देखें:

//As decltype(variable)::member_name is invalid currently, 
//the following template is a workaround.
//Usage: t2t<decltype(variable)>::t::member_name
template<typename T>
struct t2t
{
    typedef T t;
};

template<typename R, typename V>
struct fixpoint
{
    typedef std::function<R (V)> func_t;
    typedef std::function<func_t (func_t)> tfunc_t;
    typedef std::function<func_t (tfunc_t)> yfunc_t;

    class loopfunc_t {
    public:
        func_t operator()(loopfunc_t v)const {
            return func(v);
        }
        template<typename L>
        loopfunc_t(const L &l):func(l){}
        typedef V Parameter_t;
    private:
        std::function<func_t (loopfunc_t)> func;
    };
    static yfunc_t fix;
};
template<typename R, typename V>
typename fixpoint<R, V>::yfunc_t fixpoint<R, V>::fix = 
[](fixpoint<R, V>::tfunc_t f) -> fixpoint<R, V>::func_t {
    fixpoint<R, V>::loopfunc_t l = [f](fixpoint<R, V>::loopfunc_t x) ->
        fixpoint<R, V>::func_t{
            //f cannot be captured since it is not a local variable
            //of this scope. We need a new reference to it.
            auto &ff = f;
            //We need struct t2t because template parameter
            //V is not accessable in this level.
            return [ff, x](t2t<decltype(x)>::t::Parameter_t v){
                return ff(x(x))(v); 
            };
        }; 
        return l(l);
    };

int _tmain(int argc, _TCHAR* argv[])
{
    int v = 0;
    std::function<int (int)> fac = 
    fixpoint<int, int>::fix([](std::function<int (int)> f)
        -> std::function<int (int)>{
        return [f](int i) -> int{
            if(i==0) return 1;
            else return i * f(i-1);
        };
    });

    int i = fac(10);
    std::cout << i; //3628800
    return 0;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.