সি ++ 11 এ পুনরাবৃত্ত ল্যাম্বদা ফাংশন


143

আমি সি ++ 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;
}

সংকলন ত্রুটি:

ভিমাল @ লিনাক্স -718 ক: Study / অধ্যয়ন / 09 সি ++ / সি ++ 0x / ল্যাম্বদা> জি ++ -এসডিডি = সি ++ 0x সামিপিপি

Sum.cpp: ল্যাম্বদা ফাংশনে: sum.cpp: 18: 36: ত্রুটি: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum' ফাংশন হিসাবে ব্যবহার করা যায় না

জিসিসি সংস্করণ

জিসিসি সংস্করণ 4.5.0 20091231 (পরীক্ষামূলক) (জিসিসি)

তবে আমি যদি 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
ইউরি পিনহলো

ঠিক একটি এফওয়াইআই, আপনার দ্বিতীয় কোড স্নিপেটে আপনার 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
@ ডেড এমজি তবে অনুমানটি autoএর প্রারম্ভকালে চলকের উল্লেখ করতে নিষেধ করে। প্রারম্ভকালীন প্রক্রিয়াজাতকরণের সময় অটো ভেরিয়েবলের ধরণটি এখনও জানা যায়নি।
জোহানেস স্কাউব - লিটব

1
ভাবছেন কেন এটিকে 'উত্তর' হিসাবে চিহ্নিত করা হয়নি, এবং পাইথনটিকে 'উত্তর' হিসাবে শ্রেণীবদ্ধ করা হয়েছে ?!
অজয়

1
@ পপি: অন্তর্নিহিত ক্যাপচারের ক্ষেত্রে, যদিও দক্ষতার জন্য কেবলমাত্র রেফারেন্সড ভেরিয়েবলগুলি ক্যাপচার করা হয়, তাই শরীরকে অবশ্যই বিশ্লেষণ করতে হবে।
কেইকে

এর বাইরে কি 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সম্ভব কিন্তু করতে ধীর কোড স্থাপিত। তবে সব সময় নয়. স্ট্যান্ডার্ড :: ফাংশন বনাম টেম্পলেটগুলির উত্তরগুলি একবার দেখুন


এটি কেবলমাত্র সি ++ সম্পর্কে বিশেষত্ব নয়, এটি ল্যাম্বডা ক্যালকুলাসের গণিতে সরাসরি ম্যাপিং। উইকিপিডিয়া থেকে :

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
এটি 3 টি কারণে স্ট্যান্ড :: ফাংশনের চেয়ে আরও ভাল: এটির ধরণ বা মেমরি বরাদ্দ প্রয়োজন হয় না, এটি কনটেক্সপ্রপ হতে পারে এবং এটি অটো (টেম্প্লেটেড) পরামিতি / রিটার্ন টাইপের সাথে সঠিকভাবে কাজ করে
ইভান সান্জ-কারাসা

3
সম্ভবত এই সমাধানটিও স্ট্যান্ড :: ফাংশন রেফারেন্স ছাড়াই অনুলিপিযোগ্য হওয়ার সুবিধা আছে?
উরি গ্রান্টা

3
এইচএম, চেষ্টা করার সময়, জিসিসি 8.1 (লিনাক্স) অভিযোগ করেছে: error: use of ‘[...]’ before deduction of ‘auto’- স্পষ্টরূপে রিটার্নের ধরণ নির্দিষ্ট করতে হবে (অন্যদিকে, পরিবর্তনের প্রয়োজন নেই)।
অ্যাকোনকাগুয়া

এক্সকোড 10 এর সাথে এখানেই অ্যাকনাকাগুয়া এবং আমি সি ++ স্ট্যান্ডার্ডটি 17 এমনকি
আইসফায়ার

39

সি ++ ১৪ এর সাহায্যে, std::functionকেবল কয়েকটি লাইন কোডে (ব্যবহারকারীকে দুর্ঘটনাকৃত অনুলিপি আটকাতে আসল থেকে একটি ছোট সম্পাদনা করে) অতিরিক্ত ওভারহেড ব্যয় না করে একটি দক্ষ পুনরাবৃত্ত লাম্বদা তৈরি করা এখন বেশ সহজ is ):

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);
  }
});

