<aside> 💡 Template Functions

template<typename T> T min(Tx, Ty) {
	return x < y ? x : y;
}

int f() {
	int x = 1, y = 2;
	int z = min(x, y); // C++ infers T = int from the types of x & y
}

If C++ can’t determine T, you can tell it: int z = min<int>(x, y);

min('a', 'c'); // T = char
min(1.0, 3.0); // T = double

For what types T can min be used? For what types T does the body copile?

↑ the end of Exam coverings


Recall:

void foreach(AbstractIterator &start, AbstractIterator &finish, void(*f)(int)) {
	while(start != finish) {
		f(*start);
		++start;
	}
}
// - subtype of Abstract Iterator support !=, *, ++
// - f can be called as a fin
template<typename Iter, typename Fn>
	void foreach(Iter start, Iter finish, Fn f) {
	// as before
}

<aside> 💡 <algorithm> library:

example: for_each (as above)

template<typename Iter, typename T> 
	Iter find(Iter first, Iter last, const T &val) {
	// - return iterator to first ite min[first, last) matching val
	// - or last, if not found
}

</aside>

<aside> 💡 count - like find, but returns # occurrences of val

template<typename InIter, typename OutIter>
	OutIter copy(InIter first, InIter last, OutIter result) {
	// copies one container range [first, last) to another, starting at result
} // Note: does **not** allocate memoty
vector v{1, 2, 3, 4, 5, 6, 7};
vector <int>w(4); // 0 0 0 0
copy(v.begin() + 1, v.begin() + 5, w.begin());
	// w = {2, 3, 4, 5};

</aside>

<aside> 💡 typename Fn

template<typename InIter, typename OutIterm typename Fn>
OutIter transform(InIter first, InIter last, OutIter result, Fn f) {
	while(first != last) {
		*result = f(*first);
		++first;
		++result;
	}
	return result;
}
int add1(int n) { return n+1; }
...
vector v{2, 3, 5, 7, 11};
vector<int> w(v.size());
transform(v.begin(), v.end(), w.begin(), add1);
// w = {3, 4, 6, 8, 12};

How generalized is this code?

  1. What can we use for Fn ?
  2. What can we use for InIter/OutIter ? </aside>

<aside> 💡 1) Fn

class Plus1 {
	public:
	int operator()(int n) { return n+1; }
};
Plus1 p;
p(4); // 5

transform(v.begin(), v.end(), w.begin(), Plus1{});

// Generalize:
clsss Plus {
	int m;
	public:
	Plus(int m): m{m} {}
	int operator()(int n){ return n + m; }
};

transform(v.begin(), v.end(), w.begin(), Plus{1});
// Plus1{}, Plus{1}, p - called **function objects**(sometimes functors)

<aside> 💡 Advantage of f’n objs - can maintain state

class Increasing Plus {
	int m = 0;
	public:
	int operator()(int n){ return n+(m++); }
	void reset() { m = 0; }
};
vector<int>(5, 0);
vector<int>w(v.size(), 0);
transform(v.begin(), v.end(), m.begin(), Increasing Plus{});
// w = {0, 1, 2, 3, 4}
// Consider: How many ints in a vector v are even?
vector v{ ____ };
bool even(int n){ return n % 2 == 0; }
int x = count_if(v.begin(), v.end(), even);
// If this were Racket, we might use lambda. Do the same here
int x = count_if(v.begin(), v.end(), w.begin(), 
									[](int n) { return n % 2 == 0; });
							// |_______________________________|

</aside>

</aside>

<aside> 💡 2) Iterators

import<iterator>
vector v{1, 2, 3, 4, 5};
ostream_iterator<int> out {cout, '', ''};
copy(v.begin(), v.end(), out); // Prints 1, 2, 3, 4, 5
vector<int>w;
copy(v.begin(), v.end(), w.begin());
// X Copy doesn't allocate space in w
// - it can't - it doesn't even know that it's iterating over a vector !

But what if we had an iterator whose assignment operator inserts a new item?

copy(v.begin(), v.end(), back_inserter(w)); 
// copies v to the end of w, adds new item
// back_inserter(w): available for any container with a push_back method

</aside>

<aside> 💡 Range Abstraction

Constructor the method vector<T>::erase

O(n) cost for shuffling item. Fine

But...

Faster - shuffle the items up k pos’n (in one step each), where k = # items being erased

For this reason, most methods come with a “range” version:

iterator vector<T>::erase(iterator first, iterator last);
// erases item in [first, last) - only pats the linear cost once

Recall: transform taks two iteraots specifying a range [first, last) on input.

So maybe iterator isn;t the right level of abstraction - maybe the right abstraction encapsulations a part of iterators - a range

Consider - composing multuple f’ns on some input

<aside> 🤌🏼 consider: composing multiuple f’ns on some input

filter & transform - say, take all of the odd #’s to square them

auto odd = [](int n){ return n % 2 != 0; }
auto sqr = [](int n){ return n * n; }
vector v{ ______ };
vector<int>w(v.size()), x(v.size());
copy_if(v.begin(), v.end(), w.begin(), odd);
transform(w.begin(), w.end(), x.begin(), sqr);

Problems:

  1. calls don’t conpose well

  2. Intermediate storage - on-demand fetching!

    What if copy_if of transform returned a range?

    oprator so that R | A maps to A(R)

auto x = V | filter(off) | transform(sqr);

</aside>

</aside>