Martin Sulzmann
int myAdd(int x, int y) {
return x+y;
}
void testAdd() {
cout << "\n" << myAdd(1,3);
auto myAdd2 = [](int x, int y) -> int { return x+y; };
cout << "\n" << myAdd2(1,3);
}
C++ lambda notation:
[capture](parameters) -> return_type { function_body }
Capture list specifies non-local variables used in function body (more on this below)
List of parameters
Return type ( can be inferred, see examples below)
Function type
auto x = 1;
x
std::function<int(int)> myPlus(int x) {
return [x](int y) -> int { return x+y; };
}
void testPlus() {
cout << "\n" << myPlus(1)(3);
auto inc = myPlus(1);
cout << "\n" << inc(3);
auto myPlus2 = [](int x) -> std::function<int(int)> { return [x](int y) -> int { return x+y; }; };
auto myPlus3 = [](int x) { return [x](int y) { return x+y; }; };
cout << "\n" << myPlus2(1)(3);
cout << "\n" << myPlus3(1)(3);
}
Partial function application, see inc(3)
.
Inference of return types, see myPlus3
.
void testLambdaCapture() {
auto x = 1;
auto f_copy = [x] (int y) -> int { return x+y; };
auto f_ref = [&x] (int y) -> int { return x+y; };
cout << "\n" << f_copy(3);
cout << "\n" << f_ref(3);
x = 2;
cout << "\n" << f_copy(3);
cout << "\n" << f_ref(3);
}
Variable x
is used in the lambda function f
.
Must specify if we wish capture by copy or by reference.
Capture by copy written [x]
. "Easier" for compiler, no need to track non-local variables.
Capture by reference written [&x]
. Each access to x
will retrieve its current value.
template<typename T>
void mySwap(T& x, T&y) {
T tmp;
tmp = x; x = y; y = tmp;
}
void testSwap() {
int x = 1; int y = 3;
cout << "\n" << x << y;
mySwap(x,y);
cout << "\n" << x << y;
}
Templates are "not real".
Templates will be instantiated. That is, we create a copy of the code for each specific instance. This process is called monomorphisation.
The Template-free code will then be processed by the compiler.
void mySwap_int(int& x, int&y) {
int tmp;
tmp = x; x = y; y = tmp;
}
void testSwap_int() {
int x = 1; int y = 3;
cout << "\n" << x << y;
mySwap_int(x,y);
cout << "\n" << x << y;
}
string Show(bool x) {
return x ? "true" : "false";
}
// Missing type bound, we require Show(T).
template<typename T>
string Show(vector<T> xs) {
string s;
s = "[";
for(int i=0; i < xs.size(); i++) {
s = s + Show(xs[i]);
if (i != xs.size() - 1) {
s = s + ",";
}
}
s = s + "]";
return s;
}
void testShow() {
vector<bool> xs;
xs.push_back(true);
xs.push_back(false);
cout << Show(xs);
}
We use here overloading in combination with templates.
Roughly corresponds to the "Show" instances we have seen in Haskell.
The "instance" for vectors is missing a type bound!
We only consider type-specific test data generation.
We make use of lambdas to emulate lazy evaluation of generators.
We make use of template specialization to implement "arbitrary".
Recall that arbitrary
is a function overloaded on the return type. However, C++ overloading is more restricted. Overloading must be resolved based on the argument types only. Hence, we resort to template specialization.
// Generator interface.
// The actual generator is a parameter-less function.
// We "force" generation by calling this function.
// In essence, we emulate here "lazy" evaluation.
template<typename T>
class Gen {
public:
Gen() {};
Gen(std::function<T()> gen_) { gen = std::function<T()>(gen_); }
std::function<T()> gen;
T generate() { return gen(); };
};
// Computing an arbitrary generator.
// Uses template specialization.
template<typename T>
Gen<T> arbitrary();
// Some instances
// bool
template<>
Gen<bool> arbitrary() { return Gen<bool>([] () -> bool { return (rand() % 2 == 0) ? true : false; }); }
// char
template<>
Gen<char> arbitrary() { return Gen<char>([] () -> char { return (char)(rand() % 255); }); }
// vector<char>
template<>
Gen<vector<char>> arbitrary() { return Gen<vector<char>>([] () -> vector<char> {
auto n = rand() % 11; // max of 10 characters
vector<char> vs;
auto ch = arbitrary<char>();
for(int i=0; i < n; i++) {
vs.push_back(ch.generate());
}
return vs; });
}
void testGen() {
auto g = arbitrary<vector<char>>();
vector<char> vs = g.generate();
string s(vs.begin(), vs.end());
cout << "\n" << s;
}
Point to note. It seems impossible to a "generic" arbitrary instance for vector<T>
.
// Usage: g++ --std=c++11
#include <functional>
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
//////////////////////////////////////////////////
// Lambdas and local type inference (auto)
// Lambdas in C++
int myAdd(int x, int y) {
return x+y;
}
void testAdd() {
cout << "\n" << myAdd(1,3);
auto myAdd2 = [](int x, int y) -> int { return x+y; };
cout << "\n" << myAdd2(1,3);
}
// More lambdas.
std::function<int(int)> myPlus(int x) {
return [x](int y) -> int { return x+y; };
}
void testPlus() {
cout << "\n" << myPlus(1)(3);
auto inc = myPlus(1);
cout << "\n" << inc(3);
auto myPlus2 = [](int x) -> std::function<int(int)> { return [x](int y) -> int { return x+y; }; };
auto myPlus3 = [](int x) { return [x](int y) { return x+y; }; };
cout << "\n" << myPlus2(1)(3);
cout << "\n" << myPlus3(1)(3);
}
// Capture by copy versus capture by reference
void testLambdaCapture() {
auto x = 1;
auto f_copy = [x] (int y) -> int { return x+y; };
auto f_ref = [&x] (int y) -> int { return x+y; };
cout << "\n" << f_copy(3);
cout << "\n" << f_ref(3);
x = 2;
cout << "\n" << f_copy(3);
cout << "\n" << f_ref(3);
}
////////////////////////////////////
// Templates
template<typename T>
void mySwap(T& x, T&y) {
T tmp;
tmp = x; x = y; y = tmp;
}
void testSwap() {
int x = 1; int y = 3;
cout << "\n" << x << y;
mySwap<int>(x,y);
cout << "\n" << x << y;
}
// C++ applies "monomorphisation"
void mySwap_int(int& x, int&y) {
int tmp;
tmp = x; x = y; y = tmp;
}
void testSwap_int() {
int x = 1; int y = 3;
cout << "\n" << x << y;
mySwap_int(x,y);
cout << "\n" << x << y;
}
// Show
string Show(bool x) {
return x ? "true" : "false";
}
// Missing type bound, we require Show(T).
template<typename T>
string Show(vector<T> xs) {
string s;
s = "[";
for(int i=0; i < xs.size(); i++) {
s = s + Show(xs[i]);
if (i != xs.size() - 1) {
s = s + ",";
}
}
s = s + "]";
return s;
}
void testShow() {
vector<bool> xs;
xs.push_back(true);
xs.push_back(false);
cout << Show(xs);
}
// "arbitrary" see QuickCheck
// Generator interface.
// The actual generator is a parameter-less function.
// We "force" generation by calling this function.
// In essence, we emulate here "lazy" evluation.
template<typename T>
class Gen {
public:
Gen() {};
Gen(std::function<T()> gen_) { gen = std::function<T()>(gen_); }
std::function<T()> gen;
T generate() { return gen(); };
};
// Computing an arbitrary generator.
// Uses template specialization.
template<typename T>
Gen<T> arbitrary();
// Some instances
// bool
template<>
Gen<bool> arbitrary() { return Gen<bool>([] () -> bool { return (rand() % 2 == 0) ? true : false; }); }
// char
template<>
Gen<char> arbitrary() { return Gen<char>([] () -> char { return (char)(rand() % 255); }); }
// vector<char>
template<>
Gen<vector<char>> arbitrary() { return Gen<vector<char>>([] () -> vector<char> {
auto n = rand() % 11; // max of 10 characters
vector<char> vs;
auto ch = arbitrary<char>();
for(int i=0; i < n; i++) {
vs.push_back(ch.generate());
}
return vs; });
}
void testGen() {
auto g = arbitrary<vector<char>>();
vector<char> vs = g.generate();
string s(vs.begin(), vs.end());
cout << "\n" << s;
}
int main() {
testAdd();
testPlus();
testLambdaCapture();
testSwap();
testShow();
testGen();
}