This question让我觉得“根本不要使用显式循环!使用STL / Boost算法“但仔细查看,我注意到有adjacent_difference
,accumulate
和Boost在某处有zip
,
while (i<l-1){
ans = ans + max(abs(X[i]-X[i+1]), abs(Y[i]-Y[i+1]));
i++;
}
他们根本不会堆叠在一起,但每个人只能自己完成一次。因此,以直接的方式使用它们需要许多包含部分结果的中间副本。也就是说,让adjacent_difference写一个新的向量,这是zip等的参数。
现在在“现代”C ++中,口头禅是我们不应该“编写代码”并且很少需要显式循环。
但我的实际经验更像是这样的情况:要做的事情不是一个简单的步骤,结果不会像那样收集。
那么,如何以简化的方式编写,指的是要执行的操作,而不是在范围内循环而不是明确地拉动每个元素。
Boost迭代器过滤器可以通常构建更复杂的逻辑,最终在驱动循环内部(因此没有中间结果的整体复制)但是这个例子有几个功能说明了我找到的东西限制Boost范围滤波器!设置它比编写for
循环更复杂!
所以,如果C ++“谁是谁”说我们应该能够用新的语言和库功能编写这种方式,你如何做到这里,一个更真实的简单案例 - 世界比他们在演讲中所表现的那样?
答案 0 :(得分:2)
只使用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
adjacent_difference的范围适配器
请注意,我没有复制std::adjacent_difference
的行为,因为它包含结果中的第一个源值(我们不想要)。相反,我们需要n-1个微分值。
我已接受§3.1 in the docs的说明,加上iterator_facade
to reduce typing的一些说明:
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 = {};
} }
此演示程序测试100个不同的随机范围以获得正确的结果:它运行问题(foo
)和范围化版本(foo_ex
)的原始算法并验证结果。
<强> Live On Coliru 强>
#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。
答案 1 :(得分:-1)
它可能会或可能没有帮助实际生产代码现在,但这似乎与v3 Range library正好解决,Reference here是最终将成为标准库的一部分的原型。 / p>
范围相对于迭代器的最大优势是它们的可组合性。它们允许一种功能性的编程风格,通过将数据传递给一系列组合器来操纵数据。此外,组合器可以是惰性的,只在请求答案时才能正常工作,并且纯粹功能齐全,而不会改变原始数据。
此介绍页面上的第一个示例是 piping 操作,并且记录的第一件事(字母顺序的意外)是view::adjacent_filter
。
我还没有安装并尝试过它并学会了如何编写这个特定的例子,但我确实认为这是缺失的部分。我希望它在今天的代码中足够可用。