Lamdas
Lamdas
414
Functors: Motivation
A simple output filter
template <typename T, typename Function>
void filter(const T& collection, Function f){
for (const auto& x: collection)
if (f(x)) std::cout << x << " ";
std::cout << "\n";
}
415
Functors: Motivation
A simple output filter
template <typename T, typename Function>
void filter(const T& collection, Function f){
for (const auto& x: collection)
if (f(x)) std::cout << x << " ";
std::cout << "\n";
}
415
Functors: Motivation
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
filter(a,even<int>); // output: 2,4,6,16
416
Context and Overview
Recent C++-lecture: make code parametric on the data it operates
Pair<T> for element types T
print<C> for iterable containers C
Now: make code parameteric on a (part of) the algorithm
filter(container, predicate)
apply(signal, transformation/filter)
We learn about
Higher-order functions: fucntions that take functions as arguments
Functors: objects with overloaded function operator ().
Lambda-Expressions: anonymous functors (syntactic sugar)
Closures: lambdas that capture their environment
417
Functors: Motivation
template <typename T, typename Function>
void filter(const T& collection, Function f){
for (const auto& x: collection)
if (f(x)) std::cout << x << " ";
std::cout << "\n";
}
Requirements on f: must be callable/applicable (...)
f must be a kind of function ⇒ filter is a function that takes a
function as argument
A function taking (or returning) a function is called a higher order
function
Higher order functions are parameteric in their functionality (or they
generate functions)
418
What if...
the filter should be more flexible:
template <typename T, typename function>
void filter(const T& collection, function f);
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int val = 8;
filter(a,largerThan<int>( ? ,val));
419
What if...
the filter should be more flexible:
template <typename T, typename function>
void filter(const T& collection, function f);
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int val = 8;
filter(a,largerThan<int>( ? ,val)); (No, this does not exist)
419
Functor: Object with Overloaded Operator ()
class GreaterThan{
int value; // state
public:
GreaterThan(int x):value{x}{} A Functor is a callable ob-
ject. Can be understood as
bool operator() (int par) const { a stateful function.
return par > value;
}
};
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int value=8;
filter(a,GreaterThan(value)); // 9,11,16,19
420
Functor: object with overloaded operator ()
template <typename T>
class GreaterThan{
T value;
public:
GreaterThan(T x):value{x}{} (this also works with a tem-
plate, of course)
bool operator() (T par) const{
return par > value;
}
};
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int value=8;
filter(a,GreaterThan<int>(value)); // 9,11,16,19
421
Observations
Need to give the predicate a name
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int value=8;
That is just syntactic sugar, from which the compiler generates a suitable
functor.
423
Interlude: Sorting with Custom Comparator
std::sort is generic
in the iterator-type
in the values iterated over
in the used comparator
424
Sorting by Different Order
// pre: i >= 0
// post: returns sum of digits of i
int q(int i){
int res =0;
for(;i>0;i/=10)
res += i % 10;
return res;
}
std::vector<int> v {10,12,9,7,28,22,14};
std::sort (v.begin(), v.end(),
[] (int i, int j) { return q(i) < q(j);}
);
425
Sorting by Different Order
// pre: i >= 0
// post: returns sum of digits of i
int q(int i){
int res =0;
for(;i>0;i/=10)
res += i % 10;
return res;
}
std::vector<int> v {10,12,9,7,28,22,14};
std::sort (v.begin(), v.end(),
[] (int i, int j) { return q(i) < q(j);}
);
Now v =
425
Sorting by Different Order
// pre: i >= 0
// post: returns sum of digits of i
int q(int i){
int res =0;
for(;i>0;i/=10)
res += i % 10;
return res;
}
std::vector<int> v {10,12,9,7,28,22,14};
std::sort (v.begin(), v.end(),
[] (int i, int j) { return q(i) < q(j);}
);
Now v =10, 12, 22, 14, 7, 9, 28 (sorted by sum of digits)
425
Lambda-Expressions in Detail
426
Closure
427
Simple Lambda Expression
428
Simple Lambda Expression
call:
[]()->void {std::cout << "Hello World";}();
428
Simple Lambda Expression
call:
[]()->void {std::cout << "Hello World";}();
assignment:
auto f = []()->void {std::cout << "Hello World";};
428
Minimal Lambda Expression
[]{}
430
Examples
430
Examples
int k = 8;
auto f = [](int& v) {v += v;};
f(k);
std::cout << k;
Output:
431
Examples
int k = 8;
auto f = [](int& v) {v += v;};
f(k);
std::cout << k;
Output: 16
431
Examples
int k = 8;
auto f = [](int v) {v += v;};
f(k);
std::cout << k;
Output:
432
Examples
int k = 8;
auto f = [](int v) {v += v;};
f(k);
std::cout << k;
Output: 8
432
Commonly Used: std::foreach
433
Sum of Elements – Old School
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int sum = 0;
for (auto x: a)
sum += x;
std::cout << sum << std::endl; // 83
434
Sum of Elements – Old School
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int sum = 0;
....
std::cout << sum << std::endl; // 83
435
Sum of Elements – with Functor
template <typename T>
struct Sum{
T value = 0;
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
Sum<int> sum;
// for_each copies sum: we need to copy the result back
sum = std::for_each(a.begin(), a.end(), sum);
std::cout << sum.value << std::endl; // 83
436
Sum of Elements – with Functor
template <typename T>
struct Sum{
T value = 0;
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
Sum<int> sum;
// for_each copies sum: we need to copy the result back
sum = std::for_each(a.begin(), a.end(), sum);
std::cout << sum.value << std::endl; // 83
Ok: solves the problem but does not operate on the sum variable
436
Sum of Elements – with References
template <typename T>
struct SumR{
T& value;
SumR (T& v):value{v} {}
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int s=0;
SumR<int> sum{s};
// cannot (and do not need to) assign to sum here
std::for_each(a.begin(), a.end(), sum);
std::cout << s << std::endl; // 83
437
Of course this works, very similarly, using pointers
Sum of Elements – with Λ
std::vector<int> a {1,2,3,4,5,6,7,9,11,16,19};
int s=0;
438
Capture – Lambdas
439
Capture – Lambdas
std::vector<int> v = {1,2,4,8,16};
int elements=0;
int sum=0;
std::for_each(v.begin(), v.end(),
[&] (int k) {sum += k; elements++;} // capture all by reference
)
std::cout << "sum=" << sum << " elements=" << elements << std::endl;
Output:
440
Capture – Lambdas
std::vector<int> v = {1,2,4,8,16};
int elements=0;
int sum=0;
std::for_each(v.begin(), v.end(),
[&] (int k) {sum += k; elements++;} // capture all by reference
)
std::cout << "sum=" << sum << " elements=" << elements << std::endl;
440
Capture – Lambdas
vector<int> s;
sequence(s, [&] {return s.size() >= 5;} )
now v =
441
Capture – Lambdas
vector<int> s;
sequence(s, [&] {return s.size() >= 5;} )
now v = 0 1 2 3 4
441
Capture – Lambdas
vector<int> s;
sequence(s, [&] {return s.size() >= 5;} )
now v = 0 1 2 3 4
The capture list refers to the context of the lambda expression.
441
Capture – Lambdas
Output:
442
Capture – Lambdas
Output: 42
Values are assigned when the lambda-expression is created.
442
Capture – Lambdas
(Why) does this work?
class Limited{
int limit = 10;
public:
// count entries smaller than limit
int count(const std::vector<int>& a){
int c = 0;
std::for_each(a.begin(), a.end(),
[=,&c] (int x) {if (x < limit) c++;}
);
return c;
}
};
443
Capture – Lambdas
(Why) does this work?
class Limited{
int limit = 10;
public:
// count entries smaller than limit
int count(const std::vector<int>& a){
int c = 0;
std::for_each(a.begin(), a.end(),
[=,&c] (int x) {if (x < limit) c++;}
);
return c;
}
};
The this pointer is implicitly copied by value
443
Capture – Lambdas
struct mutant{
int i = 0;
void action(){ [=] {i=42;}();}
};
mutant m;
m.action();
std::cout << m.i;
Output:
444
Capture – Lambdas
struct mutant{
int i = 0;
void action(){ [=] {i=42;}();}
};
mutant m;
m.action();
std::cout << m.i;
Output: 42
The this pointer is implicitly copied by value
444
Lambda Expressions are Functors
can be implemented as
unnamed {x,y};
with
class unnamed {
int x; int& y;
unnamed (int x_, int& y_) : x (x_), y (y_) {}
void operator () () {y = x;}
};
445
Lambda Expressions are Functors
can be implemented as
unnamed {x,y};
with
class unnamed {
int x; int y;
unnamed (int x_, int y_) : x (x_), y (y_) {}
int operator () () const {return x + y;}
};
446
Polymorphic Function Wrapper std::function
#include <functional>
int k= 8;
std::function<int(int)> f;
f = [k](int i){ return i+k; };
std::cout << f(8); // 16
447
Example
448
Example
auto Gaussian(double mu, double sigma){
return [mu,sigma](double x) {
const double a = ( x - mu ) / sigma;
return std::exp( -0.5 * a * a );
};
}
std::vector<double> v {1,2,5,3};
auto f = toFunction(v);
auto k = Gaussian(0,0.1);
auto g = smooth(f,k);
450
Conclusion
451