我尝试编写将 .txt 文件作为参数的代码,读取所述文件,然后获取文本的所有字母(字符)和文本的所有单词(字符串),将它们中的每一个放入映射或无序映射中(由 pair
#include <iostream>
#include <fstream>
#include <map>
#include <unordered_map>
#include <string>
#include <vector>
#include <algorithm>
#include <cctype>
#include <iterator>
#include <utility>
#include <type_traits>
std::string sanitize_word(const std::string& word)
{
//Whatever this does
}
bool compare(const auto& a, const auto& b)
{
//whatever this does }
std::unordered_map<char,int> get_letterfrequencies(const std::string& datei)
{//this works, get_wordfrequencies is pretty much the same, i just had to shorten it
}
std::unordered_map<std::string,int> get_wordfrequencies(const std::string& datei)
{
std::unordered_map<std::string, int> frequencies;
std::ifstream eingabe(datei);
if (!eingabe.is_open())
{//irrelevant
}
std::string word;
while (eingabe >> word)
{
std::string sanitized_word = sanitize_word(word);
if (sanitized_word.size() > 0)
{
if (frequencies.count(sanitized_word))
{
frequencies[sanitized_word]++;
}
else
{
frequencies[sanitized_word] = 1;
}
}
else
{//irrelevant
}
}
eingabe.close();
return frequencies;
}
template <typename Map, typename typ>
void print_frequencies_sorted(const Map& map)
{
std::vector<typ, int> mapvector(map.begin(), map.end());
//if (!std::is_same<typ, char>() && !std::is_same<typ, std::string>())
std::sort(mapvector.begin(), mapvector.end(), compare);
long double gesamt = 0;
for(const auto& p : mapvector)
{
gesamt += p.second;
double Anteil = (p.second / gesamt) * 100;
std::cout << "[ " << p.first << ": " << p.second << "x ; also " << Anteil << "% ]" << std::endl;
}
std::cout << " " << std::endl;
}
int main(int argc, char* argv[])
{
print_frequencies_sorted(get_wordfrequencies(argv[1]));
return 0;
}
我尝试将 print_frequencies_sorted 函数移动到一个单独的函数中,该函数只执行这两行,但它没有用,我尝试给它另一个映射,但我得到“错误:没有匹配的函数调用'print_frequencies_sorted(std :: map
std::vector
的第二个模板参数是分配器。
使用
std::vector<...> mapvector(map.begin(), map.end());
初始化vector时,需要使用map.begin()
返回的迭代器的值类型。
这个问题虽然是地图的元素有一个 const 限定的键(例如
std::pair<const std::string, int>
),这阻止了std::sort
.的重新分配
出于这个原因,您需要使用指向元素的指针(或者使用迭代器)。当然你需要一个合适的比较函数。您的版本将无法运行,因此请引入合适的模板或使用 lambda。
std::unordered_map<char, int> get_letterfrequencies()
{
// simplified logic here...
return { {'a', 7}, {'b', 3}, {'c', 4} };
}
std::unordered_map<std::string, int> get_wordfrequencies()
{
// simplified logic here...
return {
{"foo", 10},
{"bar", 3},
{"baz", 1},
};
}
template <typename Map>
void print_frequencies_sorted(const Map& map)
{
// introduce a type alias for the elements of the vector we sort
using SortedVectorElement = typename std::iterator_traits<decltype(map.begin())>::pointer;
// alternative relying on type value_type type alias of standard library maps
//using SortedVectorElement = const typename Map::value_type*;
std::vector<SortedVectorElement> mapvector;
mapvector.resize(map.size()); // resize to the number of elements needed
// convert the map entries elements to pointers to map entries
std::transform(map.begin(), map.end(), mapvector.begin(), [](auto& entry) { return &entry; });
// sort in by frequency in ascending order
std::sort(mapvector.begin(), mapvector.end(), [](SortedVectorElement v1, SortedVectorElement v2)
{
return v1->second < v2->second;
});
long double gesamt = 0;
for (auto p : mapvector) // no need to use references for pointers...
{
gesamt += p->second;
double Anteil = (p->second / gesamt) * 100;
std::cout << "[ " << p->first << ": " << p->second << "x ; also " << Anteil << "% ]" << std::endl;
}
std::cout << " " << std::endl;
}
int main(int argc, char* argv[])
{
print_frequencies_sorted(get_wordfrequencies());
print_frequencies_sorted(get_letterfrequencies());
return 0;
}
注意:我没有修复的一件事是
gesamt
不包含频率总和,而是到目前为止的频率总和。在所有情况下,它都会为第一个条目显示 100%,并且只为最后一个条目产生所需的输出,至少根据我的假设。
你可以用一种相当通用的方式解决你的问题。
如果您查看 std::unordered_map 和 std::map 的定义,您可以读到,这两个映射都有成员类型“key_type”和“mapped_type”。
所以,如果你创建一个模板,以映射类型作为参数,你可以找出键类型和映射类型。有了它,您可以创建一个非常通用的函数。它会吃和映射,
std::unordered_map
或 std::map
具有许多键类型和许多整数值类型。因此,无论键是char
还是string
都没有关系,计数器也可以是int
,unsigned_int
。或long
或其他什么。
因为我们要排序、赋值和求和,所以类型需要满足特殊的要求。我们可以使用 C++20 概念来确保我们获得正确的类型。但我不会在这里解决这个问题。
我们将通过以下方式实施解决方案
std::vector
并使用std::vector
s范围constructor(第5号)将地图中的所有日期复制到std::vector
std::accumulate
计算所有计数的总和。使用依赖数据类型生成的函数将非常紧凑且易于理解。请看:
#include <iostream>
#include <string>
#include <map>
#include <unordered_map>
#include <algorithm>
#include <utility>
#include <numeric>
#include <iomanip>
std::map<char, int> mci{ {'a',1},{'b',2},{'c',3} };
std::map<std::string, int> msi{ {"aa",4},{"bb",5},{"cc",6}};
std::unordered_map<char, int> umci{ {'d',10},{'e',20},{'f',30} };
std::unordered_map<std::string, int> umsi{ {"dd",40},{"ee",50},{"ff",60} };
template <typename MapType>
void print_frequencies_sorted(const MapType& anyMap) {
// Get the type of the pair from template element
using Pair = std::pair < MapType::key_type, MapType::mapped_type>;
// Copy data into map using the vectors range constructor
std::vector<Pair> data{ anyMap.begin(), anyMap.end() };
// Sort, descending, by frequency
std::sort(data.begin(), data.end(), [](const Pair& p1, const Pair& p2) {return p1.second == p2.second ? p1.first < p2.first : p1.second > p2.second; });
// Build the sum of all frequencies
typename MapType::mapped_type sum = std::accumulate(data.begin(), data.end(), 0, [](const MapType::mapped_type sum, const Pair& p) { return sum + p.second; });
// Print result
for (const auto& [item,count] : data)
std::cout << "[ " << item << ":\t" << count << "x ;\t also " << (double)count / (double)sum * 100.0 << "% ]" << std::endl;
}
int main() {
print_frequencies_sorted(mci);
print_frequencies_sorted(msi);
print_frequencies_sorted(umci);
print_frequencies_sorted(umsi);
}