11 Functional Programming
C++ is a multi-paradigm programming language. Meaning that it supports multiple programming paradigms (like object-oriented or functional).
Functional Programming
In functional programming, functions are treated as first-class citizens. This means that functions can be passed as arguments to other functions and returned from functions. Objects are immutable and state is not shared between functions (functions are pure = have no side effects).
Elements of Functional Programming
- Functions
- Functor: Class instances that overloads the function call operator
operator()(...)) - Function Pointer: Pointer to a function (address of a function)
- Method Pointer: Pointer to an instance-bound method (address of a method)
- Lambda: Anonymous functor
- Function Object: Generalization of all of the above (
functionalfrom header<functional>)
Functor
Example of a functor that calculates the sine of a value in radians, degrees or gradians:
#include <cmath>
enum Mode { Rad, Deg, Grad };
class Sine {
Mode m_mode;
public:
explicit Sine(Mode mode = Rad) : m_mode(mode) {}
double operator()(double x) const {
switch (m_mode) {
case Rad: return sin(x);
case Deg: return sin(x * M_PI / 180);
case Grad: return sin(x * M_PI / 200);
}
}
};
Lambda
Lambdas in C++ are declared using the following syntax:
[capture](parameters) -> return_type { body }
The capture clause (lambda-introducer) contains a list of variables that are captured by the lambda from the surrounding scope. The capture clause can be empty (no variables are captured) or it can be one of the following:
[]cpatures no variables from outside scope[x]capturesxby value[&x]capturesxby reference[&]captures all variables by reference[=]captures all variables by value[&, x]captures all variables by reference, exceptxwhich is captured by value[=, &x]captures all variables by value, exceptxwhich is captured by reference[this]captures thethispointer by value[this, x]captures thethispointer by value andxby value[this, &x]captures thethispointer by value andxby reference[=, this]captures all variables by value and thethispointer by value[&, this]captures all variables by reference and thethispointer by value[=, &this]captures all variables by value and thethispointer by reference[&, &this]captures all variables by reference and thethispointer by reference
Example of a lambda that adds a captured bias to the sum of two values:
auto add = [bias](int a, int b) { return a + b + bias; };
Lambda Behind the Scenes
class Op {
const int m_val; // Captured by value
int& m_ref;
Op(int value, int eew& ref) : m_val(value), m_ref(ref) {}
int operator()(int i) const {
++m_ref = return i + m_val + ref;
}
} op(byval, byref) // Instance creation directly after class definition
Closure
A closure is a lambda that captures variables from the surrounding scope. The captured variables are stored in the closure object.
int f = 2; // local variable
auto l1 = [&f](int x) { return x*f++; }; // lambda; f passed by reference
auto l2 = [f](int x) mutable { return x*f++; }; // closure; f passed by value
std::cout << "value = " << l1(3) << ", f = " << f << std::endl; // value = 6, f = 3
std::cout << "value = " << l1(3) << ", f = " << f << std::endl; // value = 9, f = 4
std::cout << "value = " << l2(3) << ", f = " << f << std::endl; // value = 6, f = 4 (f is passed by value at creation of closure)
std::cout << "value = " << l2(3) << ", f = " << f << std::endl; // value = 9, f = 4 (internal f of closure has been updated by previous call)
Function Object
A function object encapsulates a function or function pointer which can then be passed to another function.
#include <functional>
float foo(float a, float x) { return a + x/2.0f; }
template<typename Iter, typename AT, typename Func>
AT accumulate(Iter beg, Iter end, AT a, Func f) {
while(beg != end) {
a = f(a, *beg++);
}
return a;
}
void main() {
function<float (float, float)> func; // function object: returns float, takes float and int
vector<int> v{1,2,3,4,5}
func = [](float x, float y) { return x * y; }; // lambda
cout << accumulate(v.cbegin(), v.cend(), 1.0f, func); << endl; // 120
cout << accumulate(v.cbegin(), v.cend(), 1.0f, &foo); << endl; // 8.5
}
Method Pointer
A method pointer is a pointer to a method of a class. It can be used to call the method on an instance of the class.
struct A {
float m_div;
A(float div) : m_div(div) {}
float meth(float a, float x) const { return a+x/2.0f; }
};
template<typename Iter, typename AT, typename Func>
AT accumulate(Iter beg, Iter end, AT a, Func f) {
while(beg != end) {
a = f(a, *beg++);
}
return a;
}
void main() {
function<float (float, float)> func;
vector<int> v{1,2,3,4,5};
A a(2.0f);
func = bind(&A::meth, &a, placeholders::_1, placeholders::_2);
cout << accumulate(v.cbegin(), v.cend(), 1.0f, func) << endl;
}