cls*_*udt 5 c++ generics templates functional-programming
我是一个被宠坏的Python程序员谁是用来计算argmax的collection相对于一些function用
max(collection, key=function)
Run Code Online (Sandbox Code Playgroud)
例如:
l = [1,43,10,17]
a = max(l, key=lambda x: -1 * abs(42 - x))
Run Code Online (Sandbox Code Playgroud)
a 然后包含43,最接近42的数字.
是否有可能编写一个C++函数,它接受任何"可迭代"和任何函数并返回如上所述的argmax?我想这会涉及模板参数,auto关键字和基于范围的迭代,但我无法将它拼凑在一起.
这是一个两步过程.定义一个key应该映射到元素的函数,即在找到最大值的操作之前应用的函数.在lambda表达式中将事物包装在一起,定义比较以找到最大值.
auto key = [](int x){
return -abs(42 - x);
};
std::max_element(l.begin(), l.end(), [key](int a, int b){
return key(a) < key(b);
});
Run Code Online (Sandbox Code Playgroud)
在这里,我们必须捕获key在第二个lambda函数之外定义的内容.(我们也可以在里面定义它).您也可以将它放在一个lambda函数中.当42应该从lambda外部参数化时,将其捕获为变量:
int x = 42;
std::max_element(l.begin(), l.end(), [x](int a, int b){
return -abs(x - a) < -abs(x - b);
});
Run Code Online (Sandbox Code Playgroud)
注意,std::max_element返回一个迭代器.要访问值/对它的引用,请在前面添加*:
int x = 42;
auto nearest = std::min_element(l.begin(), l.end(), [x](int a, int b){
return abs(x - a) < abs(x - b);
});
std::cout << "Nearest to " << x << ": " << *nearest << std::endl;
Run Code Online (Sandbox Code Playgroud)
你可以很好地将它包装在一个通用find_nearest函数中:
template<typename Iter>
Iter find_nearest(Iter begin, Iter end,
const typename std::iterator_traits<Iter>::value_type & value)
{
typedef typename std::iterator_traits<Iter>::value_type T;
return std::min_element(begin, end, [&value](const T& a, const T& b){
return abs(value - a) < abs(value - b);
});
}
auto a = find_nearest(l.begin(), l.end(), 42);
std::cout << *a << std::endl;
Run Code Online (Sandbox Code Playgroud)
现场演示 find_nearest:http://ideone.com/g7dMYI
类似于问题中的argmax函数的高阶函数可能如下所示:
template<typename Iter, typename Function>
Iter argmax(Iter begin, Iter end, Function f)
{
typedef typename std::iterator_traits<Iter>::value_type T;
return std::min_element(begin, end, [&f](const T& a, const T& b){
return f(a) < f(b);
});
}
Run Code Online (Sandbox Code Playgroud)
您可以使用以下代码调用此方法,具有您问题中的lambda函数:
auto a = argmax(l.begin(), l.end(), [](int x) { return -1 * abs(42 - x); });
std::cout << *a << std::endl;
Run Code Online (Sandbox Code Playgroud)
现场演示 argmax:http://ideone.com/HxLMap
现在唯一剩下的区别是这个argmax函数使用了一个基于迭代器的接口,它对应于C++标准算法(<algorithm>)的设计.将您自己的编码风格与您正在使用的工具相适应总是一个好主意.
如果你想要一个直接返回值的基于容器的接口,Nawaz提供了一个很好的解决方案,它需要decltype-feature来正确指定返回类型.我决定以这种方式保留我的版本,因此人们可以看到两种替代界面设计.
由于@leemes 解决方案太多了。一切都是正确的,除了没有人试图模仿您示例中的 Python 版本,这是我模仿的尝试:
方便的通用 argmax 函数就像 Python 版本:
template<typename Container, typename Fn>
auto max(Container const & c, Fn && key) -> decltype(*std::begin(c))
{
if ( std::begin(c) == std::end(c) )
throw std::invalid_argument("empty container is not allowed.");
typedef decltype(*std::begin(c)) V;
auto cmp = [&](V a, V b){ return key(a) < key(b); };
return *std::max_element(std::begin(c), std::end(c), cmp);
}
Run Code Online (Sandbox Code Playgroud)
并将其用作:
std::vector<int> l = {1,43,10,17};
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };
int l[] = {1,43,10,17}; //works with array also!
auto a = max(l, [](int x) { return -1 * std::abs(42-x); };
Run Code Online (Sandbox Code Playgroud)
注意:与其他解决方案不同,这max()返回元素本身,而不是元素的迭代器!
另请注意,此解决方案也适用于用户定义的容器:
namespace test
{
template<size_t N>
struct intcollection
{
int _data[N];
int const * begin() const { return _data; }
int const * end() const { return _data + N; }
};
}
test::intcollection<4> c{{1,43,10,17}};
auto r = max(c, [](int x) { return -1 * std::abs(42-x); });
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4959 次 |
| 最近记录: |