c – 根据变换的值查找最小元素

这是从代码审查到我的任务.我想根据一种特殊的比较谓词从一个集合中选择一个最小值.喜欢这个:
struct Complex { ... };

float calcReduction(Complex elem);

Complex findMinValueWithPredicates(const std::vector<Complex>& values)
{
  auto it = std::min_element(values.begin(),values.end(),[](const Complex& a,const Complex& b) { 
                               return calcReduction(a) < calcReduction(b); 
                             });

  if (it == values.end()) throw std::runtime_error("");

  return *it;
}

这里我找到基于谓词的最小元素.该谓词计算两个值的缩小以浮动,然后比较这些浮点数.工作正常,看起来整齐.

你能看到问题吗?是的,对于一组N个元素calcReduction()被称为2N次,而它只需要计算N次,每次为每个元素一次.

解决这个问题的一个原因是写出明确的计算:

Complex findMinValueExplicit(const std::vector<Complex>& values)
{
  float minReduction = std::numeric_limits<float>::max();
  Complex minValue;

  for (Complex value : values)
  {
    float reduction = calcReduction(value);
    if (reduction < minReduction)
    {
      minReduction = reduction;
      minValue = value;
    }
  }

  if (minReduction == std::numeric_limits<float>::max()) throw std::runtime_error("");

  return minValue;
}

它工作正常,我们只有N个调用calcReduction().但是,与明确调用min_element相比,它看起来太冗长,并且意图不是那么清楚.因为当你调用min_element时,很容易猜到你要找到一个最小的元素,你知道.

我现在唯一的想法是创建我自己的算法,如min_element_with_reduction,接受范围和缩小功能.听起来很合理,但我不知道是否有任何现成的解决方案.

任何想法如何解决这个任务明确的意图和一些现成的解决方案? Boost受欢迎. C 17和范围很有趣.

解决方法

您可以使用boost :: range library.
auto reductionLambda = [](const Complex& a) { return calcReduction(a); };
auto it = boost::range::min_element(values | boost::adaptors::transformed( 
                             std::ref(reductionLambda));

范围本身也应该与C 17达到标准C.

编辑

正如我们在评论中所看到的,这也会使转换两次.

所以这里有一些乐趣:

#include <boost/iterator/iterator_adaptor.hpp>
#include <boost/assign.hpp>
#include <algorithm>
#include <iostream>
#include <vector>
#include <functional>


template <class Iterator,class UnaryFunction>
class memoizing_transform_iterator
  : public boost::iterator_adaptor<
        memoizing_transform_iterator<Iterator,UnaryFunction> // Derived,Iterator                                              // Base,std::decay_t<decltype(std::declval<UnaryFunction>()(std::declval<typename Iterator::value_type>()))> // Value,boost::forward_traversal_tag    // CategoryOrTraversal
    >
{
 public:
    memoizing_transform_iterator() {}

    explicit memoizing_transform_iterator(Iterator iter,UnaryFunction f)
      : memoizing_transform_iterator::iterator_adaptor_(iter),fun(f) {}

    static int total;
 private:
    friend class boost::iterator_core_access;
    void increment() { ++this->base_reference(); memoized = false; }

    using MemoType = std::decay_t<decltype(std::declval<UnaryFunction>()(std::declval<typename Iterator::value_type>()))>;      

    MemoType& dereference() const 
    {
        if (!memoized) {
            ++total;
            memoized = true;
            memo = fun(*this->base());
        }
        return memo;
    }

    UnaryFunction fun;
    mutable bool memoized = false;
    mutable MemoType memo;
};


template <class Iterator,class UnaryFunction>
auto make_memoizing_transform_iterator(Iterator i,UnaryFunction&& f)
{
    return memoizing_transform_iterator<Iterator,UnaryFunction>(i,f);
}



template<class I,class U>
int memoizing_transform_iterator<I,U>::total = 0;


// THIS IS copIED FROM LIBSTDC++
template<typename _ForwardIterator>
   _ForwardIterator
     min_el(_ForwardIterator __first,_ForwardIterator __last)
     {
       if (__first == __last)
     return __first;
       _ForwardIterator __result = __first;
       while (++__first != __last)
     if (*__first < *__result)
       __result = __first;
       return __result;
     }


int main(int argc,const char* argv[])
{
    using namespace boost::assign;

    std::vector<int> input;
    input += 2,3,4,1,5,6,7,8,9,10;


    auto transformlambda = [](const int& a) { return a*2; };


    auto begin_it = make_memoizing_transform_iterator(input.begin(),std::ref(transformlambda));
    auto end_it = make_memoizing_transform_iterator(input.end(),std::ref(transformlambda));
    std::cout << *min_el(begin_it,end_it).base() << "\n";

    std::cout <<begin_it.total;

    return 0;
}

基本上我实现了一个迭代器来记录调用变换函数的结果.奇怪的是,至少在在线编译器中,迭代器在被取消引用的值被比较之前被复制(从而击败了记忆的目的).但是当我简单地从libstdc复制实现时,它的工作原理如下.也许你可以在真实的机器上尝试一下?实例是here.

小编:我在VS2015测试,它的工作原理与std :: min_element.

相关文章

本程序的编译和运行环境如下(如果有运行方面的问题欢迎在评...
水了一学期的院选修,万万没想到期末考试还有比较硬核的编程...
补充一下,先前文章末尾给出的下载链接的完整代码含有部分C&...
思路如标题所说采用模N取余法,难点是这个除法过程如何实现。...
本篇博客有更新!!!更新后效果图如下: 文章末尾的完整代码...
刚开始学习模块化程序设计时,估计大家都被形参和实参搞迷糊...