2009-09-09 129 views
2

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

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和functor的用法,但还没有弄清楚这些技术如何改进这个特定的例子。

+1

为什么简化呢?它是可读的。它看起来足够高效。我想你可以使用像map-reduce这样的东西,但我没有理由这么做。 – 2009-09-09 12:15:09

+4

该代码存在严重缺陷。如果矢量中没有数据,则最终的空白(不在那里)将被删除。大概给个例外 – Toad 2009-09-09 12:15:09

回答

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()); 
+0

非常感谢!我认为回报需要包括(fmt%d).str()虽然或类似的东西。 – AndyUK 2009-09-09 13:03:41

+0

是的。纠正了这一点。 – xtofl 2009-09-09 14:02:22

1

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

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; 
} 
+0

你假设浮动符合20个字符。即使这是真的,现在在未来的64或128或256位架构上也会如此吗?虽然你的代码将继续工作而不会崩溃,但更隐秘的错误是结果的截断将会发生而没有任何警告或错误。 – 2009-09-09 16:22:39

+0

你说得很对。即使使用32位浮点数,也可以使用溢出(例如1e20)。这是简单性和彻底性之间的妥协。在实践中,我敢肯定20位数字就足够了。我想我们正在处理字符串,因为结果将会是人类可读的。超过20位的数字测试人类可读性的限制。希望作者了解Hpitch的局限性。嗯,我正努力说服自己:-( – 2009-09-10 08:25:16

1

如上所述,很多方法来实现这一目标,但是...... 没有这种方法只是乞求有一些更多的参数和模板化正在? 假设有

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

则可以创建

1,2,3

1.0,然后2.0,然后5.0

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

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; 
+1

静态ostringstream将是一个非常糟糕的想法国际海事组织。如果两个线程模拟这种方法怎么办?Havok – larsmoa 2009-09-09 16:00:21

+0

当然这将是不好的,在这种情况下,你会使用一个线程局部变量。 – MSalters 2009-09-10 07:17:54

2

的“格式化”变量应你的循环之外声明,如设置格式化每次迭代都很慢并且不需要。也不需要stringstream。所以身体会变成这样的:

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

我没有找到你的原代码臃肿或迫切需要简化。不过,我想移动

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

ostringstream s; 

圈外,以确保他们只初始化一次。这样可以节省很多str.append()。我猜xtofl的std :: transform()解决方案会产生这个问题(通过为结构初始化一次就可以很容易地解决这个问题)。

如果您正在寻找其他替代

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

退房BOOST_FOREACH这将使您按以下方式进行迭代:

std::vector<double> list; 
BOOST_FOREACH(double value, list) { 
    ... 
}