我从即将出版 的《C++ 函数式编程》一书的作者 Ivan Čukić 那里得到了一个建议。他给了我与 +Casey 相同的建议,即使用对,尽管我对实现进行了一些不同的充实,并使其具有通用性。我使用 zip 来制作对(2 个元组)。完整的解决方案如下,供任何好奇的人将来参考。
我对代码不满意。它当然不漂亮,而且我觉得有很大的空间可以让头脑清醒的思维简化它。但我已经把时间花在了投资上。欢迎进一步改进。
#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;
}
编辑:让我感到困扰的是,休斯函数程序,由我转录成 C++,可能是有史以来最愚蠢的计算平方根的程序。我决定再次解决这个问题。
这一次,我把寻找函数不动点的过程抽象出来了。我使用递归 lambda 来创建迭代循环,并std::bind
部分应用于N
Newton-Raphson 步骤:
#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;
}
如果有人知道如何std::function<T(T)>
通过自动类型推断来解决,这样我就不必指定fixedpoint<double>
我会很高兴听到它。