polyhistor13's blog

By polyhistor13, 14 months ago, In English

First of all, for whom this blog is (Ideally).

-> Someone interested in learning and at least have a good knowledge of programming in c++.

-> Want's to increase speed and code cleaner.

Regardless everyone is Welcome!!!!

What is Lambda?

Well lambda are so called anonymous functions which can sometimes really help for faster coding and ease of writing.

Basic structure of Lambda

First let's see how does a lambda look like, " [] () {} ; ".

The first part, " [] " The Capture Clause: This is the most powerful part of a lambda. It defines what the lambda can "see" from its surrounding scope.

-> []: Captures nothing. The lambda can only use its own parameters or global variables.

-> [=]: Captures all outside variables by value (makes a copy).

-> [&]: Captures all outside variables by reference (can modify the originals if '**const**' not used).

-> [var1, &var2]: Captures var1 by value and var2 by reference. You can be very specific!

The second part, " () " is for declaring our parameter's taken as input, we don't need to write the parameters taken by reference in the main scope,

for eg, if inside our main function, I declare a vector dp, and use lambda as pass by ref, I don't need to specify dp in the parameter list and can directly use it, though it has power to change its values, so if we don't want that to happen, we can use "**const** vector <...> dp" and it will no longer be a risk.

The third part, " {} " is our main body of the lambda, we will write our code for what this lambda does...

There is another thing, we also can define our return type like " [] () -> type {} " we can specifically implement a return type, if we don't then 2 cases can happen,

1) If only 1 parameter is returned, it will take it from input parameter, else

2) It will take default return type to be 'void'.

It is a good practice to write the return type in codes.

We can also declare a full fledge functions with a lambda that can use recursion as well, but then we need to be a bit more clear of the data input and output....

for eg,

function < int (int) > factorial = [] (int n) -> int {
    if (n <= 1) 
        return 1;
    else 
        return n * factorial (n - 1);
};

Here inside function brackets, first is the return data type and then inside "()" are the input data type.

this can perform recursion, but if you don't need something this fancy (i.e recursion), we can just write "auto get = [] () {};" and it will get our job done.

Let's take 2 Examples here where we can quickly use lambda function rather than declaring a separate function.

Custom Sorting

Lets say we have a vector of a pair of int and we want to sort it in a specific way, like non — decreasing order as per first and non — increasing as per second. We can easily and quickly pass a lambda to STL sort and get our desired result.

sort (v.begin(), v.end(), [] (auto &a, auto &b) {
    if (a.first == b.first)
        return a.second > b.second;
    else
        return a.first < b.first;
});

Quick declaration of a function

Let's say I need a function like a function for update something in a data structure and I have quite a few different parameters declared inside my solve function, what I can do is

1) make a function in global scope and pass everything to it (for eg. 10 parameters, which is a pain) or

2) quickly make a lambda inside my solve function and define it as reference "[&]", and if needed things with const, no need to write others. This is much faster to do.

auto get = [&] (int a, int b) -> void {
    // some code that does something!!!!
};   // (don't forget ';' at the end)

int a, b;
for (int i = 0; i < n; ++i) {
    cin >> a >> b;
    get (a, b);
}

This is quite useful for quick declarations and inside scope.

There are many more use cases for simpler and less error prone codes using lambda's, like DFS, BFS, custom_hash, Predicates in STL Algorithms and so on...

This concludes my blog. Thanks for reading and happy programming!!!!

For more in-depth learning, please refer this guide by Microsoft HERE

UPD :-

For my modern friends out there, there is also a very clean way to write a recursive Lambda function like this,

"NOTE :- It uses C++-23 (that's why modern) :) ",

auto factorial = [&] (this auto&& self, int n) -> int {
    if (n <= 1)
        return 1;
    else
        return n * self(n - 1);
};

NOTE :- any name can be given instead of self

And for C++14 or later, we can also do it like this :-

1) Either use function, or (showed later in this section itself)!!

2) Use auto and create a self,

auto fac = [&](auto&& self, int x) -> int {
    if (x == 0)
        return 1;
    return x * self(self, x - 1);
};

int ans = fac(fac, 10);

But here a very important thing to note is that, we cannot perform recursion by capture this lambda by reference, so if we have to use something from the main scope, we have to specifically use it, like

auto dfs = [&g, &vis](auto&& self, int u) -> void {
        vis[u] = on;
        cout << u << " ";
        for (auto &x : g[u]) {
            if (vis[x]) continue;
            self(self, x);
        }
};

Here g and vis are captured by reference and not the lambda itself!

And still I prefer just make this once, it would be a lot easier :-

function <void (int) > dfs = [&](int u) -> void {
        vis[u] = on;
        cout << u << " ";
        for (auto &x : g[u]) {
            if (vis[x]) continue;
            dfs (x);
        }
};

And then we can like before simply call this factorial function for our answer, and also there are so called immediately invoked lambda expressions, i.e we don't make a function with a name just use it immediately.

For eg,

([&](this auto&& self, int u) -> void {
    visited[u] = true;
    cout << u << " ";
    for (int v : graph[u]) {
        if (!visited[v]) {
            self(v);
        }
    }
})(start_node); // Immediately invoke the lambda with the starting node.

