Advertisement
Alaricy

тестировщие поисковика

Nov 27th, 2022
78
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.74 KB | None | 0 0
  1. #include <algorithm>
  2. #include <cmath>
  3. #include <iostream>
  4. #include <map>
  5. #include <set>
  6. #include <string>
  7. #include <utility>
  8. #include <vector>
  9. #include <cassert>
  10. #include <tuple>
  11.  
  12. using namespace std;
  13.  
  14. const int MAX_RESULT_DOCUMENT_COUNT = 5;
  15. const double PREСISION = 1e-6;
  16.  
  17. string ReadLine() {
  18.     string s;
  19.     getline(cin, s);
  20.     return s;
  21. }
  22.  
  23. int ReadLineWithNumber() {
  24.     int result;
  25.     cin >> result;
  26.     ReadLine();
  27.     return result;
  28. }
  29.  
  30. vector<string> SplitIntoWords(const string& text) {
  31.     vector<string> words;
  32.     string word;
  33.     for (const char c : text) {
  34.         if (c == ' ') {
  35.             if (!word.empty()) {
  36.                 words.push_back(word);
  37.                 word.clear();
  38.             }
  39.         }
  40.         else {
  41.             word += c;
  42.         }
  43.     }
  44.     if (!word.empty()) {
  45.         words.push_back(word);
  46.     }
  47.  
  48.     return words;
  49. }
  50.  
  51. struct Document {
  52.     int id;
  53.     double relevance;
  54.     int rating;
  55. };
  56.  
  57. enum class DocumentStatus {
  58.     ACTUAL,
  59.     IRRELEVANT,
  60.     BANNED,
  61.     REMOVED,
  62. };
  63.  
  64. vector<Document> SortVector(vector<Document> vector) {
  65.     sort(vector.begin(), vector.end(),
  66.         [](const Document& lhs, const Document& rhs) {
  67.             if (abs(lhs.relevance - rhs.relevance) < PREСISION) {
  68.                 return lhs.rating > rhs.rating;
  69.             }
  70.             else {
  71.                 return lhs.relevance > rhs.relevance;
  72.             }
  73.         });
  74.     if (vector.size() > MAX_RESULT_DOCUMENT_COUNT) {
  75.         vector.resize(MAX_RESULT_DOCUMENT_COUNT);
  76.     }
  77.  
  78.     return vector;
  79. }
  80.  
  81. class SearchServer {
  82. public:
  83.  
  84.     template <typename Predicate>
  85.     vector<Document> FindTopDocuments(const string& raw_query,
  86.         Predicate predicate) const {
  87.         const Query query = ParseQuery(raw_query);
  88.         auto matched_documents = FindAllDocuments(query, predicate);
  89.         return SortVector(matched_documents);
  90.     }
  91.  
  92.     vector<Document> FindTopDocuments(const string& raw_query, DocumentStatus status = DocumentStatus::ACTUAL) const {
  93.         auto matched_documents = FindTopDocuments(raw_query, [status](int document_id, DocumentStatus stat, int rating) { return status == stat; });
  94.         return matched_documents;
  95.     }
  96.  
  97.     void SetStopWords(const string& text) {
  98.         for (const string& word : SplitIntoWords(text)) {
  99.             stop_words_.insert(word);
  100.         }
  101.     }
  102.  
  103.     void AddDocument(int document_id, const string& document, DocumentStatus status,
  104.         const vector<int>& ratings) {
  105.         const vector<string> words = SplitIntoWordsNoStop(document);
  106.         const double inv_word_count = 1.0 / words.size();
  107.         for (const string& word : words) {
  108.             word_to_document_freqs_[word][document_id] += inv_word_count;
  109.         }
  110.         documents_.emplace(document_id, DocumentData{ ComputeAverageRating(ratings), status });
  111.     }
  112.  
  113.  
  114.  
  115.     int GetDocumentCount() const {
  116.         return documents_.size();
  117.     }
  118.  
  119.     tuple<vector<string>, DocumentStatus> MatchDocument(const string& raw_query,
  120.         int document_id) const {
  121.         const Query query = ParseQuery(raw_query);
  122.         vector<string> matched_words;
  123.         for (const string& word : query.plus_words) {
  124.             if (word_to_document_freqs_.count(word) == 0) {
  125.                 continue;
  126.             }
  127.             if (word_to_document_freqs_.at(word).count(document_id)) {
  128.                 matched_words.push_back(word);
  129.             }
  130.         }
  131.         for (const string& word : query.minus_words) {
  132.             if (word_to_document_freqs_.count(word) == 0) {
  133.                 continue;
  134.             }
  135.             if (word_to_document_freqs_.at(word).count(document_id)) {
  136.                 matched_words.clear();
  137.                 break;
  138.             }
  139.         }
  140.         return { matched_words, documents_.at(document_id).status };
  141.     }
  142.  
  143. private:
  144.     struct DocumentData {
  145.         int rating;
  146.         DocumentStatus status;
  147.     };
  148.  
  149.     set<string> stop_words_;
  150.     map<string, map<int, double>> word_to_document_freqs_;
  151.     map<int, DocumentData> documents_;
  152.  
  153.     bool IsStopWord(const string& word) const {
  154.         return stop_words_.count(word) > 0;
  155.     }
  156.  
  157.     vector<string> SplitIntoWordsNoStop(const string& text) const {
  158.         vector<string> words;
  159.         for (const string& word : SplitIntoWords(text)) {
  160.             if (!IsStopWord(word)) {
  161.                 words.push_back(word);
  162.             }
  163.         }
  164.         return words;
  165.     }
  166.  
  167.     static int ComputeAverageRating(const vector<int>& ratings) {
  168.         if (ratings.empty()) {
  169.             return 0;
  170.         }
  171.         int rating_sum = 0;
  172.         for (const int rating : ratings) {
  173.             rating_sum += rating;
  174.         }
  175.         return rating_sum / static_cast<int>(ratings.size());
  176.     }
  177.  
  178.     struct QueryWord {
  179.         string data;
  180.         bool is_minus;
  181.         bool is_stop;
  182.     };
  183.  
  184.     QueryWord ParseQueryWord(string text) const {
  185.         bool is_minus = false;
  186.         // Word shouldn't be empty
  187.         if (text[0] == '-') {
  188.             is_minus = true;
  189.             text = text.substr(1);
  190.         }
  191.         return { text, is_minus, IsStopWord(text) };
  192.     }
  193.  
  194.     struct Query {
  195.         set<string> plus_words;
  196.         set<string> minus_words;
  197.     };
  198.  
  199.     Query ParseQuery(const string& text) const {
  200.         Query query;
  201.         for (const string& word : SplitIntoWords(text)) {
  202.             const QueryWord query_word = ParseQueryWord(word);
  203.             if (!query_word.is_stop) {
  204.                 if (query_word.is_minus) {
  205.                     query.minus_words.insert(query_word.data);
  206.                 }
  207.                 else {
  208.                     query.plus_words.insert(query_word.data);
  209.                 }
  210.             }
  211.         }
  212.         return query;
  213.     }
  214.  
  215.     // Existence required
  216.     double ComputeWordInverseDocumentFreq(const string& word) const {
  217.         return log(GetDocumentCount() * 1.0 / word_to_document_freqs_.at(word).size());
  218.     }
  219.  
  220.     template <typename Predicate>
  221.     vector<Document> FindAllDocuments(const Query& query, Predicate predicat) const {
  222.         map<int, double> document_to_relevance;
  223.         for (const string& word : query.plus_words) {
  224.             if (word_to_document_freqs_.count(word) == 0) {
  225.                 continue;
  226.             }
  227.             const double inverse_document_freq = ComputeWordInverseDocumentFreq(word);
  228.             for (const auto [document_id, term_freq] : word_to_document_freqs_.at(word)) {
  229.                 if (predicat(document_id, documents_.at(document_id).status, documents_.at(document_id).rating)) {
  230.                     document_to_relevance[document_id] += term_freq * inverse_document_freq;
  231.                 }
  232.             }
  233.         }
  234.  
  235.         for (const string& word : query.minus_words) {
  236.             if (word_to_document_freqs_.count(word) == 0) {
  237.                 continue;
  238.             }
  239.             for (const auto [document_id, _] : word_to_document_freqs_.at(word)) {
  240.                 document_to_relevance.erase(document_id);
  241.             }
  242.         }
  243.  
  244.         vector<Document> matched_documents;
  245.         for (const auto [document_id, relevance] : document_to_relevance) {
  246.             matched_documents.push_back(
  247.                 { document_id, relevance, documents_.at(document_id).rating });
  248.         }
  249.         return matched_documents;
  250.     }
  251. };
  252.  
  253. //Тестирующие функции:
  254.  
  255.  
  256.  
  257. void TestExcludeStopWordsFromAddedDocumentContent() {
  258.     const int doc_id = 42;
  259.     const string content = "cat in the city"s;
  260.     const vector<int> ratings = { 1, 2, 3 };
  261.     // Сначала убеждаемся, что поиск слова, не входящего в список стоп-слов,
  262.     // находит нужный документ
  263.     {
  264.         SearchServer server;
  265.         server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  266.         const auto found_docs = server.FindTopDocuments("in"s);
  267.         assert(found_docs.size() == 1);
  268.         const Document& doc0 = found_docs[0];
  269.         assert(doc0.id == doc_id);
  270.     }
  271.  
  272.     // Затем убеждаемся, что поиск этого же слова, входящего в список стоп-слов,
  273.     // возвращает пустой результат
  274.     {
  275.         SearchServer server;
  276.         server.SetStopWords("in the"s);
  277.         server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  278.         assert(server.FindTopDocuments("in"s).empty());
  279.     }
  280. }
  281. void TestMinusWordsFromAddedDocumentContent() {
  282.     const int doc_id = 42;
  283.     const string content = "cat in the city"s;
  284.     const vector<int> ratings = { 1, 2, 3 };
  285.     SearchServer server;
  286.     server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  287.     assert(server.FindTopDocuments("cat in the -city"s).empty());
  288.    
  289. }
  290.  
  291. void TestMatching() {
  292.     const int doc_id = 42;
  293.     const string content = "cat in the city"s;
  294.     const vector<int> ratings = { 1, 2, 3 };
  295.     SearchServer server;
  296.     server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  297.     tuple<vector<string>, DocumentStatus> match = server.MatchDocument("cat in the city"s, 42);
  298.     vector<string> answer = { "cat"s, "city"s, "in"s, "the"s };
  299.     vector<string> totest = get<0>(match);
  300.     assert(answer==totest);
  301. }
  302.  
  303.  
  304. void TestMatchingWithMinus() {
  305.     const int doc_id = 42;
  306.     const string content = "cat in the city"s;
  307.     const vector<int> ratings = { 1, 2, 3 };
  308.     SearchServer server;
  309.     server.AddDocument(doc_id, content, DocumentStatus::ACTUAL, ratings);
  310.     tuple<vector<string>, DocumentStatus> match = server.MatchDocument("cat in the -city"s, 42);
  311.     vector<string> totest = get<0>(match);
  312.     assert(totest.empty());
  313. }
  314.  
  315. void RelevanceSortingTest() {
  316.     SearchServer server;
  317.     server.AddDocument(1, "Привет прогулка друг дрова"s, DocumentStatus::ACTUAL, { 1,2,3 });
  318.     server.AddDocument(2, "Привет привет друг дрова"s, DocumentStatus::ACTUAL, { 1,2,3 });
  319.     server.AddDocument(3, "Привет привет привет дрова"s, DocumentStatus::ACTUAL, { 1,2,3 });
  320.     server.AddDocument(4, "Привет привет привет привет"s, DocumentStatus::ACTUAL, { 1,2,3 });
  321.     server.AddDocument(5, "Трактор прогулка друг дрова"s, DocumentStatus::ACTUAL, { 1,2,3 });
  322.    
  323.     vector<int> relevances;
  324.     for (const Document& document : server.FindTopDocuments("Привет привет"s))
  325.     {
  326.         relevances.push_back(document.id);
  327.     }
  328.     vector <int> answer = { 4,3,2,1 };
  329.     assert(relevances == answer);
  330. }
  331.  
  332. void RatingTest() {
  333.     SearchServer server;
  334.     server.AddDocument(1, "Привет прогулка друг дрова"s, DocumentStatus::ACTUAL, { 5,7,8,9,12,20,100,1,2,5,4,6,7,1,-3,-5,-1});
  335.     //cout << "rating = "s << server.FindTopDocuments("Привет привет"s)[0].rating << " }"s << endl;
  336.     assert((server.FindTopDocuments("Привет привет"s)[0].rating) == 10);
  337. }
  338.  
  339. void PredicatTest() {
  340.     SearchServer server;
  341.     server.SetStopWords("и в на"s);
  342.     server.AddDocument(0, "белый кот и модный ошейник"s, DocumentStatus::ACTUAL, { 8, -3 });
  343.     server.AddDocument(1, "пушистый кот пушистый хвост"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  344.     server.AddDocument(2, "ухоженный пёс выразительные глаза"s, DocumentStatus::ACTUAL, { 5, -12, 2, 1 });
  345.     server.AddDocument(3, "ухоженный скворец евгений"s, DocumentStatus::BANNED, { 9 });
  346.     int id1 = server.FindTopDocuments("пушистый ухоженный кот"s)[0].id;
  347.     int id2 = server.FindTopDocuments("пушистый ухоженный кот"s, DocumentStatus::BANNED)[0].id;
  348.     int id3 = server.FindTopDocuments("пушистый ухоженный кот"s, [](int document_id, DocumentStatus status, int rating) { return document_id % 2 == 0; })[0].id;
  349.     assert(id1 == 1);
  350.     assert(id2 == 3);
  351.     assert(id3 == 0);
  352. }
  353.  
  354. void StatusTest() {
  355.     SearchServer server;
  356.     server.AddDocument(0, "ухоженный скворец евгений"s, DocumentStatus::ACTUAL, { 9 });
  357.     server.AddDocument(1, "ухоженный скворец евгений"s, DocumentStatus::IRRELEVANT, { 9 });
  358.     server.AddDocument(2, "ухоженный скворец евгений"s, DocumentStatus::BANNED, { 9 });
  359.     server.AddDocument(3, "ухоженный скворец евгений"s, DocumentStatus::REMOVED, { 9 });
  360.     int id1 = server.FindTopDocuments("евгений - свинья"s, DocumentStatus::BANNED)[0].id;
  361.     int id2 = server.FindTopDocuments("евгений - скворец"s, DocumentStatus::ACTUAL)[0].id;
  362.     int id3 = server.FindTopDocuments("евгений - яйцо"s, DocumentStatus::REMOVED)[0].id;
  363.     int id4 = server.FindTopDocuments("евгений - полушлюз"s, DocumentStatus::IRRELEVANT)[0].id;
  364.     assert(id1 == 2);
  365.     assert(id2 == 0);
  366.     assert(id3 == 3);
  367.     assert(id4 == 1);
  368. }
  369.  
  370. void RelevanceTest() {
  371.     SearchServer search_server;
  372.     search_server.SetStopWords("и в на"s);
  373.     search_server.AddDocument(0, "белый кот и модный ошейник"s, DocumentStatus::ACTUAL, { 8, -3 });
  374.     search_server.AddDocument(1, "пушистый кот пушистый хвост"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  375.     search_server.AddDocument(2, "ухоженный пёс выразительные глаза"s, DocumentStatus::ACTUAL, { 5, -12, 2, 1 });
  376.     search_server.AddDocument(3, "ухоженный скворец евгений"s, DocumentStatus::BANNED, { 9 });
  377.     double rel = search_server.FindTopDocuments("пушистый ухоженный кот"s)[0].relevance;
  378.     assert(rel == 0.8664339756999316);
  379. }
  380.  
  381.  
  382. void TestSearchServer() {
  383.     TestExcludeStopWordsFromAddedDocumentContent();
  384.     TestMinusWordsFromAddedDocumentContent();
  385.     //
  386.     TestMatching();
  387.     TestMatchingWithMinus();
  388.     //
  389.     RelevanceSortingTest();
  390.     RatingTest();
  391.     PredicatTest();
  392.     StatusTest();
  393.     RelevanceTest();
  394.  
  395.     cout << "тесты ок" << endl;
  396. }
  397.  
  398. // ==================== для примера =========================
  399.  
  400. void PrintDocument(const Document& document) {
  401.     cout << "{ "s
  402.         << "document_id = "s << document.id << ", "s
  403.         << "relevance = "s << document.relevance << ", "s
  404.         << "rating = "s << document.rating << " }"s << endl;
  405. }
  406. int main() {
  407.     setlocale(LC_ALL, "Russian");
  408.     TestSearchServer();
  409.     SearchServer search_server;
  410.     search_server.SetStopWords("и в на"s);
  411.     search_server.AddDocument(0, "белый кот и модный ошейник"s, DocumentStatus::ACTUAL, { 8, -3 });
  412.     search_server.AddDocument(1, "пушистый кот пушистый хвост"s, DocumentStatus::ACTUAL, { 7, 2, 7 });
  413.     search_server.AddDocument(2, "ухоженный пёс выразительные глаза"s, DocumentStatus::ACTUAL, { 5, -12, 2, 1 });
  414.     search_server.AddDocument(3, "ухоженный скворец евгений"s, DocumentStatus::BANNED, { 9 });
  415.     cout << "ACTUAL by default:"s << endl;
  416.     for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s)) {
  417.         PrintDocument(document);
  418.     }
  419.     cout << "BANNED:"s << endl;
  420.     for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s, DocumentStatus::BANNED)) {
  421.         PrintDocument(document);
  422.     }
  423.     cout << "Even ids:"s << endl;
  424.     for (const Document& document : search_server.FindTopDocuments("пушистый ухоженный кот"s, [](int document_id, DocumentStatus status, int rating) { return document_id % 2 == 0; })) {
  425.         PrintDocument(document);
  426.     }
  427.     return 0;
  428. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement