2009-09-09 3 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 и функторов, в частности, но не смог выяснить, как эти методы могут улучшить этот конкретный пример.

+1

Почему упрощать? Это читаемо. Он выглядит достаточно эффективным. Думаю, вы могли бы использовать что-то вроде уменьшения карты, но я не вижу причин для этого. –

+4

Код как есть имеет серьезный недостаток. Если в векторе нет данных, то окончательное пробельное пространство (которое не существует) будет удалено. Вероятно, давая исключение – Toad

ответ

11

Так как вы на самом деле преобразование двойников в строки и добавление этих строк в stringstream, вы можете использовать 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

+0

Да, да. Исправлено это. – xtofl

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-битных архитектурах в будущем? Хотя ваш код будет продолжать работать без сбоев, более коварная ошибка заключается в том, что усечение результата произойдет без предупреждения или ошибки. –

+0

Вы совершенно правы. Даже с 32-битным поплавком вы можете сделать с переполнением (например, 1e20 будет). Это компромисс между простотой и тщательностью. На практике я почти уверен, что 20 цифр будет достаточно. Я предполагаю, что мы имеем дело со строками, потому что результат будет читабельным для человека. Номера с более чем 20 цифрами проверяют пределы удобочитаемости человека. Надеюсь, у автора есть представление о том, какие ограничения для Hpitch. Хммм, я изо всех сил пытаюсь убедить себя здесь :-( –

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 будет очень плохой идеей IMO. Что делать, если два потока называют этот метод simulatanously? Havok – larsmoa

+0

Это было бы плохо, конечно. В этом случае вы бы использовали вместо этого используется локальная переменная потока. – MSalters

2

«FMT» переменная должна быть объявлена ​​за пределами цикла, как и установив форматирование каждой итерации происходит медленно и не требуется. также строка string не нужна. Таким образом, тело станет что-то вроде этого:

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() - тоже. Я предполагаю, что решение std :: transform() xtofl будет иметь эту проблему (его легко исправить, инициализируя его один раз для структуры, хотя).

Если вы ищете другие альтернативы

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

заканчивало BOOST_FOREACH, который позволил бы вам перебирать следующим образом:

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