2009-09-09 117 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) { 
    ... 
}