I am writing library which allows some function programming operations on ranges. Ranges are generalization of STL containers. My question is what result of folding for empty range should be?
auto r = range(4); // lazy numeric range {0,1,2,3}
auto r0 = range(0); // empty range {}
vector<string> vs {"a", "bb"};
vector<string> vs0 {};
// this is obvious and implemented part
cout << (r || add); // 6, || - folding op
cout << (r0 || add); // 0
cout << (vs || add); // "abb"
cout << (vs0|| add); // ""
cout << (r || mul); // 0
cout << (r0 || mul); // 1
cout << (r || max); // 3
// What result of these should be?
cout << (r0 || div); // ???
cout << (r0 || sub); // ???
cout << (r0 || max); // -∞ ???
cout << (r0 || min); // +∞ ???
cout << (r0 || ???); // result of arbitrary op?
EDIT – ANSWER
I assume your “folders” are instances of some template, with a binary function attached and maybe an initial value.
Traditionally, fold is defined as recursively calling said binary function on (initial, first), then (old value, next) until you run out of stuff to call it on.
There is no such initial value such that subtraction and division work the way you might expect them to (such that fold({1,2}) is 1/2).
Thus “folders” of subtraction and division are either “sums of inverse” and “product of inverse” (ie, fold(r) = 1/fold(r), and fold(r) = -fold(r), which seems pretty boring), or they are fundamentally different things that don’t work on empty containers.
max and min should clearly generate the highest and lowest value for a given type, or be the folders of the second type that do not make sense on empty containers.
By “not work”, they could throw an exception, or they could return something like a
boost::optional<T>— ie, on an empty list, they do not return anything.Your folder type could take a function which finds the initial value for a given type, which should resolve to a traits template class or a free function (similar to
std::begin).…
Edit: from the comments below, an improvement to the answer.
The real trick here is that there is no left-hand identity for subtraction and division But there is a right hand identity!
Operations that only have a right hand identity should be expressed as right-hand folds, and operations that only have a left hand identity should be expressed as left-hand folds (aka, foldr and foldl)
Ie, the natural way to express a fold on a list
{a,b,c}with identityidof binary operation*op*is:but for operations without a left-hand identity, this doesn’t work.
However, if you reverse the fold handedness, you get this:
which works so long as you have a right-hand identity.
This is important for
divandsub—divhas a right-hand identity of 1, andsubhas a right-hand identity of 0, but neither has a left-hand identity. (there is no elementesuch thate-x = xfor allx. There is an elementesuch thatx-e = xfor allx, namely 0).The same is true of exponentiation (which has a right-hand identity of
1, but no left-hand identity).This still doesn’t match what a naive expectation of what
fold divshould do. It works on lists of length 2, but on lists of length 3 something unintuitive happens. But at least it is mathematically sound. 🙂