2

我有一个函数,它基本上从双精度向量中读取值,将它们附加到一个字符串(同时确保每个之间有一个空格并设置它们的精度)并返回最终结果,减去最后的空格:

std::string MultiplePrintProperties::GetHpitchString()  
{    
    std::string str;  
    vector< double >::iterator it;    

    for ( it  = Vals.begin();    
          it != Vals.end();  
          it++ )  
    {
        ostringstream s;    

        // Set precision to 3 digits after the decimal point
        // and read into the string 
        boost::format fmt( "%.3f " ); 
        s << fmt % *( it );
        str.append( s.str() );      
    }

    // Remove last white space and return string   
    return str.substr( 0, str.length() - 1 ); 
}

我想知道是否可以以任何方式简化此代码。我最近一直在研究特别是 for_each 和仿函数的使用,但还没有弄清楚这些技术如何改进这个特定的例子。

4

7 回答 7

11

由于您实际上是将双精度转换为字符串,并将这些字符串附加到字符串流,因此您可以使用std::transform

// your functor, transforming a double into a string
struct transform_one_double {
   std::string operator()( const double& d ) const {
     boost::format fmt( "%.3f " ); 
     return (fmt % d).str();
   }
};

// iteration code, taking each value and inserting the transformed
// value into the stringstream.
std::transform( vals.begin(), vals.end()
              , std::ostream_iterator<std::string>( s, " ")
              , transform_one_double() );
于 2009-09-09T12:19:36.800 回答
4

这些天我好像有点老了。我会这样做:

std::string MultiplePrintProperties::GetHpitchString()  
{    
    std::string str;  
    vector< double >::iterator it;    

    for ( it  = Vals.begin();    
          it != Vals.end();  
          it++ )  
    {
        // Set precision to 3 digits after the decimal point
        // and write it into the string 
        char buf[20];
        snprintf( buf, 20, "%.3f", *it );
        if (str.length() > 0)
            str.append(" ");
        str.append( buf );          
    }

    return str; 
}
于 2009-09-09T12:42:52.033 回答
2

'fmt' 变量应该在循环之外声明,因为每次迭代都设置格式很慢并且不需要。也不需要字符串流。所以身体会变成这样:

  std::string s;
  std::vector<double>::iterator i = vals.begin();

  if (i != vals.end())
{
  boost::format fmt("%.3f");
  s = str(fmt % *i++);

  while (i != vals.end())
    s += ' ' + str(fmt % *i++);
}
于 2009-09-09T14:10:23.270 回答
2

我没有发现您的原始代码臃肿或迫切需要简化。但是我会移动

boost::format fmt("%.3f");

ostringstream s;

退出循环以确保它们只被初始化一次。这也将节省大量的 str.append()-ing。我猜 xtofl 的 std::transform() 解决方案会遇到这个问题(不过,通过为结构初始化一次很容易解决)。

如果您正在寻找其他替代品

 for (it = begin(); it != end(); ++it) {...}

查看BOOST_FOREACH这将使您能够以以下方式进行迭代:

std::vector<double> list;
BOOST_FOREACH(double value, list) {
    ...
}
于 2009-09-09T15:40:50.337 回答
1

您可以创建一个operator()具有对 std::string 的引用作为成员的重载类。您将声明该类的一个对象并将字符串传递给构造函数,然后将该对象用作 for_each 的第三个参数。将为每个元素调用重载的 operator() 并将文本附加到引用的字符串。

于 2009-09-09T12:35:22.150 回答
1

如上所述,有很多方法可以实现这一点,但是......这种方法不只是乞求更多的参数并被模板化吗?假设你有

template< class tType >
std::string PrintVectorToArray( const std::vector< tType >& V, const char* Seperator );

那么你可以创建

1 2 3

1、2、3

1.0 然后 2.0 然后 5.0

对于任何可转换为字符串和任何分隔符的类型!我曾经这样做过,现在发现自己经常使用这种方法。

于 2009-09-09T13:00:36.903 回答
0

我建议使用单个字符串流和单个格式。那些并不便宜。

std::string MultiplePrintProperties::GetHpitchString()  
{    
    std::ostringstream s;    
    // Set precision to 3 digits after the decimal point
    static boost::format fmt( "%.3f " ); 

    for ( vector< double >::iterator it  = Vals.begin();    
          it != Vals.end(); it++ )  
    {
        // and read into the string 
        s << fmt % *( it );
    }
    // Remove last white space (if present) and return result
    std::string ret = s.str();
    if (!ret.empty()) ret.resize(ret.size()-1);
    return ret;
}

如果我有分析信息证明它仍然是一个瓶颈,我会考虑使用静态 ostringstream:

static std::ostringstream s;    
...
std::string ret;
std::swap(ret, s.str());
return ret;
于 2009-09-09T13:55:00.723 回答