I got a suggestion from Ivan Čukić, author of the upcoming book Functional Programming in C++. He gave me the same suggestion as +Casey, to use pairs, although I fleshed out the implementation a little differently, and made it generic. I used zip to make pairs (tuples of 2). The full solution is below, for the future reference of anyone curious.
I'm not happy with the code. It certainly isn't pretty, and I feel like there's a lot of room for clear-headed thinking to simplify the it. But I've spent my time investment. Further improvements welcomed.
#include <iostream>
#include <list>
#include <range/v3/all.hpp>
using namespace ranges::view;
// create a lazy stream given a function f and a seed value a0.
// stream will contain [a0, f(a0), f(f(a0)), f(f(f(a0))),...]
template <typename T> //
auto repeat_stream(std::function<T(T)> f, T a0) {
return generate([ f, a = a0 ]() mutable {
auto prev = a;
a = f(a);
return prev;
});
}
// Consumes a stream until cosnecutive values differ within eps, and then the
// latest value is returned.
template <typename E, typename T>
E within(E eps, T v) {
std::list<std::tuple<E, E>> converging_list =
zip(v, v | drop(1))
| take_while([eps](std::tuple<E, E> x) {
return std::abs(std::get<0>(x) - std::get<1>(x)) > eps;
});
return std::get<0>(converging_list.back());
}
// Newton-Raphson Square Roots
// A la Hughes 1989 "Why Functional Programming Matters"
// http://www.cs.utexas.edu/~shmat/courses/cs345/whyfp.pdf
float nr_sqrt(float x, float x0) {
return within(
1E-15,
repeat_stream<float>([n = x](float a) { return (a + n / a) / 2; }, x0));
}
int main() {
std::cout << nr_sqrt(9, 4) << std::endl;
return 0;
}
Edit: It bothered me that Hughes functional program, as transcribed by me into C++, was perhaps the dumbest program ever written to compute a square root. I decided to take another hack at the problem.
This time, I abstracted out the process of finding a fixed-point of the function. I used a recursive lambda to create the iteration loop, and std::bind
to partially apply N
in the Newton-Raphson step:
#include <cmath>
#include <functional>
#include <iostream>
using namespace std::placeholders;
template <typename T>
T fixedpoint(std::function<T(T)> f, T start, T eps) {
std::function<T(T, T)> iter = [&iter, eps, &f](T old, T nw) -> T {
if (std::abs(old - nw) < eps)
return nw;
else
return iter(nw, f(nw));
};
return iter(start, f(start));
}
auto nr_step(double N, double a) { return (a + N / a) / 2; }
int main() {
std::cout << fixedpoint<double>(std::bind(nr_step, 3, _1), 1.2, 1E-10)
<< std::endl;
return 0;
}
If anyone knows how to resolve std::function<T(T)>
with automatic type deduction so that I don't have to specify fixedpoint<double>
I would be happy to hear about it.