【问题标题】:accumulate the sum of elements in map, using value使用 value 累积 map 中元素的总和
【发布时间】:2009-04-06 14:58:35
【问题描述】:

说我有一个

struct SMyStruct 
{
   int MULT;
   int VAL;

};


std::map<std::string, SMyStuct*> _idToMyStructMap;

现在我想计算所有 SMyStuct 的总和,其中总定义为 idToMyStructMap 中每个元素的 MULT1 *VAL1 + MULT2 *VAL2。

似乎累积函数是一个自然的选择。请建议。谢谢

请不要提升....只是一个'ld时尚stl

【问题讨论】:

  • 哦,bb 比我更容易回答,所以我会有点迂腐:你知道前导下划线通常是个坏主意,对吧? (在大多数情况下保留给实现)并且结构上的 S 前缀是完全没有意义的噪音。 :)
  • 啊,好吧……我们总是在结构体前面加上 S,在类前面加上 C;我们的编码标准要求。就“”而言,我同意,但由于它不是任何东西的一部分,所以我就这样离开了。我通常将 m 用于成员 g_ 用于全局,将 s_ 用于静态。感谢您指出。
  • 技术上_可能没问题。规则类似于“双前导 _ 或前导 _ 后跟大写字母保留给实现。前导 _ 后跟其他任何内容都保留在全局命名空间中”。最容易完全避免领先 _ ;)
  • 这很好地总结了 C/C++ 中保留的各种名称形式:stackoverflow.com/questions/228783/…
  • 虽然可以使用std::accumulate 完成,但我不确定这是否值得付出努力和牺牲可读性,因为操作的复杂性保持不变。

标签: c++ algorithm stl


【解决方案1】:
typedef std::map< std::string, SMyStruct* > string_to_struct_t;

int add_to_totals( int total, const string_to_struct_t::value_type& data )
{
    return total + data.second->MULT * data.second->VAL; 
}

const int total = std::accumulate(
                         _idToMyStructMap.begin(),
                         _idToMyStructMap.end(),
                         0, 
                         add_to_totals );

【讨论】:

  • 将 add_to_totals 写为带有 operator() 的对象会更有效。否则,很好的解决方案。
  • @bb:因为如果它是一个结构,编译器可以推断出调用了哪个函数,并简单地内联它。如果它是一个函数指针,它必须做一些相当复杂的过程间分析来确定调用哪个函数。所以你通常不能指望它内联调用。
  • 也就是说,如果它是一个结构,那么要调用的类型将是 add_to_totals::operator(),这是一个静态函数。使用函数指针,类型只是“int(*)(int, const string_to_struct_t::value_type&)”,所以实际函数是未知的。
  • 阅读这篇关于标识符下划线的帖子:stackoverflow.com/questions/228783/…
  • @Martin:哇,很棒的帖子。想知道为什么我以前没见过那个。很高兴知道。 :)
【解决方案2】:

主题的一个变体是为你的结构定义 operator+,然后在其默认模式下使用 std::accumulate。

int & operator+ (const int &lhs, const SMyStruct &rhs){
    return lhs + (rhs.MULT * rhs.VALUE);
}

然后:

std::accumulate(_idToMyStructMap.begin(), _idToMyStructMap.end(), 0);

当然,如果operator+ 对您的结构一般来说是有意义的,那么您也需要在左侧添加重载以使用 SMyStruct,和/或将它们设为模板,以便获得 int、float 的函数,双,长等,一拍即合。正如 cmets 中提到的 jalf,如果 operator+(或它的这个版本)对您的结构通常没有意义,那么其他解决方案会更好。

【讨论】:

  • 问题在于你现在有一个 operator+,它在整个代码库中都是可见的,但只在这个特定的上下文中才有意义。可能会让任何阅读(或维护)代码的人感到困惑。
  • @jalf - 也许,但也许不是。我不知道,这在更多地方实际上可能有意义。如果没有,那么有正当理由不选择这条路。
  • 这行不通。你应该为 std::map::value_type 写 operator+(...)
  • 我同意@jaff 关于代码质量的看法; bb 的解决方案对于这个问题更干净。谢谢
  • 最重要的是,bb 的权利:当 map.begin() 和 map.end() 时,accumulate 会尝试查找 operator+( pair&, pair )被视为迭代器。基本问题是取消引用包裹在对中的东西。
【解决方案3】:

您还可以将“取第二对”功能与“计算 MULT*VAL”和“向累加器添加内容”分开。

虽然您不需要提升来执行此操作,但他们已经创建了大量的“函数式”编程框架。如果你不能使用 boost,你需要一些你自己的模板魔法。不过复杂了。

#include <map>
#include <algorithm>
#include <numeric>
#include <functional>
#include <iostream>

现在我认为最好将乘法放在类中。

struct SMyStruct 
{
   int MULT;
   int VAL;
   long f() const { return MULT*VAL; }
};

为“take second of pair”创建一个通用函子:

// a 'take-second' functor
template< typename at_pair >
struct to_second_t : public std::unary_function< at_pair, typename at_pair::second_type > {
  const typename at_pair::second_type& operator()( const at_pair & p ) const {
    return p.second;
  }
};

这看起来很棘手,但只是一种通用的说法:'先做这个,然后再做那个结果':

// compose two functors (simplified)
template< typename at_F, typename at_G >
struct compose_t : public std::unary_function< typename at_F::argument_type, typename at_G::result_type >{
    at_F f;
    at_G g;
    compose_t( at_F& f, at_G& g ): f( f ), g(g) {}

    typename at_G::result_type operator()( const typename at_F::argument_type& v ) const {
        return g( f( v ) );
    }
};

template< typename at_F, typename at_G >
compose_t<at_F, at_G> compose( at_F& f, at_G& g ) { return compose_t<at_F,at_G>( f, g ); }



// compose two functors (a unary one, and a binary one)
//
template< typename at_F, typename at_G >
struct compose2_t : public std::binary_function< typename at_F::first_argument_type, typename at_G::argument_type, typename at_G::result_type >{
    at_F f;
    at_G g;
    compose2_t( at_F& f, at_G& g ): f( f ), g(g) {}

    typename at_G::result_type operator()( const typename at_F::first_argument_type& a1, const typename at_G::argument_type& v ) const {
        return f( a1, g( v ) );
    }
};

template< typename at_F, typename at_G >
compose2_t<at_F, at_G> compose2( at_F& f, at_G& g ) { return compose2_t<at_F,at_G>( f, g ); }

最后,付诸实践:

int main()
{
  typedef std::map<int, SMyStruct > tMap; 
  tMap m;
  SMyStruct s = {1,2};
  m[1].VAL = 1; m[1].MULT = 3;
  m[2].VAL = 2; m[2].MULT = 10;
  m[3].VAL = 3; m[3].MULT = 2;

  // mind, this is not LISP (yet)
  long total = std::accumulate( m.begin(), m.end(), 0, 
    compose2( 
      std::plus<int>(),  
      compose( 
        to_second_t<tMap::value_type>(), 
        std::mem_fun_ref( &SMyStruct::f ) ) )
    );

  std::cout << "total: " << total <<std::endl;
  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-15
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多