2015-04-21 1 views
2

У меня есть std::string. Я хочу набор уникальных символов в нем, причем каждый символ представлен как std::string.Как я могу получить std :: набор символов в строке, как строки?

я могу получить набор символов легко:

std::string some_string = ... 
std::set<char> char_set(some_string.begin(), some_string.end()); 

И я мог бы преобразовать их в строки, как это:

std::set<std::string> string_set; 
for (char c: char_set) { 
    string_set.emplace(1, c); 
} 

Но такой подход кажется неуклюжим. Есть ли лучший способ (предпочтительнее стандартная библиотека с одним слоем) для этого?

+7

Что кажется неуклюжим именно? Мне все это кажется неудобным. Зачем вам это делать? – Shoe

+0

@Jefffrey У меня есть отвращение к циклу, когда он не использует стандартную библиотеку, я имею в виду, мгновенный O (* n *). Я хочу сделать это, потому что я возьму объединение этого набора с другим набором 'std :: string', поэтому оба набора должны иметь один и тот же тип. – EMBLEM

+2

@EMBLEM Итак, у нас в основном есть проблема XY? – Columbo

ответ

1

Вы можете использовать:

std::for_each(some_string.begin(), some_string.end(), 
       [&string_set] (char c) -> void { string_set.insert(std::string({c}));}); 

Вы также можете использовать:

for (char c: some_string) 
    { 
     string_set.insert(std::string{c}); 
    } 

Рабочая программа:

Выход:

 

I 
a 
c 
e 
f 
h 
i 
n 
o 
q 
r 
s 
t 
u 
w 
+1

Ваше решение в основном является нечитаемой версией для диапазонов! – Columbo

+0

@ Коломбо, это лучшее, что я мог придумать. –

+1

Если вы не можете придумать хорошее решение (возможно, потому, что там просто нет), зачем публиковать ответ в первую очередь? – Columbo

3

А transform может быть использован в качестве однострочника:

transform(begin(some_string), end(some_string), 
      inserter(string_set, begin(string_set)), 
      [] (char c) -> std::string { return {c}; }); 

Я бы не рекомендовал использовать это решение, хотя, как это ужасно нечитаемым. Обычно вы хотите написать код, который является интуитивно понятным и понятным. То, что вы написали в своем ответе, уже достаточно, и я бы не рекомендовал искать короткие сокращения, чтобы уменьшить ваш код в один лайнер, жертвуя своей ясностью.

+4

Это в основном делает то же самое, что и нежелательный код в OP, но теперь с большим количеством служебных и менее читаемых кодов. –

+0

@ LB-- Он сказал, что хочет «предпочтительного решения с одной библиотекой». – 0x499602D2

+1

Почему это вниз? OP хотел один лайнер, и он получил один лайнер. Он работает, не так ли? +1 – Barry

2

Есть ли лучший способ (предпочтительнее стандартная библиотека с одним слоем) для этого?

No. Все, что вы найдете в стандартной библиотеке C++ предназначено для более сложных случаев, когда они упрощающих код, который вы должны написать иначе. В вашем случае ваш код проще. Попытка заставить себя использовать что-то из стандартной библиотеки для этого сделает ваш код более запутанным.

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

Ваш цикл является лучшим решением. Это просто, оно передает намерение читателю, и компилятор легко оптимизировать. Нет причин тратить время на поиск более сложного решения простой проблемы.

Все решения правильные, но вы всегда должны выбрать правильное решение . Напиши меньше, а не больше.

1

Я не уверен, что вы хотите, особенно отличная идея, но один способ сделать примерно то же самое было бы класс, который поддерживает неявное преобразование из char и std::string:

class cvt { 
    std::string val; 
public: 
    cvt(char c) : val(1, c) {} 
    cvt(std::string const &s) : val(s) {} 
    operator std::string() const { return val; } 
    friend bool operator<(std::string const &a, cvt const &b) { return a < b.val; } 
}; 

С этим мы можем создать set<cvt>, но и использовать его как если бы это было set<std::string> (так как элементы в нем может/будет конвертировать в std::string неявно и сравнить с std::string):

int main() { 
    std::string some_string = "ZABCDECD"; 

    // Create our (sort of) set<string> from characters in some_string: 
    std::set<cvt> char_set(some_string.begin(), some_string.end()); 

    // An actual set<string> to use with it:  
    std::set<std::string> strings{ "A", "C", "E", "F", "Y" }; 

    // demonstrate compatibility: 
    std::set_intersection(char_set.begin(), char_set.end(), strings.begin(), strings.end(), 
     std::ostream_iterator<std::string>(std::cout, "\n")); 
} 

Live on Coliru.

В зависимости от границ вы имеете в виду, что может подходит то, что вы хотите ...

+0

Это чрезмерная дополнительная типизация, она добавляет накладные расходы, которые сложно компиляторам оптимизировать, и больше всего это нарушает KISS. Однако это очень умно, и это то, что я сделал бы, если бы для циклов не существовало. –