সিটিএডের সাথে সি ++ 17 এ আমরা একটি ছাড়ের গাইড যোগ করতে পারি:

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

যা সহায়ক ফাংশনটির প্রয়োজনকে প্রতিপন্ন করে। আমরা কেবল y_combinator{[](auto self, ...){...}}সরাসরি লিখতে পারি ।


সমষ্টিগুলির জন্য সিটিএডের সাথে সি ++ ২০ এ, ছাড়ের গাইডের প্রয়োজন হবে না।


এটি দুর্দান্ত, তবে শেষের লাইনের std::forward<decltype(sum)>(sum)পরিবর্তে কেউ বিবেচনা করতে পারে sum
জোহান লন্ডবার্গ

@ জোহান না, কেবলমাত্র একটি আছে operator()তাই ফরোয়ার্ড করে লাভ করার কিছুই নেইsum
ব্যারি

ওহ, এটা সত্য। ফরোয়ার্ড ছাড়াই ফরওয়ার্ডিং রেফারেন্স ব্যবহার করতে অভ্যস্ত না।
জোহান লন্ডবার্গ

ওয়াই-সংযুক্তকারী অবশ্যই যাওয়ার উপায় the তবে constপ্রদত্ত ফাংশন-অবজেক্টটিতে একটি constকল-অপারেটর না থাকলে আপনার সত্যই একটি অ- ওভারলোড যুক্ত করা উচিত । এবং SFINAE এবং noexceptউভয়ের জন্য গণিত ব্যবহার করুন। এছাড়াও, আর সি ++ 17-এ মেকার-ফাংশনের প্রয়োজন নেই।
উত্সাহক

2
@ মিনেক্স হ্যাঁ, 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
এটিতে স্ট্যান্ড :: ফাংশনের চেয়ে আরও ভাল পারফরম্যান্স থাকতে হবে, সুতরাং বিকল্পটির জন্য +1 করুন। তবে সত্যই, এই মুহুর্তে আমি ভাবছি যে ল্যাম্বডাস ব্যবহার করা সবচেয়ে ভাল বিকল্প কিনা;)
এন্টোইন

আপনার যদি স্টেটলেস ল্যাম্বদা থাকে তবে আপনি এটি কেবল একটি সম্পূর্ণ ফাংশন করতে পারেন।
টিম্ম্ম্ম

1
@ টিএমএমএম তবে তারপরে আপনি প্রয়োগের কিছু অংশ বাইরের শব্দের কাছে ফাঁস করেন, সাধারণত ল্যাম্বডাস পিতামাতার ফাংশনটির সাথে ঘনিষ্ঠভাবে মিলিত হয় (এমনকি যখন ক্যাপচারের সাথেও থাকে)। যদি এটি না হয় তবে আপনার প্রথমে ল্যাম্বডাস ব্যবহার করা উচিত নয় এবং ফান্টারের সাধারণ ফাংশন ব্যবহার করা উচিত নয়।
ইয়াঙ্কস

10

আপনি নিজেকে পুনরাবৃত্তভাবে একটি ল্যাম্বদা ফাংশন কল করতে পারেন । কেবলমাত্র আপনাকে যা করতে হবে তা হ'ল এটি কোনও ফাংশন মোড়কের মাধ্যমে রেফারেন্স করা যাতে কম্পাইলার জানে যে এটির রিটার্ন এবং আর্গুমেন্ট টাইপ (আপনি কোনও পরিবর্তনশীল - ল্যাম্বডা নিজেই ক্যাপচার করতে পারবেন না - এটি এখনও সংজ্ঞায়িত হয়নি) ।

  function<int (int)> f;

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

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

র‍্যাপারের পরিধিটি যেন ছড়িয়ে না যায় সে সম্পর্কে খুব সতর্ক হন।


3
তবে, এটি গৃহীত উত্তরের অনুরূপ এবং স্ট্যান্ড ফাংশনটি ব্যবহারের জন্য এটির একটি জরিমানাও থাকতে পারে।
জোহান লন্ডবার্গ

9

