From bdd0a5e32f56ef31deaf904d23ff85ced578f810 Mon Sep 17 00:00:00 2001 From: levi Date: Wed, 2 Apr 2025 23:06:38 +0200 Subject: [PATCH 01/14] Initial lambda draft A tutorial explaining lambdas. --- src/resources/cpp/lambdas.md | 204 +++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 src/resources/cpp/lambdas.md diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md new file mode 100644 index 0000000..e0ecc64 --- /dev/null +++ b/src/resources/cpp/lambdas.md @@ -0,0 +1,204 @@ +# Lambdas + +## Prerequisites + +- This chapter assumes you have a basic understanding the following topics as this builds up on the knowledge taught in the following chapters: +- [functions](functions.md) +- references +- classes + + + +## What is a lambda? + +Lambdas are similar to function, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. + +if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their behavior to be slightly different depending on your specific use case. + +```cpp +#include +#include +#include + +int main() +{ + std::vector v{3, -4, 2, -8, 15, 267}; + + // Here we declare a local lambda object, called print + auto print = [](const int& n) { std::cout << n << '\n'; }; + + // Here we use this lambda object in the algorithm for each, where the lambda will be called for each + // iteration and print the value in the vector. + std::for_each(v.cbegin(), v.cend(), print); + + // As a lambda is similar to a function we can also call it like a function. This will print 5 + print(5); +} +``` + +## Structure of lambdas + +A lambda `[](){}` consist of 3 main parts, the **capture list** `[]`, **parameters** `()` and the **body** `{}`. You will *almost* always need all these 3 components to make a valid lambda. In specific cases the parameter list may be omitted, but I would generally recommend against omitting this regardless if it's allowed or not. + +### Parameters `()` + +The parameter part of a lambda is the exact same as the parameter list of a function. Here you specify the inputs of your lambda. + +For example `[](int a, int b){}` is a lambda that takes in 2 integer values. + +### body `{}` + +The lambda body is the exact same as the body of the function. It's hold the code that the lambda will execute when it's invoked. + +### Capture list + +As the name implies the capture list allows us to capture values into the lambda as it's constructed. Unlike the parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when you want to bring external state into a generic algorithm. + +Take for example a simple program where you want to multiply every number with a number the user provides. + +```cpp +#include +#include +#include + +int main() +{ + std::vector v{3, -4, 2, -8, 15, 267}; + + std::cout << "Please enter a number to multiply with"; + int multiply_number = 0; + std::cin >> multiply_number; + + // Here we declare a local lambda object. We capture the variable multiply_number so it can be used in the lambda. We return the value of the passed in value with multiply_number. + auto func = [multiply_number](const int& n) { + return n * multiply_number; + }; + + // Here we use this lambda object in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) + std::transform(v.cbegin(), v.cend(), v.begin(), func); +} +``` + +You might have noticed that the lambda uses the same variable name inside the lambda as declared in main. These are actually not the same variables. The multiply_number in the lambda is a copy of the original in main. you can modify the above example like this to give the variable a different name if so desired. + +```cpp +auto func = [num = multiply_number](const int& n) { + return n * multiply_number; + }; +``` + +Here we make a copy of multiply_number called num, we do not need to specify the type here. + +Making a copy of a value might be undesirable and if wanted we can also capture the variable as a reference. Now no copy is made of multiply_number. + +```cpp +auto func = [&multiply_number](const int& n) { + return n * multiply_number; + }; +``` + +You can mix this syntax to capture multiple variables. + +```cpp +int a = 1; +int b = 2; +int c = 3; +auto func = [a, &b, d = c](const int& n) { + return a + b + d; + }; +``` + +Here we capture `a` by value, `b` as a reference and `c` as a copy into the variable `d` in the lambda. + +As you noticed the list here is getting rather long. Luckily we have some special values that can help us keep the lambda small. We have `=` that captures all used variables in the lambda by value. and `&` that captures all variables as a reference. + +```cpp +int a = 1; +int b = 2; +int c = 3; +int d = 4; +// a,b, c are copied into the lambda. d is not copied as it's not used. +auto func = [=](const int& n) { + return a + b + c; + }; + +// a,b, c are captured as a reference into the lambda. +auto func2 = [&](const int& n) { + return a + b + c; + }; +``` + +You can also mix this with the syntax you learned above to capture specifically by reference or value. So for example by default we make a copy except the specific value we capture as a reference. + +```cpp +int a = 1; +int b = 2; +int c = 3; +int d = 4; +// a,b are copied into the lambda. d is not copied as it's not used. and c is captured as a reference into the lambda. +auto func = [=, &c](const int& n) { + return a + b + c; + }; +``` + +## `this` keyword + +The lambda has 1 additional special keyword called `this` you might be familiar with this keyword if you have worked with classes. For lambdas however the meaning is slightly different. By specifying this into the lambda capture list you give it access to the class variables as if it where part of the parent class itself. even private variables. + +```cpp +#include + +class Foo +{ + private: + int a = 5; + + public: + void func(){ + auto lambda = [this](){ + // notice how we can access the private member a here! + std::cout << a << "\n"; + }; + + lambda(); + } +}; + +int main(){ + Foo obj; + obj.func(); + +} +``` + +You can capture additional variables as well with the same rules as described in the previous chapter. + +```cpp +#include + +class Foo +{ + private: + int a = 5; + + public: + void func(){ + int b = 1; + int c = 2; + + // b is captured as a copy, and c is captured by reference. + auto lambda = [this, b, &c](){ + // notice how we can access the private member a here! as well as the local variable b and c + std::cout << a << " " << b << " " << c << "\n"; + }; + + lambda(); + } +}; + +int main(){ + Foo obj; + obj.func(); + +} +``` \ No newline at end of file From 50dda00e0a3f7fbaf82950ded4a22e8882438c41 Mon Sep 17 00:00:00 2001 From: Levi <39926733+mcmlevi@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:01:20 +0200 Subject: [PATCH 02/14] lambda parts surgestion Co-authored-by: Jan Schultke --- src/resources/cpp/lambdas.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index e0ecc64..fb819d9 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -38,7 +38,12 @@ int main() ## Structure of lambdas -A lambda `[](){}` consist of 3 main parts, the **capture list** `[]`, **parameters** `()` and the **body** `{}`. You will *almost* always need all these 3 components to make a valid lambda. In specific cases the parameter list may be omitted, but I would generally recommend against omitting this regardless if it's allowed or not. +A lambda consists of at least three parts: +- `[]`, the introducer, containing captures. +- `()`, the parameters (optional since C++23). +- `{}`, the body of the lambda. + +There are many further optional parts, but `[](){}` or `[]{}` are the bare minimum, depending on the language version. ### Parameters `()` From 064d04c48cc5e333c73669029e9fae4e9ed44773 Mon Sep 17 00:00:00 2001 From: Levi <39926733+mcmlevi@users.noreply.github.com> Date: Thu, 3 Apr 2025 00:01:35 +0200 Subject: [PATCH 03/14] text fixes Co-authored-by: Jan Schultke --- src/resources/cpp/lambdas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index fb819d9..9a98537 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -57,7 +57,7 @@ The lambda body is the exact same as the body of the function. It's hold the cod ### Capture list -As the name implies the capture list allows us to capture values into the lambda as it's constructed. Unlike the parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when you want to bring external state into a generic algorithm. +As the name implies, the capture list allows us to capture values into the lambda as it's constructed. Unlike the parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when you want to bring external state into a generic algorithm. Take for example a simple program where you want to multiply every number with a number the user provides. From 419cd21be751684af2387fb2365f017478e3dc64 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 3 Apr 2025 00:02:43 +0200 Subject: [PATCH 04/14] typos and lambda spacing --- src/resources/cpp/lambdas.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index e0ecc64..813f753 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -11,7 +11,7 @@ ## What is a lambda? -Lambdas are similar to function, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. +Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their behavior to be slightly different depending on your specific use case. @@ -72,7 +72,7 @@ int main() // Here we declare a local lambda object. We capture the variable multiply_number so it can be used in the lambda. We return the value of the passed in value with multiply_number. auto func = [multiply_number](const int& n) { return n * multiply_number; - }; + }; // Here we use this lambda object in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) std::transform(v.cbegin(), v.cend(), v.begin(), func); @@ -84,7 +84,7 @@ You might have noticed that the lambda uses the same variable name inside the la ```cpp auto func = [num = multiply_number](const int& n) { return n * multiply_number; - }; +}; ``` Here we make a copy of multiply_number called num, we do not need to specify the type here. @@ -94,7 +94,7 @@ Making a copy of a value might be undesirable and if wanted we can also capture ```cpp auto func = [&multiply_number](const int& n) { return n * multiply_number; - }; +}; ``` You can mix this syntax to capture multiple variables. @@ -105,7 +105,7 @@ int b = 2; int c = 3; auto func = [a, &b, d = c](const int& n) { return a + b + d; - }; +}; ``` Here we capture `a` by value, `b` as a reference and `c` as a copy into the variable `d` in the lambda. @@ -120,12 +120,12 @@ int d = 4; // a,b, c are copied into the lambda. d is not copied as it's not used. auto func = [=](const int& n) { return a + b + c; - }; +}; // a,b, c are captured as a reference into the lambda. auto func2 = [&](const int& n) { return a + b + c; - }; +}; ``` You can also mix this with the syntax you learned above to capture specifically by reference or value. So for example by default we make a copy except the specific value we capture as a reference. @@ -138,7 +138,7 @@ int d = 4; // a,b are copied into the lambda. d is not copied as it's not used. and c is captured as a reference into the lambda. auto func = [=, &c](const int& n) { return a + b + c; - }; +}; ``` ## `this` keyword @@ -201,4 +201,4 @@ int main(){ obj.func(); } -``` \ No newline at end of file +``` From 6358257ca3bf3ba3d0d108cf5274ee67f23aa0b6 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 3 Apr 2025 00:08:19 +0200 Subject: [PATCH 05/14] fixed incorrect info regarding omiting of lambda parameters. --- src/resources/cpp/lambdas.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index d1bf6c2..4d197dc 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -7,8 +7,6 @@ - references - classes - - ## What is a lambda? Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. @@ -39,8 +37,9 @@ int main() ## Structure of lambdas A lambda consists of at least three parts: + - `[]`, the introducer, containing captures. -- `()`, the parameters (optional since C++23). +- `()`, the parameters (optional in some cases). - `{}`, the body of the lambda. There are many further optional parts, but `[](){}` or `[]{}` are the bare minimum, depending on the language version. From 252140043c52db6252c7f3927bcb0491aa6498f9 Mon Sep 17 00:00:00 2001 From: Levi de Koning Date: Thu, 3 Apr 2025 09:47:55 +0200 Subject: [PATCH 06/14] Feedback Removed fluff, added return 0 in main. --- src/resources/cpp/lambdas.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index 4d197dc..e78c9f5 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -9,7 +9,7 @@ ## What is a lambda? -Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. +Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)). if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their behavior to be slightly different depending on your specific use case. @@ -31,6 +31,8 @@ int main() // As a lambda is similar to a function we can also call it like a function. This will print 5 print(5); + + return 0; } ``` @@ -80,6 +82,8 @@ int main() // Here we use this lambda object in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) std::transform(v.cbegin(), v.cend(), v.begin(), func); + + return 0; } ``` @@ -172,6 +176,7 @@ int main(){ Foo obj; obj.func(); + return 0; } ``` @@ -204,5 +209,6 @@ int main(){ Foo obj; obj.func(); + return 0; } ``` From b954807f4f0c7e4f30f5160b614f15c38e085077 Mon Sep 17 00:00:00 2001 From: Levi de Koning Date: Thu, 3 Apr 2025 09:50:42 +0200 Subject: [PATCH 07/14] run auto formatter --- src/resources/cpp/lambdas.md | 42 +++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index e78c9f5..65dff6a 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -2,16 +2,20 @@ ## Prerequisites -- This chapter assumes you have a basic understanding the following topics as this builds up on the knowledge taught in the following chapters: +- This chapter assumes you have a basic understanding the following topics as this builds up on the knowledge taught in + the following chapters: - [functions](functions.md) - references - classes ## What is a lambda? -Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)). +Lambdas are similar to functions, however they are not identical they specifically are +[closures](). -if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their behavior to be slightly different depending on your specific use case. +if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the +simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their +behavior to be slightly different depending on your specific use case. ```cpp #include @@ -31,7 +35,7 @@ int main() // As a lambda is similar to a function we can also call it like a function. This will print 5 print(5); - + return 0; } ``` @@ -48,17 +52,21 @@ There are many further optional parts, but `[](){}` or `[]{}` are the bare minim ### Parameters `()` -The parameter part of a lambda is the exact same as the parameter list of a function. Here you specify the inputs of your lambda. +The parameter part of a lambda is the exact same as the parameter list of a function. Here you specify the inputs of +your lambda. For example `[](int a, int b){}` is a lambda that takes in 2 integer values. ### body `{}` -The lambda body is the exact same as the body of the function. It's hold the code that the lambda will execute when it's invoked. +The lambda body is the exact same as the body of the function. It's hold the code that the lambda will execute when it's +invoked. ### Capture list -As the name implies, the capture list allows us to capture values into the lambda as it's constructed. Unlike the parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when you want to bring external state into a generic algorithm. +As the name implies, the capture list allows us to capture values into the lambda as it's constructed. Unlike the +parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly +common when you want to bring external state into a generic algorithm. Take for example a simple program where you want to multiply every number with a number the user provides. @@ -82,12 +90,14 @@ int main() // Here we use this lambda object in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) std::transform(v.cbegin(), v.cend(), v.begin(), func); - + return 0; } ``` -You might have noticed that the lambda uses the same variable name inside the lambda as declared in main. These are actually not the same variables. The multiply_number in the lambda is a copy of the original in main. you can modify the above example like this to give the variable a different name if so desired. +You might have noticed that the lambda uses the same variable name inside the lambda as declared in main. These are +actually not the same variables. The multiply_number in the lambda is a copy of the original in main. you can modify the +above example like this to give the variable a different name if so desired. ```cpp auto func = [num = multiply_number](const int& n) { @@ -97,7 +107,8 @@ auto func = [num = multiply_number](const int& n) { Here we make a copy of multiply_number called num, we do not need to specify the type here. -Making a copy of a value might be undesirable and if wanted we can also capture the variable as a reference. Now no copy is made of multiply_number. +Making a copy of a value might be undesirable and if wanted we can also capture the variable as a reference. Now no copy +is made of multiply_number. ```cpp auto func = [&multiply_number](const int& n) { @@ -118,7 +129,9 @@ auto func = [a, &b, d = c](const int& n) { Here we capture `a` by value, `b` as a reference and `c` as a copy into the variable `d` in the lambda. -As you noticed the list here is getting rather long. Luckily we have some special values that can help us keep the lambda small. We have `=` that captures all used variables in the lambda by value. and `&` that captures all variables as a reference. +As you noticed the list here is getting rather long. Luckily we have some special values that can help us keep the +lambda small. We have `=` that captures all used variables in the lambda by value. and `&` that captures all variables +as a reference. ```cpp int a = 1; @@ -136,7 +149,8 @@ auto func2 = [&](const int& n) { }; ``` -You can also mix this with the syntax you learned above to capture specifically by reference or value. So for example by default we make a copy except the specific value we capture as a reference. +You can also mix this with the syntax you learned above to capture specifically by reference or value. So for example by +default we make a copy except the specific value we capture as a reference. ```cpp int a = 1; @@ -151,7 +165,9 @@ auto func = [=, &c](const int& n) { ## `this` keyword -The lambda has 1 additional special keyword called `this` you might be familiar with this keyword if you have worked with classes. For lambdas however the meaning is slightly different. By specifying this into the lambda capture list you give it access to the class variables as if it where part of the parent class itself. even private variables. +The lambda has 1 additional special keyword called `this` you might be familiar with this keyword if you have worked +with classes. For lambdas however the meaning is slightly different. By specifying this into the lambda capture list you +give it access to the class variables as if it where part of the parent class itself. even private variables. ```cpp #include From 81e92528e1642ef375923fca9f175c7f6c1e7c07 Mon Sep 17 00:00:00 2001 From: Levi de Koning Date: Thu, 3 Apr 2025 09:56:09 +0200 Subject: [PATCH 08/14] use bound lambda argument rather then orginal --- src/resources/cpp/lambdas.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index 65dff6a..1966465 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -101,7 +101,7 @@ above example like this to give the variable a different name if so desired. ```cpp auto func = [num = multiply_number](const int& n) { - return n * multiply_number; + return n * num; }; ``` From 7f7e9d19544fd295f4cb4f879903f160cc157ce5 Mon Sep 17 00:00:00 2001 From: levi Date: Sun, 6 Apr 2025 22:04:57 +0200 Subject: [PATCH 09/14] removed prereq --- src/resources/cpp/lambdas.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index 4d197dc..b9ee98f 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -1,12 +1,5 @@ # Lambdas -## Prerequisites - -- This chapter assumes you have a basic understanding the following topics as this builds up on the knowledge taught in the following chapters: -- [functions](functions.md) -- references -- classes - ## What is a lambda? Lambdas are similar to functions, however they are not identical they specifically are [closures](https://en.wikipedia.org/wiki/Closure_(computer_programming)) what might be a term you are familiar with from other languages. From 1d7bd666de378d3f14a40d09a1d6f645f8491f14 Mon Sep 17 00:00:00 2001 From: levi Date: Sun, 6 Apr 2025 22:18:53 +0200 Subject: [PATCH 10/14] added trailing return type information --- src/resources/cpp/lambdas.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index 27decd8..2f2f663 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -42,6 +42,22 @@ A lambda consists of at least three parts: There are many further optional parts, but `[](){}` or `[]{}` are the bare minimum, depending on the language version. +for a lambda the return type does not need to be specified like functions do. The return type is deduced by the +compiler. However you can specify a trailing return type to the lambda. + +```cpp +// This lambda return an interger value +[]() -> int { + return 5; +}; + +// ``equivalent to the following lambda +[](){ + return 5; +}; + +``` + ### Parameters `()` The parameter part of a lambda is the exact same as the parameter list of a function. Here you specify the inputs of From c2fd62d654c3dabb954f07309274467780bcc3d9 Mon Sep 17 00:00:00 2001 From: levi Date: Sun, 6 Apr 2025 22:31:23 +0200 Subject: [PATCH 11/14] Attempt at making a stronger entry point for lambdas As per Eisenwave's suggestion, an attempt to create a stronger use case at the start. --- src/resources/cpp/lambdas.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index 2f2f663..f370108 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -5,9 +5,15 @@ Lambdas are similar to functions, however they are not identical they specifically are [closures](). -if they are similar to functions one might ask why would I ever need a lambda? They have multiple usages but the -simplest and most common one is for predicates in algorithms. Algorithms are nice and generic and you want their -behavior to be slightly different depending on your specific use case. +if they are similar to functions one might ask why would I ever need a lambda? For example, algorithms in the standard +library can often have their behavior changed using something callable. Creating a whole function is often excessive, +and we use lambdas instead: + +`int non_a_amount = std::ranges::count_if("abcaabbac", [](char c) { return c != 'a'; });` + +This counts all results where the lambda returns true, in this case if the latter is not 'a'. + +Another example is where we want to print each value in an array: ```cpp #include From 384595c638aef8f919758623eff55e42b7bd6d81 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 24 Apr 2025 19:02:24 +0200 Subject: [PATCH 12/14] adressing code style / tone comments. --- src/resources/cpp/lambdas.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index f370108..ba6a61d 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -2,7 +2,7 @@ ## What is a lambda? -Lambdas are similar to functions, however they are not identical they specifically are +Lambdas are similar to functions; however, they are not identical. Specifically, they are [closures](). if they are similar to functions one might ask why would I ever need a lambda? For example, algorithms in the standard @@ -48,8 +48,8 @@ A lambda consists of at least three parts: There are many further optional parts, but `[](){}` or `[]{}` are the bare minimum, depending on the language version. -for a lambda the return type does not need to be specified like functions do. The return type is deduced by the -compiler. However you can specify a trailing return type to the lambda. +For a lambda, the return type does not need to be explicitly specified, unlike functions. The return type is deduced by +the compiler. However, you may specify a trailing return type to the lambda explicitly. ```cpp // This lambda return an interger value @@ -57,7 +57,7 @@ compiler. However you can specify a trailing return type to the lambda. return 5; }; -// ``equivalent to the following lambda +// equivalent to the following lambda [](){ return 5; }; @@ -73,14 +73,14 @@ For example `[](int a, int b){}` is a lambda that takes in 2 integer values. ### body `{}` -The lambda body is the exact same as the body of the function. It's hold the code that the lambda will execute when it's -invoked. +The lambda body is the exact same as the body of the function. The body contains all the code that will be executed when +the lambda is invoked. ### Capture list -As the name implies, the capture list allows us to capture values into the lambda as it's constructed. Unlike the -parameters where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly -common when you want to bring external state into a generic algorithm. +As the name implies, the capture list allows us to capture values as the lambda is constructed. Unlike the parameters +where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when +you want to bring external state into a generic algorithm. Take for example a simple program where you want to multiply every number with a number the user provides. @@ -97,7 +97,7 @@ int main() int multiply_number = 0; std::cin >> multiply_number; - // Here we declare a local lambda object. We capture the variable multiply_number so it can be used in the lambda. We return the value of the passed in value with multiply_number. + // Here we declare a lambda object. The lambda capture the variable multiply_number so it can be used in the lambda. The lambda return the value of the passed in value with multiply_number. auto func = [multiply_number](const int& n) { return n * multiply_number; }; From 55e3b755deff791d351250a341e7f84493101241 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 24 Apr 2025 19:15:54 +0200 Subject: [PATCH 13/14] Simpler lambda introduction --- src/resources/cpp/lambdas.md | 41 +++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index ba6a61d..c229909 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -5,13 +5,44 @@ Lambdas are similar to functions; however, they are not identical. Specifically, they are [closures](). -if they are similar to functions one might ask why would I ever need a lambda? For example, algorithms in the standard -library can often have their behavior changed using something callable. Creating a whole function is often excessive, -and we use lambdas instead: +In the following example a lambda is shown with a matching function. -`int non_a_amount = std::ranges::count_if("abcaabbac", [](char c) { return c != 'a'; });` +```cpp +#include + +void say_a() +{ + std::cout << "a" << "\n"; +} + +int main() +{ + auto say_b = [](){ + std::cout << "b" << "\n"; + }; + + say_a(); // prints a + say_b(); // prints b +} + +``` + +As is visible from the above code snippet a lot of the syntax of functions is shared with lambdas. Where lambdas shine +is when used in algorithms. In the standard library there are algorithms that can have their behavior changed using +something callable. Creating a whole function is often excessive, and we use lambdas instead: + +```cpp +// A lambda that checks if the passed in letter is not equal to a. +auto a_checker = [](char c) { + return c != 'a'; + }; + + +// the function count_if is used together with the lambda a_checker to get the count of all letters that are not a. +int non_a_amount = std::ranges::count_if("abcaabbac", a_checker);` +``` -This counts all results where the lambda returns true, in this case if the latter is not 'a'. +This code sample counts all cases where the lambda returns true, in this case if the latter is not 'a'. Another example is where we want to print each value in an array: From f6be7942580be2653b1b84af7204264dd73ce3a9 Mon Sep 17 00:00:00 2001 From: levi Date: Thu, 24 Apr 2025 20:01:28 +0200 Subject: [PATCH 14/14] tonal changes --- src/resources/cpp/lambdas.md | 63 ++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/src/resources/cpp/lambdas.md b/src/resources/cpp/lambdas.md index c229909..33fe6c5 100644 --- a/src/resources/cpp/lambdas.md +++ b/src/resources/cpp/lambdas.md @@ -55,14 +55,14 @@ int main() { std::vector v{3, -4, 2, -8, 15, 267}; - // Here we declare a local lambda object, called print + // Here is a lambda object, called print auto print = [](const int& n) { std::cout << n << '\n'; }; - // Here we use this lambda object in the algorithm for each, where the lambda will be called for each + // Here this lambda object in used the algorithm for each, where the lambda will be called for each // iteration and print the value in the vector. std::for_each(v.cbegin(), v.cend(), print); - // As a lambda is similar to a function we can also call it like a function. This will print 5 + // As a lambda is similar to a function, it can also be called it like a function. This will print 5 print(5); return 0; @@ -80,7 +80,7 @@ A lambda consists of at least three parts: There are many further optional parts, but `[](){}` or `[]{}` are the bare minimum, depending on the language version. For a lambda, the return type does not need to be explicitly specified, unlike functions. The return type is deduced by -the compiler. However, you may specify a trailing return type to the lambda explicitly. +the compiler. However trailing return type may be added to the lambda explicitly. ```cpp // This lambda return an interger value @@ -97,8 +97,8 @@ the compiler. However, you may specify a trailing return type to the lambda expl ### Parameters `()` -The parameter part of a lambda is the exact same as the parameter list of a function. Here you specify the inputs of -your lambda. +The parameter part of a lambda is the exact same as the parameter list of a function. Here inputs of the lambda are +specified. For example `[](int a, int b){}` is a lambda that takes in 2 integer values. @@ -109,11 +109,11 @@ the lambda is invoked. ### Capture list -As the name implies, the capture list allows us to capture values as the lambda is constructed. Unlike the parameters -where we specify which values we want to pass in when we invoke the lambda. This tends to be particularly common when -you want to bring external state into a generic algorithm. +As the name implies, the capture list allows values to be captured as the lambda is constructed. Unlike the parameters +where we specify which values that are passed in when the lambda is invoked. This tends to be particularly common when +external state is needed in a generic algorithm. -Take for example a simple program where you want to multiply every number with a number the user provides. +Take for example a simple program where every number is multiplied with a number the user provides. ```cpp #include @@ -128,21 +128,21 @@ int main() int multiply_number = 0; std::cin >> multiply_number; - // Here we declare a lambda object. The lambda capture the variable multiply_number so it can be used in the lambda. The lambda return the value of the passed in value with multiply_number. + // Here a lambda object is declared. The lambda capture the variable multiply_number so it can be used in the lambda. The lambda return the value of the passed in value with multiply_number. auto func = [multiply_number](const int& n) { return n * multiply_number; }; - // Here we use this lambda object in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) + // Here the lambda object in used in the algorithm transform. This algorithm takes a ranges to iterate over, an output destination (the begin of the same vector in this case) and the predicate (our lambda) std::transform(v.cbegin(), v.cend(), v.begin(), func); return 0; } ``` -You might have noticed that the lambda uses the same variable name inside the lambda as declared in main. These are -actually not the same variables. The multiply_number in the lambda is a copy of the original in main. you can modify the -above example like this to give the variable a different name if so desired. +As is visible above have noticed that the lambda uses the same variable name inside the lambda as declared in main. +These are actually not the same variables. The multiply_number in the lambda is a copy of the original in main. The +example can be modified like this to give the variable a different name if so desired. ```cpp auto func = [num = multiply_number](const int& n) { @@ -150,10 +150,10 @@ auto func = [num = multiply_number](const int& n) { }; ``` -Here we make a copy of multiply_number called num, we do not need to specify the type here. +Here a copy is made of multiply_number called num. The type does not need to be specified here. -Making a copy of a value might be undesirable and if wanted we can also capture the variable as a reference. Now no copy -is made of multiply_number. +Making a copy of a value might be undesirable and thus it's also possible to capture the variable as a reference. Now no +copy is made of multiply_number. ```cpp auto func = [&multiply_number](const int& n) { @@ -161,7 +161,7 @@ auto func = [&multiply_number](const int& n) { }; ``` -You can mix this syntax to capture multiple variables. +This syntax can be mixed to capture multiple variables. ```cpp int a = 1; @@ -172,11 +172,11 @@ auto func = [a, &b, d = c](const int& n) { }; ``` -Here we capture `a` by value, `b` as a reference and `c` as a copy into the variable `d` in the lambda. +Here `a` is captured by value, `b` as a reference and `c` as a copy into the variable `d` in the lambda. -As you noticed the list here is getting rather long. Luckily we have some special values that can help us keep the -lambda small. We have `=` that captures all used variables in the lambda by value. and `&` that captures all variables -as a reference. +As is visible the capture list here is getting rather long. Luckily there are some special values that can help keep the +lambda small. `=` Can be used captures all used variables in the lambda by value, and `&` captures all variables as a +reference. ```cpp int a = 1; @@ -194,8 +194,8 @@ auto func2 = [&](const int& n) { }; ``` -You can also mix this with the syntax you learned above to capture specifically by reference or value. So for example by -default we make a copy except the specific value we capture as a reference. +This can also be mixed with the syntax learned above to capture specifically by reference or value. So for example by +default a copies can be made except the specific value that is captured as a reference. ```cpp int a = 1; @@ -210,9 +210,10 @@ auto func = [=, &c](const int& n) { ## `this` keyword -The lambda has 1 additional special keyword called `this` you might be familiar with this keyword if you have worked -with classes. For lambdas however the meaning is slightly different. By specifying this into the lambda capture list you -give it access to the class variables as if it where part of the parent class itself. even private variables. +The lambda has 1 additional special keyword called `this`. This keyword might sound familiar with this keyword if you +have worked with classes before. For lambdas however the meaning is slightly different. By specifying this into the +lambda capture list the lambda is given access to the class variables as if it where part of the parent class itself. +That also means even private variables can be accessed. ```cpp #include @@ -225,7 +226,7 @@ class Foo public: void func(){ auto lambda = [this](){ - // notice how we can access the private member a here! + // notice how the lambda can access the private member a here! std::cout << a << "\n"; }; @@ -241,7 +242,7 @@ int main(){ } ``` -You can capture additional variables as well with the same rules as described in the previous chapter. +Additional variables can be captured as well with the same rules as described in the previous chapter. ```cpp #include @@ -258,7 +259,7 @@ class Foo // b is captured as a copy, and c is captured by reference. auto lambda = [this, b, &c](){ - // notice how we can access the private member a here! as well as the local variable b and c + // notice how the lambda can access the private member a here! as well as the local variable b and c std::cout << a << " " << b << " " << c << "\n"; };