2013-11-28 3 views
5

У меня есть вектор строки, как {"1.2","3.4","0.5","200.7"}.Конвертировать вектор <std::string> в вектор <double>

Я хотел бы преобразовать каждый элемент в двойной и сохранить его в vector<double>.

Как так {1.2,3.4,0.5,200.7}

Что бы лучший способ сделать это?

Я знаю об std::stod(string, size); Но я надеюсь на лучший способ сделать это.

Я искал что-то вроде:

vector<double> doubleVector = convertStringVectortoDoubleVector(myStringVector); 

Там, кажется, не будет ничего подобного; так что же самое лучшее?


EDIT: Вот что я в конечном итоге с помощью:

std::vector<double> convertStringVectortoDoubleVector(const std::vector<std::string>& stringVector){ 
std::vector<double> doubleVector(stringVector.size()); 
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val) 
       { 
        return stod(val); 
       }); 
return doubleVector;} 

Для полного ответа проверить ответ Zac ХАУЛЕНД в ответ и Крис Jester-Янга. (P.S. Это полностью основано на ответе Зака) Спасибо

+0

вы не можете преобразовать все элементы без перебора всех элементов. если вы хотите, чтобы что-то делало конверсию лениво, то есть другой вопрос –

+1

AFAIK, нет такой вещи, которая «сделала бы это без повторения всего вектора, применяя его к каждому элементу» _. Вы можете написать функцию 'convertVectortoDouble', хотя – P0W

+0

Не будет ли' convertVectortoDouble' перебирать вектор? – 0x499602D2

ответ

8

Для полноты (так как Крис удалили редактирования из его ответа):

std::vector<double> doubleVector(stringVector.size()); 
std::transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), [](const std::string& val) 
{ 
    return std::stod(val); 
}); 

Сравнение с использованием std::back_inserter без reserve

Без резерва, вы рискуете того, чтобы изменить размер массив каждый раз, когда back_inserter звонит push_back. Он должен проверять текущий размер на текущую емкость, и если емкость необходимо увеличить, он скопирует вектор в новое место (с увеличенной емкостью). После всего этого он увеличит размер и введет новый элемент. Это накладные расходы, когда вы знаете, какой размер должен начинаться (он будет соответствовать размеру stringVector).

Comparision с использованием std::back_inserter с reserve

резервируя необходимое количество памяти позволит предотвратить проблему перераспределения, но push_back все еще обновляет размер и делает проверку, чтобы увидеть, если пропускная способность была достигнута каждой итерации. Вы значительно сократили накладные расходы (больше не рискуете «переместить» массив из-за проблем с размерами), но у вас все еще есть много ненужных условных проверок.

Установка размера первоначально имеет небольшие накладные расходы при установке всех элементов на значение по умолчанию (0.0 в случае удвоений). С каждой итерацией вы просто устанавливаете значение текущего элемента. Итак, для вектора из N элементов у вас есть 2N + 2 назначения (установка емкости, размера, начального значения элементов и действительного значения элементов) без ненужных условных проверок. Метод резервирования имеет 2N + 1 присваивания (задает емкость один раз, обновляет размер N раз и устанавливает значение N удваивает) в дополнение к N условным проверкам.

Если вы действительно хотите, чтобы оптимизировать его еще дальше, вы можете создать свой собственный итератор оболочку, которая делает преобразование, который затем позволит вам писать правильное значение удваивается при инициализации вектора:

// pseudo-code 
std::vector<double> doubleVector(my_string_conversion_iterator(stringVector.begin()), my_string_conversion_iterator(stringVector.end()); 
+0

Yay для полноты (+1). Мой ответ расширяет это, объясняя обстоятельства, при которых вы должны отдавать предпочтение каждому подходу. В частности, для общих функций, где целевой тип не является конструктивным и/или назначаемым по умолчанию, этот подход был бы неосуществим. –

+0

Кроме того, пользовательский подход итератора, если он не моделирует итератор случайного доступа (в частности, 'distance' должен работать в постоянное время), производительность вряд ли будет лучше, чем подход' back_inserter'. –

+0

Ну, чтобы быть вставленным в вектор, тип должен быть назначен, но в целом я согласен. Пользовательский подход итератора был бы более эффективен, если бы оболочка была написана как тонкая оболочка поверх собственного итератора вектора строки (и просто изменила оператор разыменования, чтобы вернуть двойник вместо строки). Это позволяет объединять преимущества подходов, отличных от 'back_inserter' и' back_inserter'. –

8

Чтобы применить преобразование к каждому элементу, вы должны использовать std::transform.

vector<double> doubleVector; 
doubleVector.reserve(stringVector.size()); 
transform(stringVector.begin(), stringVector.end(), back_inserter(doubleVector), 
     [](string const& val) {return stod(val);}); 

Как Zac Хоулэнд указует, здесь другой подход к этому, который включает в себя инициализацию вектора с конструируемым по умолчанию элементов первой, а затем просто заполнение вектора с правильными значениями впоследствии:

vector<double> doubleVector(stringVector.size()); 
transform(stringVector.begin(), stringVector.end(), doubleVector.begin(), 
     [](string const& val) {return stod(val);}); 

Преимущество этого подхода состоит в том, что вектор имеет размер ровно один раз, а не непрерывно растет. Недостаток заключается в том, что векторные элементы сначала должны быть построены по умолчанию, а затем переназначаться с правильным значением впоследствии. Этот выбор стоит типов элементов, которые удовлетворяют всем следующим:

  1. может быть по умолчанию возведенных
  2. дешевы по умолчанию-построить
  3. можно присвоить значение одного и того же типа
  4. дешевы назначить

в этом случае, double выполняет все четыре требования, и поэтому последний подход лучше. Для других типов в целом и, в частности, при написании шаблона функции для этого, ваша реализация по умолчанию должна использовать прежний подход.

+1

Если вы задаете размер двойного вектора размеру векторной строки, вы можете избежать использования 'back_inserter ' –

+1

@ZacHowland Действительно, и способ сделать это (с точки зрения избежания перераспределения, вместо того, чтобы избегать' back_inserter'), следует использовать 'vector :: reserve'. : -P (у меня есть сильное отвращение к объектам, построенным по умолчанию, а затем назначаю их позже, но предпочитаю сначала создавать правильное значение, поэтому да, я отбросил ваше редактирование.) –

+0

Если вы ищете для получения производительности (на самом деле это не проблема с этой конкретной проблемой), 'back_inserter' каждый раз вызывает' push_back', что означает, что каждый раз он обновляет член 'size' (и каждый раз проверяет« емкость »). Использование конструктора размера по умолчанию задает член 'size' один раз, за ​​счет очень дешевой инициализации по умолчанию для вашего вектора удвоений. Короче говоря, вы торгуете большим количеством процессорного времени, просто чтобы не устанавливать значение удвоения в два раза. –

2

Использование std::transform

vector<string> str ; 
vector<double> dv ; 

std::transform(str.begin(), str.end(), back_inserter(dv), [](const string & astr){ return stod(astr) ; }) ; 
+0

Вы забыли использовать 'back_inserter'. Также см. Мой ответ. :-P –

+0

Да, видел это. спасибо. – woolstar

1

вы можете использовать std:for_each и lambda в C++ 11.

vector<string> a = {"1.2","3.4","0.5","200.7"}; 
vector<double> b; 
for_each(a.begin(), a.end(), [&b](const string &ele) { b.push_back(stod(ele)); }); 
Смежные вопросы