使用C++實(shí)現(xiàn)簡單的文章生成器
1 前言
繼上次【C++】狗屁不通文章生成器之后,很久不想看一眼這個(gè)代碼,因?yàn)楫?dāng)時(shí)寫這個(gè)代碼深受中文字符的處理煩惱。而且現(xiàn)在回看,程序的模塊化、可讀性使我大受震驚,是在想不到當(dāng)時(shí)的我為什么要這樣做。于是昨天無心工作,想到了把這堆樂色改進(jìn)一下,至少做到能看的水平。遂記之。
2 改進(jìn)
2.1 字詞的前后關(guān)系
為了表示字詞的前后關(guān)系,即將句子劃分為前綴詞+后綴詞的關(guān)系,依然需要定義一個(gè)class wordpair,這里去除一些數(shù)據(jù)上的冗余,強(qiáng)化了類的封閉性。
class wordpair { private: string preword; // 前綴 map<string, int> sufwords; // 后綴,次數(shù) int count; // 總次數(shù) public: wordpair(string pre); wordpair(string pre, string suf); wordpair(string pre, map<string, int> suf); ~wordpair(); string getPreword() const; map<string, int> getSufwords() const; void setPreword(string pre); void setSufwords(map<string, int> suf); string toJson() const; void addSufword(string suf); string chooseSufword() const; };
采用map記錄后綴的出現(xiàn)次數(shù),數(shù)據(jù)的結(jié)構(gòu)性更強(qiáng),也易于查找。記錄所有后綴出現(xiàn)的總次數(shù)是為了在生成文章時(shí)選擇后綴提供方便(具體作用看3.1.3)
2.2 文章生成系統(tǒng)
將太多的操作塞進(jìn)main()函數(shù)的做法不夠美觀,且容易忘記各個(gè)部分的功能。于是這里將文章生成的功能抽象出來,作為一個(gè)類。主要的工作是記錄所有的字詞對、記錄生成的、文件流操作、文章生成等邏輯。
class createArticle { private: vector<wordpair> wordpairlist; string article; public: createArticle(); ~createArticle(); void importWords(string filename, int len_pre = 1, int len_suf = 1); void exportWords(string filename); void addWordPair(string pre, string suf); void generateArticle(string startword, int lenout = 10000); void printArticle(string filename); };
3 實(shí)現(xiàn)(部分)
由于大多函數(shù)都很簡單,這里只貼出部分比較重要的函數(shù)。
3.1 class wordpair
除去構(gòu)造函數(shù)、類成員輸出輸入等函數(shù),我們直接進(jìn)入主題。
3.1.1 轉(zhuǎn)化為 json
這個(gè)函數(shù)主要是為了輸出格式化的詞對,而文本文件中json格式的結(jié)構(gòu)性且簡單。
ps: 其實(shí)這個(gè)函數(shù)不太重要,主要目的是檢查。不過也可以為直接讀詞對做準(zhǔn)備(雖然這里沒有從文件導(dǎo)入詞對的功能)
string wordpair::toJson() const { string str = "\""; str += this->preword + "\" : {"; for (auto &it : this->sufwords) { str += "\"" + it.first + "\"" + ":" + to_string(it.second) + ","; } str += "}"; return str; }
效果演示:
3.1.2 添加后綴詞
添加后綴的函數(shù),邏輯是:
if 這個(gè)后綴已經(jīng)有記錄 then count++;
else 添加新的后綴到map中
void wordpair::addSufword(string suf) { for (auto &it : this->sufwords) { if (it.first == suf) { it.second++; return; } } this->sufwords[suf] = 1; // if the word is not in the map, add it with a count of 1 }
3.1.3 選擇后綴詞
這個(gè)函數(shù)的主要功能是從眾多后綴詞中選取一個(gè)(語料庫大的話就會多啦),選擇的策略是隨機(jī)數(shù)的方案,類似于轉(zhuǎn)盤抽獎(jiǎng)。實(shí)現(xiàn)方法如下:
string wordpair::chooseSufword() const { if (this->sufwords.size() == 1)//如果只有一個(gè)后綴詞就直接輸出,減少算力負(fù)擔(dān) { return this->sufwords.begin()->first; } else { // 隨機(jī)選擇一個(gè)后綴詞 random_device rd; ranlux48 engine(rd()); uniform_int_distribution<> dist(0, this->count);//在類中定義了count,這里就省掉了遍歷 int random_number = dist(engine);//產(chǎn)生一個(gè)隨機(jī)數(shù) std::string result; for (auto &it : this->sufwords)//抽獎(jiǎng) { if (random_number < it.second) { result = it.first; } else random_number -= it.second; } return result; } }
3.2 class createArticle
3.2.1文本分割
vector<string> charlist = splitchar(filestr);//先將從文件讀到的字符串分割 string preword = "", sufword = ""; for (int i = 0; i < charlist.size() - len_suf - len_pre; i++)//每次向后移動一個(gè)字符,進(jìn)行切割 { preword = "", sufword = ""; for (int j = i; j < i + len_pre + len_suf; j++) { if (j - i < len_pre) { preword += charlist[j];//從第i個(gè)字符開始,到第i+len_pre個(gè)字符連接起來作為前綴 } else { sufword += charlist[j];//從第i+len_pre個(gè)到字符開始,到第i+len_pre+len_suf個(gè)字符連接作后綴 } } this->addWordPair(preword, sufword);//添加進(jìn)wordpairlist }
3.2.2生成文章
/* startword——啟動詞 lenout——長度限制(避免無限循環(huán)) */ void createArticle::generateArticle(string startword, int lenout) { this->article += startword; bool stop; // 加一個(gè)停止標(biāo)志,當(dāng)無法匹配到前綴時(shí)停止 int prewordlen = this->wordpairlist.front().getPreword().length(); int sufwordlen = this->wordpairlist.front().getSufwords().begin()->first.length(); string lastword; for (int i = 0; i < lenout; ++i) { stop = true; if (this->article.length() >= prewordlen) // 如果文章長度大于詞對中前綴詞的長度,則直接拼接 { lastword = this->article.substr(this->article.length() - prewordlen, prewordlen);//article最后的len_pre個(gè)字符,作為前綴 for (auto &it : this->wordpairlist) { if (it.getPreword() == lastword)//通過lastword匹配詞對 { this->article += it.chooseSufword(); stop = false; break; } } if (stop)//遍歷了一邊詞對的list沒有匹配的詞對時(shí),退出循環(huán) break; } else//啟動詞長度小于詞對前綴的情況,例如詞對分割為3+2時(shí),啟動詞長度為2,小于前綴長度3,無法正常拼接,于是走此處 { lastword = this->article; for (auto &it : this->wordpairlist)//同上遍歷 { int position = it.getPreword().find(lastword); if (position != string::npos) { this->article += (it.getPreword() + it.chooseSufword()).substr(position+lastword.length(), sufwordlen);//先將前后綴連接,再從匹配到的位置開始截取 stop = false; break; } } if (stop) break; } } }
4演示
4.1 wordpair(3x2), 啟動詞(春天)
4.2 wordpair(2x1),啟動詞(春天)
4.3 wordpair(2x2),啟動詞(春天)
可見,加了長度限制的重要性。
5總結(jié)
目前,這個(gè)版本的處理方法不會出現(xiàn)中文亂碼,即使是中英文混合字符串也能正確讀取和分割。而且拼接時(shí)采用的隨機(jī)數(shù)策略,在語料庫足夠大的情況下可以有較好的靈活性。但是任然無法產(chǎn)出具備可讀性的文章。
到此這篇關(guān)于使用C++實(shí)現(xiàn)簡單的文章生成器的文章就介紹到這了,更多相關(guān)C++文章生成器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++瓦片地圖坐標(biāo)轉(zhuǎn)換的實(shí)現(xiàn)詳解
常見的瓦片地圖有矩形、菱形、正六邊形幾種。此文章主要討論菱形瓦片,也就是大家常說的2.5D,斜45度瓦片地圖。比如《紅警2》、《帝國時(shí)代2》都是采用這種技術(shù)2022-09-09C++實(shí)現(xiàn)簡單的信息管理系統(tǒng)
這篇文章主要為大家介紹了C++實(shí)現(xiàn)簡單的信息管理系統(tǒng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-04-04C++ for循環(huán)與nullptr的小知識點(diǎn)分享
這篇文章主要是來和大家介紹一些C++中的小知識點(diǎn),本文分享的是for循環(huán)與nullptr,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-05-05C語言數(shù)據(jù)結(jié)構(gòu)中二分查找遞歸非遞歸實(shí)現(xiàn)并分析
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu)中二分查找遞歸非遞歸實(shí)現(xiàn)并分析的相關(guān)資料,需要的朋友可以參考下2017-03-03