Very general argmax function in C ++

I am a spoiled Python programmer that is used to calculate the argmax collection with respect to some function with

 max(collection, key=function) 

For instance:

 l = [1,43,10,17] a = max(l, key=lambda x: -1 * abs(42 - x)) 

a then contains 43, the number closest to 42.

Is it possible to write a C ++ function that accepts any “iterable” and any function and returns argmax as described above? I suppose that would include template options, the auto keyword, and range-based iteration , but I couldn't put it together.

+4
source share
2 answers

Since @leemes there are too many solutions. That's right, except that no one is trying to emulate the Python versions in your example. Here is my attempt to imitate this:

Convenient universal argmax function similar to the Python version:

 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); } 

And use it like:

 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); }; 

Note: unlike another solution, this max() returns the element itself, not the iterator of the element!

Also note that this solution will work for a custom container as well:

 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); }); 

Watch a live demo .

+4
source

This is a two-step process. Define a key function that should be mapped to elements, that is, that is applied before the operation that finds the maximum. Wrap things together in a lambda expression, defining a comparison to find the maximum.

 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); }); 

Here we must write the key that was defined outside the second lambda function. (We could also define it inside). You can also put this in a single lambda function. When 42 should be parameterized from outside the lambda, commit this as a variable:

 int x = 42; std::max_element(l.begin(), l.end(), [x](int a, int b){ return -abs(x - a) < -abs(x - b); }); 

Note that std::max_element returns an iterator. To access the value / link to it, add it with * :

 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; 

You can beautifully wrap this in the find_nearest generic function:

 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; 

Live demo find_nearest : http://ideone.com/g7dMYI


A higher-order function, similar to the argmax function in your question, might look like this:

 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); }); } 

You can call this with the following code having exactly the lambda function from your question:

 auto a = argmax(l.begin(), l.end(), [](int x) { return -1 * abs(42 - x); }); std::cout << *a << std::endl; 

Live demo argmax : http://ideone.com/HxLMap


The only remaining difference is that this argmax function uses an iterator-based interface that matches the design of standard C ++ <algorithm> ( <algorithm> ). It is always useful to adapt your own coding style to the tools you use.

If you need a container-based interface that returns a value directly, Nawaz has provided a nice solution for which the decltype function should correctly indicate the type of the return value. I decided to keep my version so that people could see both alternative interface designs.

+7
source

All Articles