【问题标题】:Minimum return value for range范围的最小返回值
【发布时间】:2017-07-05 14:06:48
【问题描述】:

我想知道是否有一个标准函数可以返回给定范围元素的返回值的最小值/最大值。像这样的:

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

如果没有这样的问题,解决此类问题的最佳方法是什么? 我的列表很长,我真的不想复制它,也不想调用每个元素多次查找最小返回值的函数。谢谢!

更新:

根据 ForEveR 使用 std::min_element 的建议,我做了以下基准测试:

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

效果很好:

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

修改代码以改用有状态 lambda 后:

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

结果:

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

使用有状态的 lambda 似乎是要走的路。问题是:有没有更代码紧凑的方式来实现这一点?

【问题讨论】:

  • 取决于,如果没有时间复杂度约束,可以 std::sort(list.begin(), list.end()) 第一个元素是最小值,最大值是最后一个使用默认值比较器,即 nlogn。
  • std::min_element? std::minmax_element?
  • 或者std::minmax_element如果你需要两者
  • @AdamHunyadi std::minmax_element(list.begin(), list.end(), [] (int a, int b) { return std::abs(a) &lt; std::abs(b); })
  • @XinHuang 列表很长,不想复制。另外,我想为每个元素调用一次我查找其最小返回值的函数。

标签: c++ algorithm c++11 c++14


【解决方案1】:

使用range-v3,会是这样的:

ranges::min(list, std::less<>{}, [](auto e) { return std::abs(e); });

【讨论】:

    【解决方案2】:

    好吧,假设 Boost 就像现在的标准库,你可能会使用这个:

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

    【讨论】:

    • 嗯,这样就好了,但是我的列表占用了很多内存,所以我不想复制它:S
    • 我的解决方案根本没有副本。 boost::adaptors::transformed 仅创建视图。
    【解决方案3】:

    如果它会被重用,并为您提供另一种选择,您可以按照 std 约定编写自己的通用算法。

    template <typename T, typename ForwardIt, typename UnaryFunction, typename Comparator>
    ForwardIt find_min_return_value(ForwardIt first, ForwardIt last, UnaryFunction op, Comparator compare)
    {
        if (first == last)
            return last;
    
        ForwardIt smallestItr = first;
        T smallestValue = op(*first);
    
        for (auto itr = first + 1; itr != last; ++itr)
            {
            T current = op(*itr);
            if (compare(current, smallestValue))
                {
                smallestValue = current;
                smallestItr = itr;
                }
            }
    
        return smallestItr;
    }
    

    使用起来非常紧凑,每个元素只执行一次操作:

    int main()
    {
        std::vector<int> list = { -2, -1, 6, 8, 10 };
        auto it1 = find_min_return_value<int>(list.begin(), list.end(), [](int i){ return std::abs(i); }, std::less<int>());
    
        std::vector<std::string> strings = { "Lorem", "ipsum", "dolor", "sit", "amet", "consectetur", "adipiscing", "elit" };
        auto it3 = find_min_return_value<size_t>(strings.begin(), strings.end(), [](std::string s){ return s.length(); }, std::less<size_t>());
    
        std::cout << *it1 << "\n"; // outputs -1
        std::cout << *it3 << "\n"; // outputs sit
    }
    

    如果您只是怀疑它可能有一天会被重用,它可能不会,然后它过于复杂,应该只是一个简单的功能。

    【讨论】:

    • 确实做到了:D
    • smallestValue = current; 可以是smallestValue = std::move(current);op 可以在您进行投影时重命名为project。 (该命名用于范围库中)。
    猜你喜欢
    • 2016-02-20
    • 1970-01-01
    • 2013-11-14
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 2011-07-05
    相关资源
    最近更新 更多