বাহ্যিক ক্লাস এবং ফাংশন (যেমন std::functionবা ফিক্সড-পয়েন্ট সংযুক্তকারী) ব্যবহার না করে ল্যাম্বদা পুনরাবৃত্ত করার জন্য সি ++ 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<>ক্যাপচার পদ্ধতিটি ব্যবহার করে একটি পুনরাবৃত্ত ফাংশন বনাম একটি পুনরাবৃত্ত ল্যাম্বদা ফাংশনের সাথে তুলনা করে আমি একটি বেঞ্চমার্ক চালিয়েছিলাম । ঝাঁকুনি দেওয়া সংস্করণ ৪.১ এ সক্ষম হওয়া সম্পূর্ণ অনুকূলিতকরণের সাথে ল্যাম্বডা সংস্করণটি উল্লেখযোগ্যভাবে ধীর গতিতে চলেছে।

#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]

যা প্রত্যাশিত এবং নিরাপদ, তবে লক্ষ্য করা উচিত।

আমাদের টুলবেল্টগুলিতে সমাধান পাওয়া খুব দুর্দান্ত, তবে আমি মনে করি যদি বর্তমান পদ্ধতির সাথে পারফরম্যান্সের তুলনা করা যায় তবে ভাষার ক্ষেত্রে এই কেসটি পরিচালনা করার আরও ভাল পদ্ধতির প্রয়োজন হবে।

বিঃদ্রঃ:

একজন মন্তব্যকারী হিসাবে উল্লেখ করা হয়েছে, মনে হচ্ছে ভিসি ++ এর সর্বশেষ সংস্করণ এটি সমান পারফরম্যান্সের বিন্দুতে অনুকূলিত করার একটি উপায় খুঁজে পেয়েছে। হতে পারে আমাদের সর্বোপরি (সিনট্যাকটিক চিনি ব্যতীত) এটি হ্যান্ডেল করার জন্য আরও ভাল পদ্ধতির দরকার নেই।

এছাড়াও, কিছু অন্যান্য এসও পোস্ট যেমন সাম্প্রতিক সপ্তাহগুলিতে বর্ণিত হয়েছে std::function<>, কমপক্ষে ল্যাম্বডা ক্যাপচারটি std::functionছোট-ফান্টেক্টরগুলির জন্য কয়েকটি লাইব্রেরি-অনুকূলিত স্থান ব্যবহারের সাথে ফিট করার জন্য লাম্বদা ক্যাপচারটি খুব বড় হয়ে গেলে সাম্প্রতিক সপ্তাহগুলিতে নিজের কর্মক্ষমতা হ্রাসের কারণ হতে পারে calling (আমি বিভিন্ন শর্ট স্ট্রিং অপটিমাইজেশনের মতো কান্ডা অনুমান করি?)।


2
-1। লক্ষ্য করুন যে "ল্যাম্বডা" সংস্করণটি বেশি সময় নেওয়ার একমাত্র কারণ হ'ল আপনি এটি স্টাডি :: ফাংশনে আবদ্ধ করেন যা অপারেটরকে () একটি ভার্চুয়াল কল কল করে এবং এতে স্পষ্টতই বেশি সময় লাগতে পারে। সর্বোপরি, ভিএস ২০১২ রিলিজ মোডে আপনার কোড, উভয় ক্ষেত্রে একই পরিমাণে সময় নিয়েছিল।
ইয়াম মার্কোভিক

@ ইয়ামমার্কোভিচ কী? এটি বর্তমানে পুনরাবৃত্ত ল্যাম্বদা লেখার একমাত্র জ্ঞাত উপায় (এটি উদাহরণের মূল বিষয় ছিল)। আমি খুব জেনে খুশি হয়েছি যে ভিএস ২০১২ এই ব্যবহারের ক্ষেত্রে অনুকূলকরণের একটি উপায় খুঁজে পেয়েছে (যদিও সম্প্রতি এই বিষয়ে আরও কিছু উন্নয়ন হয়েছে, স্পষ্টতই যদি আমার ল্যাম্বডা আরও ক্যাপচার করত তবে এটি স্ট্যান্ড :: ফাংশন ছোট- মেমরি ফান্টর অপ্টিমাইজেশন বা হোয়াট নোট)
মিমোকনি