NOTE :- any name can be given instead of self

This performs dfs only once and its not a separate named function.

There is also something called as y combinator, which can be used for recursion in lambda calculus, but I think it's a bit tricky and lengthy. I think a full named function would be better instead but anyhow it can be use with c++14 or above.

I have attached a link to a stack overflow blog about it HERE,

I was not able to find a good and somewhat complete resource for it, if you do, please write a comment about it. Thanks in advance :)

I hope this help's to become a better coder in future

  • Vote: I like it
  • +70
  • Vote: I do not like it

| Write comment?
»
14 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Nice rp.root. It can help me out through out my competitive programming. Btw, how is your CP experience

  • »
    »
    14 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Thanks Ayyan, it’s been nice and actually a long journey till i got to expert, i have my old account in which it took a long time to reach specialist, and after that i started this account so that i can repractice the question in a bit more structured way and i reached expert here quite fast.

    My old account was Zeta_function

»
14 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

Auto comment: topic has been updated by polyhistor13 (previous revision, new revision, compare).

»
13 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

Very nice blog! Thanks for sharing for such useful info

»
13 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

Why you guys calling it self in c++23 version? It can be named same as lambda object everywhere

  • »
    »
    13 months ago, hide # ^ |
     
    Vote: I like it +1 Vote: I do not like it

    Yes we can use any name instead of self, but i thought it would be “self” explanatory HAHA :), but i should add that as well thanks.

»
13 months ago, hide # |
 
Vote: I like it +3 Vote: I do not like it

Very nice blog , I am hoping you can add y combinater in it too.

  • »
    »
    13 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Thanks sujalxpro, i am pleased to hear that. I should add that for someone who doesnt use c++23, just i think it’s kind of leangthy and a bit tricky.

    Almost in every practical scenario (for CP at least where speed of writing matters), making a named function is significantly better than y combinator.

    But i will still attach a documentation about it, there is no harm in it ofcourse.

    • »
      »
      »
      13 months ago, hide # ^ |
       
      Vote: I like it +3 Vote: I do not like it

      yeah i agree to that , but the use of lambda function makes the code by around 10 — 20 % slower ,

      however , i don't think it is going to make much of an difference as i have even made highly strict code pass using lambda function

      the only person who i have seen to use y combinater is ecnerwala who has an rating of 3500++

      • »
        »
        »
        »
        13 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        Ecnerwala has amazing code's.

        Yes it slows down the code due to function overhead but I think at least in lower question it doesn't matter that much as those questions are not for testing out optimizations. Ofcourse in question of 2600+ (maybe), optimizations will matter.

»
13 months ago, hide # |
← Rev. 3  
Vote: I like it +11 Vote: I do not like it

I want to add that another way to do pre-C++23 recursion with lambdas is (it uses auto):

auto fac = [&](auto &&self, int x) -> int {
    if (x == 0) return 1;
    return self(self, x-1)*x;
};
fac(fac, 5); // 120
»
13 months ago, hide # |
 
Vote: I like it +11 Vote: I do not like it

I've been using lambda functions for quite some time — they are quite handy, time-saving , and easy to code.

Here is one of the best use-cases from my experience

DFS — we use it frequently with trees (also on graphs and grids)

// adjacency list => adj

// Often, we need arrays for height, depth, subtree count, node values, parent references, etc.
// Declare required vectors here
function<void(int,int)> dfs = [&](int u , int p){
    for(auto &child : adj[u]){
        // Update parent/depth/subtree here
        if(child != p){
            dfs(child , u);
        }
    }
};// Don't forget the **semicolon** (I've forgotten it countless times)
»
13 months ago, hide # |
← Rev. 2  
Vote: I like it 0 Vote: I do not like it

https://ideone.com/gTk3r5 this code is showing error while compiling

can anyone please find me a solution? polyhistor13?

  • »
    »
    13 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    Wait, it shows Runtime error.

  • »
    »
    13 months ago, hide # ^ |
     
    Vote: I like it 0 Vote: I do not like it

    One of the problem I can see is that you are capturing dfs by reference, a lambda cannot capture itself by reference, but u want to capture everything else by ref, so do this instead :-

    auto dfs = [&g, &vis](auto&& self, int u) -> void {
            vis[u] = on;
            cout << u << " ";
            for (auto &x : g[u]) {
                if (vis[x]) continue;
                self(self, x);
            }
        };
    

    This captures g and vis by reference and the dfs lambda is not captures by reference

    Or you can also simply do this,

    function <void (int) > dfs = [&](int u) -> void {
            vis[u] = on;
            cout << u << " ";
            for (auto &x : g[u]) {
                if (vis[x]) continue;
                dfs (x);
            }
        };
    

    This will also work, and other thing

    Maybe, not sure, there is stack overflow as for every solve u are making g (N + 1), either make it global or only make it to appropriate size for the test case....

»
5 months ago, hide # |
 
Vote: I like it 0 Vote: I do not like it

so is there any performance difference while writing recursive lamda functions and normal recursive functions ?