生成器take_while基于元素之间的差异

时间:2017-06-01 04:37:57

标签: c++ c++14 range-v3

我试图从1989年paper"为什么功能编程很重要"来尝试近似Hughes功能版的Newton-Raphson平方根算法。

我感谢任何有关替代方法的建议:越多越好。我目前的方法是使用Niebler的range-v3。您将在代码片段中看到我创建了一个生成器来创建连续的迭代并将它们插入流中。我的问题是终止条件。我需要检测流中连续浮点数之间的差异何时低于阈值:

#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges::view;

int main() {

  auto sqrt_stream = generate([ x = 1.f, n = 3.f ]() mutable {
    auto prevx = x;
    x = (x + n / x) / 2;
    return prevx;
  });

  std::vector<float> sequence = sqrt_stream | take_while([](int x) { ??? });

  return 0;
}

我不确定如何进行任何购买来比较连续元素,例如简单的前向差异。即使我这样做了,一旦我将流转换为前向差异,我将如何从生成器中恢复适当的元素?

2 个答案:

答案 0 :(得分:1)

我能想到的最简单的解决方案是生成序列元素对并按连续差异(live)进行过滤:

  auto pair_stream = generate([ x = 1., n = 7. ]() mutable {
    auto prevx = x;
    x = (x + n / x) / 2;
    return std::make_pair(prevx, x);
  });

  auto rng = pair_stream | take_while([](auto&& pair) {
    return std::abs(pair.second - pair.first) > epsilon;
  }) | transform([](auto&& x) { return x.second; });

必须省略序列中的第一个估计值。

答案 1 :(得分:0)

我得到了即将出版的book 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;
}

编辑: 令我感到困扰的是Hughes功能程序,由我转录到C ++中,可能是用于计算平方根的最愚蠢的程序。我决定再解决这个问题。

这一次,我抽象出了找到函数定点的过程。我使用递归lambda来创建迭代循环,并使用std::bind在Newton-Raphson步骤中部分应用N

#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>我会很高兴听到它。