仅使用 Boost Range,您想编写:
auto ans = boost::accumulate(
boost::combine(X|differential|abs, Y|differential|abs),
0ull,
[](auto accum, auto const& xy) { return accum + std::max(boost::get<0>(xy), boost::get<1>(xy)); }
);
这可以通过一些方便的工作来实现。
abs
绝对值的范围适配器
我在这里作弊,因为我不想麻烦在这里创建一个真正的适配器范围:
auto abs = transformed([](auto x) { return std::abs(x); });
就这样。
differential
用于相邻差异的范围适配器
请注意,我没有复制 的行为,std::adjacent_difference
因为它在结果中包含第一个源值(我们不想要)。相反,我们想要 n-1 个差分值。
我已经从文档中的 §3.1 中获取了说明,并结合了一些iterator_facade
减少输入的内容:
namespace boost { namespace adaptors {
template <typename R>
struct differential_range {
public:
using base_iterator = typename boost::range_iterator<R const>::type;
struct iterator : boost::iterator_facade<iterator, int, typename boost::iterator_category<base_iterator>::type, int>
{
iterator(base_iterator raw) : _raw(raw) {}
private:
friend class boost::iterator_core_access;
bool equal(iterator other) const { return _raw == other._raw; }
void decrement() { --_raw; }
void increment() { ++_raw; }
int dereference() const { return *next() - *_raw; }
ptrdiff_t distance_to(iterator other) const { return std::distance(_raw, other._raw); }
base_iterator _raw;
base_iterator next() const { return std::next(_raw); }
};
using const_iterator = iterator;
differential_range(R &r) : _b(boost::begin(r)), _e(boost::end(r)) {
if (_b != _e)
--_e;
}
const_iterator begin() const { return _b; }
const_iterator end() const { return _e; }
iterator begin() { return _b; }
iterator end() { return _e; }
private:
iterator _b, _e;
};
没什么特别的。现在我们需要安装转发器,以便我们可以使用| differential
语法简写:
namespace detail {
struct adjacent_difference_forwarder {
};
}
template <class BidirectionalRng>
inline differential_range<BidirectionalRng> operator|(BidirectionalRng &r,
detail::adjacent_difference_forwarder) {
return differential_range<BidirectionalRng>(r);
}
template <class BidirectionalRng>
inline differential_range<const BidirectionalRng> operator|(const BidirectionalRng &r,
detail::adjacent_difference_forwarder) {
return differential_range<const BidirectionalRng>(r);
}
static const detail::adjacent_difference_forwarder differential = {};
} }
演示
foo
这个演示程序测试了 100 个不同的随机范围以获得正确的结果:它运行来自问题 ( ) 和范围化版本 ( )的原始算法foo_ex
并验证结果。
住在科利鲁
#include <vector>
#include <vector>
#include <algorithm>
#include <cassert>
template <typename Range>
int64_t foo(Range const& X, Range const& Y) {
assert(Y.size() == X.size());
size_t const l = X.size();
int64_t ans = 0;
for (size_t i=0; i<l-1; ++i) {
ans = ans + std::max(std::abs(X[i]-X[i+1]), std::abs(Y[i]-Y[i+1]));
}
return ans;
}
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/combine.hpp>
#include <boost/range/numeric.hpp>
#include <boost/iterator/iterator_facade.hpp>
using namespace boost::adaptors;
namespace boost { namespace adaptors {
template <typename R>
struct differential_range {
public:
using base_iterator = typename boost::range_iterator<R const>::type;
struct iterator : boost::iterator_facade<iterator, int, typename boost::iterator_category<base_iterator>::type, int>
{
iterator(base_iterator raw) : _raw(raw) {}
private:
friend class boost::iterator_core_access;
bool equal(iterator other) const { return _raw == other._raw; }
void decrement() { --_raw; }
void increment() { ++_raw; }
int dereference() const { return *next() - *_raw; }
ptrdiff_t distance_to(iterator other) const { return std::distance(_raw, other._raw); }
base_iterator _raw;
base_iterator next() const { return std::next(_raw); }
};
using const_iterator = iterator;
differential_range(R &r) : _b(boost::begin(r)), _e(boost::end(r)) {
if (_b != _e)
--_e;
}
const_iterator begin() const { return _b; }
const_iterator end() const { return _e; }
iterator begin() { return _b; }
iterator end() { return _e; }
private:
iterator _b, _e;
};
namespace detail {
struct adjacent_difference_forwarder {
bool absolute = false;
};
}
template <class BidirectionalRng>
inline differential_range<BidirectionalRng> operator|(BidirectionalRng &r,
detail::adjacent_difference_forwarder) {
return differential_range<BidirectionalRng>(r);
}
template <class BidirectionalRng>
inline differential_range<const BidirectionalRng> operator|(const BidirectionalRng &r,
detail::adjacent_difference_forwarder) {
return differential_range<const BidirectionalRng>(r);
}
static const detail::adjacent_difference_forwarder differential = {};
} }
template <typename Range>
int64_t foo_ex(Range const& X, Range const& Y) {
auto abs = transformed([](auto x) { return std::abs(x); });
return boost::accumulate(
boost::combine(X|differential|abs, Y|differential|abs),
0ull,
[](auto accum, auto const& xy) { return accum + std::max(boost::get<0>(xy), boost::get<1>(xy)); }
);
}
#include <iostream>
#include <random>
int main() {
std::vector<int> x(100), y=x;
std::mt19937 rng { std::random_device{}() };
std::uniform_int_distribution<int> dist(-50, 50);
auto gen = [&] { return dist(rng); };
int n = 100;
while (n--) {
std::generate(x.begin(), x.end(), gen);
std::generate(y.begin(), y.end(), gen);
auto ans = foo(x,y),
ans_ex = foo_ex(x,y);
std::cout << ans << " " << ans_ex << "\t" << std::boolalpha << (ans==ans_ex) << "\n";
}
}
打印正确的结果,例如:
4769 4769 true
5027 5027 true
4471 4471 true
4495 4495 true
4774 4774 true
4429 4429 true
4331 4331 true
4951 4951 true
4095 4095 true
...
想法,总结
您可能可以differential
更一般地想象... adjacent_transformed
,您可以在哪里说
auto differential = adj_transformed([](auto x, auto y) { return y - x; });
这将使代码重用变得更加容易,不需要为任何新的相邻二进制变换使用全范围适配器。有关指导,请参阅第 3.2 节。