Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Minimum return value for range

I was wondering if there was a standard function that returns the minimum/maximum of the return values for a given range of elements. Something like this:

std::vector<int> list = { -2, -1, 6, 8, 10 };
auto it = 
    std::find_min_return_value(list.begin(), list.end(), std::abs, std::less<int>);
// it should be the iterator for -1

If there is no such, what is the best approach for a problem like this? My list is long, I really don't want to copy it, and also don't want to call the function whose minimum return value I look for more than once per element. Thanks!

UPDATE:

Based on ForEveR's suggestion to use std::min_element, I made the following benchmarking tests:

std::vector<double> list = { -2, -1, 6, 8, 10 };
auto semi_expensive_test_function = [] (const double& a) { return asin(sin(a)); };
for(int i = 0; i < 10000000; ++i)
{
    auto it = std::min_element(list.begin(), list.end(), 
        [&] (const double& a, const double& b) mutable 
    {
        return(semi_expensive_test_function(a) < semi_expensive_test_function(b));
    });
}

This worked just fine:

./a.out  11.52s user 0.00s system 99% cpu 11.521 total

After modifying the code to use a stateful lambda instead:

for(int i = 0; i < 10000000; ++i)
{
    auto it = std::min_element(list.begin() + 1, list.end(), 
        [&, current_min_value = semi_expensive_test_function(*(list.begin()))] (const double& a, const double& b) mutable 
    {
        double current_value = semi_expensive_test_function(b);
        if(current_value < current_min_value)
        {
            current_min_value = std::move(current_value);
            return true;
        } 
        return false; 
    });
}

This resulted:

./a.out  6.34s user 0.00s system 99% cpu 6.337 total

Using stateful lambdas seems to be the way to go. The question is: is there a more code-compact way to achieve this?

like image 740
Adam Hunyadi Avatar asked Apr 09 '26 22:04

Adam Hunyadi


2 Answers

With range-v3, it would be something like:

ranges::min(list, std::less<>{}, [](auto e) { return std::abs(e); });
like image 138
Jarod42 Avatar answered Apr 12 '26 13:04

Jarod42


Well, assuming Boost is like the standard library nowadays, you might use this:

#include <boost/range/adaptor/transformed.hpp>
#include <algorithm>

int main()
{
    std::vector<int> list = { -2, -1, 6, 8, 10 };
    auto abs_list = list | boost::adaptors::transformed(+[](int v) { return std::abs(v); });
    //                                                  ^ - read http://stackoverflow.com/questions/11872558/using-boost-adaptors-with-c11-lambdas
    auto it =  std::min_element(abs_list.begin(), abs_list.end(), std::less<int>{});
    std::cout << *it;
}
like image 35
PiotrNycz Avatar answered Apr 12 '26 13:04

PiotrNycz