From 22e29abdd26aed3a252b311e6b0374c98ab3cee4 Mon Sep 17 00:00:00 2001 From: Robert Douglas Date: Fri, 27 Oct 2023 13:04:19 -0500 Subject: [PATCH 1/4] Strawman for lambdas Issue #35 --- sources/modules/functions/lambdas.md | 110 +++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 sources/modules/functions/lambdas.md diff --git a/sources/modules/functions/lambdas.md b/sources/modules/functions/lambdas.md new file mode 100644 index 00000000..d74b1aa9 --- /dev/null +++ b/sources/modules/functions/lambdas.md @@ -0,0 +1,110 @@ +## Module name: Lambdas + +_Skeleton descriptions are typeset in italic text,_ +_so please don't remove these descriptions when editing the topic._ + +### Overview + +_Provides a short natural language abstract of the module’s contents._ +_Specifies the different levels of teaching._ + +------------------------------------------------------------------------ +Level Objective +----------------- ------------------------------------------------------ +Foundational Define and execute lambdas with basic capture syntax + +Main Passing instances of lambdas for use in external code + +Advanced Generic lambdas, utilizing mutable state, mapping + to function objects, decay to function pointers + +------------------------------------------------------------------------ + +### Motivation + +_Why is this important?_ +_Why do we want to learn/teach this topic?_ + +* function objects in relation to the standard library... +* Allows the developer to define and pass functionality as values/data/information +* Allows developer to operate in a functional-programming mindset +* Improves code readdability by localizing the code +* Effective means to help refactoring code from long functions to re-usable functions +* Library designs taking callables allows the library to be customizable for the consumer, while lambdas make this usage approachable + +### Topic introduction + +_Very brief introduction to the topic._ + +Lambdas were added in C++11 and have grown in power and functionality ever since. + +### Foundational: Define and execute lambdas with basic capture syntax + +#### Background/Required Knowledge + +A student: +_TODO: Add cross-reference to these +Explain capture-by-value and capture-by-reference + +#### Student outcomes + +_A list of things "a student should be able to" after the curriculum._ +_The next word should be an action word and testable in an exam._ +_Max 5 items._ + +A student should be able to: + +1. Use a lambda taking a concrete type as a parameter and return a value in response +2. +3. +4. +5. + +#### Caveats + +_This section mentions subtle points to understand, like anything resulting in +implementation-defined, unspecified, or undefined behavior._ + +#### Points to cover + +_This section lists important details for each point._ + +### Main: Passing instances of lambdas for use in external code + +#### Background/Required Knowledge + +* All of the above. + +#### Student outcomes + +A student should be able to: + +1. Utilize std::function as a means to hold and transfer lambdas +2. Define a function-template taking a lambda as a template parameter +3. Use captures to change + +#### Caveats + +#### Points to cover + +Capture can introduce new names +``` +char const* const s = "5"; +[x=atoi(s)](){} +``` + +``` +std::ranges::for_each( + std::vector{1,2,3,4,5}, + [init = true] (auto&& x) mutable { + if (init) + { std::cout << x; init = false}; + else + { std::cout << ',' << x;} + }); +``` + +### Advanced +Explain why they can't write out the type of a lambda? +_These are important topics that are not expected to be covered but provide +guidance where one can continue to investigate this topic in more depth._ From 5cd71544b876818db2db58f7fb0344995aa33669 Mon Sep 17 00:00:00 2001 From: Robert Douglas Date: Tue, 5 Dec 2023 13:52:05 -0600 Subject: [PATCH 2/4] WIP --- sources/modules/functions/lambdas.md | 78 ++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/sources/modules/functions/lambdas.md b/sources/modules/functions/lambdas.md index d74b1aa9..9516b8e4 100644 --- a/sources/modules/functions/lambdas.md +++ b/sources/modules/functions/lambdas.md @@ -54,11 +54,13 @@ _Max 5 items._ A student should be able to: -1. Use a lambda taking a concrete type as a parameter and return a value in response -2. -3. -4. -5. +1. Use a lambda taking a concrete type as a parameter and return a value +2. transform a function definition into a lambda and use it +3. enumerate trade-offs of code-locality using lambdas vs code reuse with free-functions +4. assign a lambda to a variable for multiple calls +5. named lambdas to increase code readability + + #### Caveats @@ -79,14 +81,62 @@ _This section lists important details for each point._ A student should be able to: -1. Utilize std::function as a means to hold and transfer lambdas +1. Utilize `std::function` as a means to hold and transfer lambdas 2. Define a function-template taking a lambda as a template parameter -3. Use captures to change +3. specify, per parameter, whether to capture by reference or value +4. specify the default capture type for a lambda +5. use a lambda in a class, capturing and utilizing class data via `this` + +#### Caveats + +#### Points to cover + +motivation: +1. how to write library code to call user code? --- callbacks +2. root finding using lambdas +3. "customizing" generic algorithms + +### Advanced +Explain why they can't write out the type of a lambda? +_These are important topics that are not expected to be covered but provide +guidance where one can continue to investigate this topic in more depth._ + + +#### Background/Required Knowledge + +* All of the above. +#### Student outcomes + +A student should be able to: + +1. Use a lambda to introduce a new identifier for use within the body of the lambda +2. explain the relationships between generic lambdas and templates +3. construct an object with choice between 2 identifiers, using immediately-dispathced lambda +4. utilize an immediately dispatched lambda to encode multiple statements where the language requires an expression +5. use the `mutable` keyword to allow changing a captured-by-value or lambda-local value within the lambda body +6. explicitly specify the return type for a lambda +7. explain under what conditions an explicit return type is necessary + #### Caveats #### Points to cover +Construct an object with choice between 2 identifiers: +``` +class X; +bool b; +X x = [c = b](){ if (c) return X(5, 10) else X("a", "b"); }(); +``` + +multiple statements in a single expression +``` +assert([](){ + std::vector v{1, 2, 3, 4, 5}; + for (auto x: v) std::cout << v << std::endl; +}()); +``` + Capture can introduce new names ``` char const* const s = "5"; @@ -96,6 +146,9 @@ char const* const s = "5"; ``` std::ranges::for_each( std::vector{1,2,3,4,5}, + // introducing new identifier + // generic lambda + // mutable allows changing "init" [init = true] (auto&& x) mutable { if (init) { std::cout << x; init = false}; @@ -104,7 +157,10 @@ std::ranges::for_each( }); ``` -### Advanced -Explain why they can't write out the type of a lambda? -_These are important topics that are not expected to be covered but provide -guidance where one can continue to investigate this topic in more depth._ +When to use lambdas or not: +forcing code into a lambda can prevent some features +``` +std::for_each(std::par_sec, v, [](){/* can't utilize OpenMP here */}}; +for(auto i : v){ /* OpenMP can be utilized here */ } +``` + From 49331c62b4b3dd8ea5ee52c07397b439dc0b58bd Mon Sep 17 00:00:00 2001 From: Patrick Diehl Date: Thu, 30 Jan 2025 12:07:02 -0700 Subject: [PATCH 3/4] Minor edits from the North America meeting today --- sources/modules/functions/lambdas.md | 46 +++++++++++++++------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/sources/modules/functions/lambdas.md b/sources/modules/functions/lambdas.md index 9516b8e4..53e68312 100644 --- a/sources/modules/functions/lambdas.md +++ b/sources/modules/functions/lambdas.md @@ -25,11 +25,11 @@ Advanced Generic lambdas, utilizing mutable state, mapping _Why is this important?_ _Why do we want to learn/teach this topic?_ -* function objects in relation to the standard library... +* Function objects in relation to the standard library, e.g. for passing functions to the standard algorithms or auxiliary functions * Allows the developer to define and pass functionality as values/data/information * Allows developer to operate in a functional-programming mindset -* Improves code readdability by localizing the code -* Effective means to help refactoring code from long functions to re-usable functions +* Improves code readability by localizing the code +* Making code more compact by writing functions in-place * Library designs taking callables allows the library to be customizable for the consumer, while lambdas make this usage approachable ### Topic introduction @@ -44,7 +44,7 @@ Lambdas were added in C++11 and have grown in power and functionality ever since A student: _TODO: Add cross-reference to these -Explain capture-by-value and capture-by-reference +Explain function argument passing by value or by reference #### Student outcomes @@ -55,10 +55,11 @@ _Max 5 items._ A student should be able to: 1. Use a lambda taking a concrete type as a parameter and return a value -2. transform a function definition into a lambda and use it -3. enumerate trade-offs of code-locality using lambdas vs code reuse with free-functions -4. assign a lambda to a variable for multiple calls -5. named lambdas to increase code readability +2. Transform a function definition into a lambda and use it +3. Enumerate trade-offs of code-locality using lambdas vs code reuse with free-functions +4. Assign a lambda to a variable for multiple calls +5. Named lambdas to increase code readability +6. Explain capture-by-value and capture-by-reference @@ -83,9 +84,10 @@ A student should be able to: 1. Utilize `std::function` as a means to hold and transfer lambdas 2. Define a function-template taking a lambda as a template parameter -3. specify, per parameter, whether to capture by reference or value -4. specify the default capture type for a lambda -5. use a lambda in a class, capturing and utilizing class data via `this` +3. Specify, per parameter, whether to capture by reference or value +4. Specify the default capture type for a lambda +5. Specify the return type for a lamda function, if not detected by the compiler +5. Use a lambda in a class, capturing and utilizing class data via `this` #### Caveats @@ -111,25 +113,25 @@ guidance where one can continue to investigate this topic in more depth._ A student should be able to: 1. Use a lambda to introduce a new identifier for use within the body of the lambda -2. explain the relationships between generic lambdas and templates -3. construct an object with choice between 2 identifiers, using immediately-dispathced lambda -4. utilize an immediately dispatched lambda to encode multiple statements where the language requires an expression -5. use the `mutable` keyword to allow changing a captured-by-value or lambda-local value within the lambda body -6. explicitly specify the return type for a lambda -7. explain under what conditions an explicit return type is necessary - +2. Explain the relationships between generic lambdas and templates +3. Construct an object with choice between 2 constructors, using immediately-dispatched lambda (see first listing) +4. Utilize an immediately dispatched lambda to encode multiple statements where the language requires an expression +5. Use the `mutable` keyword to allow changing a captured-by-value or lambda-local value within the lambda body +6. Explicitly specify the return type for a lambda +7. Explain under what conditions an explicit return type is necessary + #### Caveats #### Points to cover -Construct an object with choice between 2 identifiers: +Construct an object with choice between 2 constructors: ``` class X; bool b; X x = [c = b](){ if (c) return X(5, 10) else X("a", "b"); }(); ``` -multiple statements in a single expression +Todo add caption ``` assert([](){ std::vector v{1, 2, 3, 4, 5}; @@ -137,12 +139,14 @@ assert([](){ }()); ``` -Capture can introduce new names + +Capture can introduce new names by initializing them as `capture-default` ``` char const* const s = "5"; [x=atoi(s)](){} ``` +`Capture-default` variables can be altered by the lambda body if the lamda is marked as `mutable` ``` std::ranges::for_each( std::vector{1,2,3,4,5}, From 581c8c102223dd60aea63e92237473d1497f32da Mon Sep 17 00:00:00 2001 From: Patrick Diehl Date: Thu, 13 Feb 2025 16:58:49 -0700 Subject: [PATCH 4/4] Add old example --- sources/modules/functions/lambdas.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sources/modules/functions/lambdas.md b/sources/modules/functions/lambdas.md index 53e68312..1401888c 100644 --- a/sources/modules/functions/lambdas.md +++ b/sources/modules/functions/lambdas.md @@ -161,6 +161,14 @@ std::ranges::for_each( }); ``` +In a context where only one expression is allowed, like `assert`, one can use an immediately lambda to write multiple statements. +``` +assert([](){ + std::vector v{1, 2, 3, 4, 5}; + for (auto x: v) std::cout << v << std::endl; +}()); +``` + When to use lambdas or not: forcing code into a lambda can prevent some features ```