IMO, Ben Voigt начал с хорошей базовой идеи, но я бы предостерег от его формулировки слишком буквально.
В частности, мне не нравится идея поиска строки в наборе, а затем добавление это к вашему набору, если его нет, и добавив его в вывод, если он присутствует. Это в основном означает, что каждый раз, когда мы сталкиваемся с новым словом, мы дважды ищем наш набор существующих слов, один раз, чтобы проверить, присутствует ли слово, и снова вставить его, потому что это не так. Большая часть этого поиска будет по существу идентичной - если какой-то другой поток не изменяет структуру в промежуточный период (что может привести к состоянию гонки).
Вместо этого я начал с попытки добавить его к набору слов, которые вы видели. Это возвращает pair<iterator, bool>
, а bool
установлен на true
тогда и только тогда, когда значение было вставлено - то есть ранее не было. Это позволяет нам консолидировать поиск существующей строки и вставить новую строку вместе в одну вставку:
while (input >> word)
if (!(existing.insert(word)).second)
output.insert(word);
Это также очищает поток настолько, что довольно легко включить тест в функтор, который мы можем использовать с std::remove_copy_if
, чтобы получить наши результаты совершенно напрямую:
#include <set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::set<std::string> result;
std::remove_copy_if(words.begin(), words.end(),
std::inserter(result, result.end()), show_copies());
for (auto const &s : result)
std::cout << s << "\n";
}
В зависимости от того, насколько я больше заботился о простоте или скорости выполнения кода, я мог бы использовать std::vector
вместо set
для результата и используйте std::sort
, а затем std::unique_copy
, чтобы получить окончательный результат. В таком случае я, вероятно, также заменил бы std::set
внутри show_copies
на std::unordered_set
:
#include <unordered_set>
#include <iterator>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
class show_copies {
std::unordered_set<std::string> existing;
public:
bool operator()(std::string const &in) {
return existing.insert(in).second;
}
};
int main() {
std::vector<std::string> words{ "words", "words", "are", "fun", "fun" };
std::vector<std::string> intermediate;
std::remove_copy_if(words.begin(), words.end(),
std::back_inserter(intermediate), show_copies());
std::sort(intermediate.begin(), intermediate.end());
std::unique_copy(intermediate.begin(), intermediate.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Это немного сложнее (одна целая линия длиннее!), Но, вероятно, быть значительно быстрее, когда / если количество слов становится очень большим. Также обратите внимание, что я использую std::unique_copy
в первую очередь для получения видимого вывода. Если вы хотите получить результат в коллекции, вы можете использовать стандартную уникальную / стирающую идиому для получения уникальных элементов в intermediate
.