2
স্বীকৃত. আমি আপনার পোস্ট ভুল বুঝেছি। তারপর +1। গাহ, আপনি যদি এই উত্তরটি সম্পাদনা করেন তবে কেবল উজ্জীবিত হতে পারে। সুতরাং আপনি কি আরও কিছুটা জোর দিতে পারেন, যেমন মন্তব্যে?
ইয়াম মার্কোভিচ

1
@ ইয়ামমার্কোভিক সম্পন্ন আমি আপনার প্রতিক্রিয়া জানাতে এবং প্রয়োজন যখন এটি পরিমার্জন করতে ইচ্ছুক প্রশংসা করি। আপনাকে +1, ভাল স্যার।
মিমোকনি

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তার থেকে নতুন অনুলিপি crating প্রয়োজন *this
ইয়ানকেস

1

এখানে @ ব্যারি প্রস্তাবিত একটি এর উপর ভিত্তি করে ওয়াই-কম্বিনেটর সমাধানের পরিমার্জনিত সংস্করণ is

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 recওক্যামেলের কীওয়ার্ডের মতো , যদিও একই নয়।


0

সি ++ ১৪: এখানে একটি পুনরাবৃত্ত বেনামে স্টেটলেস / ল্যাম্বডাসের কোনও ক্যাপচার জেনেরিক সেট নেই যা সমস্ত সংখ্যা 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);

যদি আমি সঠিকভাবে বুঝতে পারি তবে এটি ওয়াই-কম্বিনেটর সমাধানটি ব্যবহার করছে

এবং এখানে যোগফল (এন, মি) সংস্করণ

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

ওপি'র চূড়ান্ত উত্তর এখানে। যাইহোক, ভিজ্যুয়াল স্টুডিও 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

আপনি সংশোধন করার মাঝে রয়েছেন এমন একটি চলক (যোগফল) ক্যাপচার চেষ্টা করছেন। এটা ভাল হতে পারে না।

আমি মনে করি না সত্যিকারের স্ব-পুনরাবৃত্ত সি ++ 0 এক্স ল্যাম্বডাস সম্ভব। আপনার অন্য ল্যাম্বডাস ক্যাপচার করতে সক্ষম হওয়া উচিত।


3
তবে ক্যাপচার-তালিকাটি পরিবর্তন না করে যোগের ডিকোরেশনটিকে 'অটো' থেকে স্টেড :: ফাংশন <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একটি অতিরিক্ত স্তরের ইন্ডিয়ারেশন যুক্ত করবে। উদাহরণটি কাজ করে এবং castালাই স্ট্যান্ডার্ড-কমপ্লায়েন্ট, আমি জানি না কী -1ছিল।
ব্যবহারকারী 1095108

না, স্ট্রাক্ট কেবল পয়েন্টারের জন্য ধারক হিসাবে কাজ করবে এবং মান হিসাবে পাস হবে। এটি পয়েন্টারের চেয়ে বেশি ইন্ডায়ারেশন বা ওভারহেড হবে না। এবং এটি সম্পর্কে -1আমি জানতাম না যে এটি আপনাকে দিয়েছিল তবে আমি মনে করি এটির কারণটি reinterpret_castএকটি শেষ অবলম্বন হিসাবে ব্যবহার করা উচিত।
ইয়াঙ্কস

castকল্পনানুসারে C ++ 11 মান দ্বারা কাজ নিশ্চিত করা হয়। একটি ব্যবহার structআমার চোখে, একটি ল্যামডা বস্তুর ব্যবহার পরাজিত পারে। সর্বোপরি, structআপনার প্রস্তাবিত একটি ফ্যামেক্টর, ল্যাম্বডা অবজেক্টটি ব্যবহার করে।
ব্যবহারকারী 1095108

@ ছদ্মনাম সমাধানটি দেখুন, কেবল মুছে ফেলুন std::functionএবং আপনার মনে যে মনে পড়েছিল তার কাছাকাছি কিছু থাকবে। এটি সম্ভবত আপনার সমাধানের অনুরূপ পারফরম্যান্স করবে have
ইয়ানকস

-3

আপনার একটি নির্দিষ্ট পয়েন্ট সংযোজক দরকার need এই দেখুন ।

বা নিম্নলিখিত কোডটি দেখুন:

//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;
}
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.