17

This is mostly a one liner style type of question, I would normally write this code in multiple lines anyway for readability reasons.

So my question is can I call the recursive lambda in the same statement where it is defined?

So instead of this:

int n=3;
function<void(int)> f {[n,&f](int i){if (i>1) { cout << "func(a, "; f(i-1); cout << ")";} else cout << "a";}};
f(n);

call the function with n in the same line where f is defined.

cbuchart
  • 10,847
  • 9
  • 53
  • 93
NoSenseEtAl
  • 28,205
  • 28
  • 128
  • 277
  • 1
    would you consider `function f;` `f = ...;` to have the *definition* on the second line? – jaggedSpire Mar 14 '17 at 21:52
  • @jaggedSpire: That does not address the issue of actually calling the lambda with the initial input value. – Remy Lebeau Mar 14 '17 at 21:53
  • Storing a lambda in a `std::function` is inefficient (constructs a function from the lambda) - better to store it in a `auto` variable as that will capture the lambdas true type and not convert to `std::function`. – Jesper Juhl Mar 14 '17 at 21:54
  • @RemyLebeau if you do that, though, you'll already have the symbol to bind to the lambda defined, and you can do `f = [f&](int){}, f(value);` – jaggedSpire Mar 14 '17 at 21:55
  • @JesperJuhl you need to have a function or else you can not have recursive function. http://stackoverflow.com/questions/14531993/can-lambda-functions-be-recursive – NoSenseEtAl Mar 14 '17 at 22:14

5 Answers5

14

In one statement which declares several variables ;-)
Mostly not what you want:

std::function<void(int)>
    f {[&f](int i){
        if (i>1) {
            std::cout << "func(a, "; f(i-1); std::cout << ")";}
        else
            std::cout << "a";
    }},
    dummy((f(3), nullptr));

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 5
    Someone perplexed asked about this answer here: [dummy() function - What is that supposed to be?](http://stackoverflow.com/questions/42805379/dummy-function-what-is-that-supposed-to-be) – Deduplicator Mar 16 '17 at 01:27
7

Let me offer a glimpse into the functional programming world, where people usually use combinators to deal with recursive lambdas. There was a proposal (P0200r0) last year to add a simple Y-combinator to the standard library.

Leaving aside the question whether it is a good idea to do this, this would allow you to write and invoke a recursive lambda like this:

y_combinator([](auto self, int i){
    if (i>1) {
        std::cout << "func(a, ";
        self(i-1);
        std::cout << ")";
    } else {
        std::cout << "a";
    }
})(6);

The basic idea here is that the y-combinator is a higher order function that wraps a lambda which is passed 'itself' as a first argument. The combinator takes care of wrapping the self argument away for all invocations of the lambda.

You can try it in coliru.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
6

As a matter of fact, you can. Here is a complete example which compiles and runs fine with g++ -std=c++11:

#include <iostream>
#include <functional>

int main() {
    std::function<int(int)> f = ([&f](int i){ return i?f(i-1)*i:1; }), trash = (std::cout << f(3) << std::endl, f);
}

However, I don't think it's a good idea to actually use this: The construct , trash = (..., f) would be in order for code golf or obfuscated programming contests, but would not meet my standards for production code.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
3

What you are essentially asking for is to create a temporary instance of std::function and then invoke operator() on that object in the same statement. Both of these fail to compile for me with the same error when I try that:

function<void(int)> f{[&f](int i){ if (i > 1) { cout << "func(a, "; f(i-1); cout << ")"; } else cout << "a"; }}(n);

function<void(int)> f([&f](int i){ if (i > 1) { cout << "func(a, "; f(i-1); cout << ")"; } else cout << "a"; })(n);

error: expected ‘,’ or ‘;’ before ‘(’ token

Pointing at the ( of (n).

See @Jarod42's answer for a viable workaround, if you don't mind the extra variable initialization.

Alternatively, this would work, though it does have to use separate variable declaration and assignment:

function<void(int)> f; f = [&f](int i){ if (i > 1) { cout << "func(a, "; f(i-1); cout << ")"; } else cout << "a"; }, f(n);

Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
2

Not sure if you consider it valid since it doesn't use lambda functions, but it is still single line and leaves no temporal variables behind ;)

struct {
  struct R {
    R(int i) {
      if (i>1) { cout << "func(a, "; R(i-1); cout << ")"; }
      else cout << "a";
    }
  } r;
} f{n};
cbuchart
  • 10,847
  • 9
  • 53
  • 93