您好,登錄后才能下訂單哦!
這篇“基于C++如何編寫一個(gè)文章生成器”文章的知識點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“基于C++如何編寫一個(gè)文章生成器”文章吧。
由用戶輸入啟動詞,根據(jù)語料庫中統(tǒng)計(jì)的詞語前后綴關(guān)系,自動生成一片新的文章。
比如:春天來了,大地媽媽穿上了碧綠的衣裳。嫩綠的小草從地下探出頭來,陶醉在美麗的春天里。
前后綴關(guān)系:[前綴,后綴]。上面這段話的前后綴關(guān)系有:[春天,來/里],[天來,了],[天里,。],[來了,,]等。
說明:
啟動詞:用戶輸入的一個(gè)詞,由這個(gè)詞開始生成文章所有內(nèi)容。
前/后綴:一個(gè)詞前后連續(xù)的n個(gè)字符。比如前綴為“春天”,由例句中得到后綴可為“來”或“里”,即表示一種語言的前后關(guān)系。
1.準(zhǔn)備語料庫:準(zhǔn)備相關(guān)文章,存為文件。利用程序讀取文章內(nèi)容,獲取文章語句中詞語的前后關(guān)系,即語料庫。語料庫的豐富程度由文章的數(shù)量決定,語料庫又決定程序運(yùn)行的時(shí)間和生成的文章質(zhì)量。
2.構(gòu)建前后綴關(guān)系:根據(jù)語料庫,依據(jù)設(shè)定的前后綴長度,構(gòu)建字詞的前后綴關(guān)系。
3.生成文章:用戶輸入啟動詞,根據(jù)啟動詞為前綴生成后綴得到文段,再根據(jù)文段生成新的前綴,以新前綴生成新后綴以此類推,得到一片文章。
4.應(yīng)盡量避免循環(huán):有時(shí)語料庫中可能出現(xiàn)類似“為所欲為”的接龍結(jié)構(gòu),造成死循環(huán),同時(shí)生成的內(nèi)容也沒有了意義。
5.輸入輸出形式:
//輸入:
2 //前綴詞長度
2 //后綴詞長度
春天 //啟動詞
//輸出:
[一篇文章]
讀文件:從多篇文章中讀取文件內(nèi)容為字符串,以前/后綴的長度遍歷獲取前/后綴,并建立前后綴關(guān)系。
寫文件:為觀察程序執(zhí)行情況,將前/后綴字符對和生成的文章寫入文件。
當(dāng)需要在兩種數(shù)據(jù)間建立一種關(guān)系時(shí),可以使用結(jié)構(gòu)化數(shù)據(jù)進(jìn)行存儲,比如建立前后綴關(guān)系可以采用字典類結(jié)構(gòu)進(jìn)行存儲,C++也有相應(yīng)的頭文件。
除此之外,還可以將其抽象為一種類,可以定制類的行為和結(jié)構(gòu)。這里選擇自建一個(gè)類進(jìn)行存儲。
類設(shè)計(jì):
一個(gè)類記錄一個(gè)前綴和它的所有后綴,
記錄后綴的出現(xiàn)次數(shù)
記錄后綴的個(gè)數(shù)
class wordpair{ private: char **suffix //后綴字符串?dāng)?shù)組,一個(gè)后綴存為一個(gè)字符串 int *freq //一個(gè)后綴出現(xiàn)的次數(shù) public: char *prefix //前綴字符串,聲明為公共成員,方便外部查找 int length //記錄后綴的個(gè)數(shù) }
在C++的頭文件中,可以使用string類型代替char類型的字符串,而且對于字符串的操作也更方便。比如
string str1 = "abcd"; string str2 = "efghijk"; string str3 = str1 + str2; // = “abcdefghijk”; 字符串拼接 string str4 = str3.substr(3,2); // = "de"; 從下標(biāo)為3的字符開始,截取長度為2的子字符串
以前一直覺得C語言和C++處理字符串很麻煩,現(xiàn)在倒覺得還是很方便的。
C語言和C++中,基礎(chǔ)的數(shù)組長度相對固定,但也不是不能改變,只是相對麻煩一些。在這個(gè)程序中,一個(gè)wordpair只存儲一個(gè)前綴和它的后綴們,所以需要創(chuàng)建一個(gè)wordpair類型的列表來存儲全部的前后綴。
我用的方法是來回地復(fù)制
int main(){ int len; int list[len]; //假設(shè)有一個(gè)長度為len的整型數(shù)組 int p[len+1]; //開辟一個(gè)len+1個(gè)整型長度的空間 // 把 list 復(fù)制到 p,然后 list = new int[len+1]; //將list長度+1 //把 p 復(fù)制過來 }
由于數(shù)組加長在程序中多次調(diào)用,且需要增長的數(shù)組各不相同,所以在這里我定義了一個(gè)函數(shù)模板,以盡可能少的代碼完成相同的任務(wù)。
template<class T> T *append2list(T *list, T t, int len){ T copy[len + 1]; // 用于備份的空間 for(int i=0; i<len; i++){ copy[i] = list[i];} copy[len] = t; //在末尾增加元素 len++; list = new T[len]; //變長 for(int i=0; i<len; i++){ //拷貝回來 list[i] = copy[i];} return list; }
首先是類成員,應(yīng)該有:
class Wordpair { private: string *suffix; //后綴列表,一個(gè)前綴可能對應(yīng)多個(gè)后綴 int *freq; // 整型數(shù)組,依此記錄后綴的頻率 public: string prefix; // 前綴 int length; // 記錄長度 , 一個(gè)前綴對應(yīng)size個(gè)后綴 Wordpair(string prefix, string suffix){ //構(gòu)造函數(shù) this->prefix = prefix; this->suffix = new string[1]; this->suffix[0] = suffix; this->freq = new int[1]; this->freq[0] = 1; this->length = 1; } Wordpair(){ //構(gòu)造函數(shù) this->prefix = "", this->suffix = new string[1]; this->suffix[0] = ""; this->freq = new int[1]; this->freq[0] = 0; this->length = 0; } /* 判斷這個(gè)后綴是否已經(jīng)有記錄,有返回下標(biāo),沒有則返回-1 */ int hasRecorded(string word){} /* 添加一個(gè)后綴 */ bool push(string word){} /* 找出出現(xiàn)次數(shù)最多的后綴的下標(biāo),采用更可信的后綴 */ string maxFrequency(){} /* 轉(zhuǎn)化為字符串,方便輸出 */ string to_String()const{} /* 重載賦值運(yùn)算符,方便與其他類型的列表共用函數(shù) */ Wordpair& operator=(Wordpair &pair){} };
除此之外,還可以重載輸出運(yùn)算符<<,便于調(diào)試時(shí)在函數(shù)中輸出wordpair值:
ostream& operator<<(ostream& out, const Wordpair& w){ out<<w.to_String(); return out; }
程序運(yùn)行時(shí),需要讀取文件為字符串,當(dāng)文件較多時(shí)把這個(gè)功能抽象出來,調(diào)用很方便。
// 讀文件 string getfile(char *path){ string alticle = ""; //初始化字符串 ifstream fin(path, ios::in); //打開文件 if(!fin.is_open()){ cout<<"文件讀取錯誤!"<<endl; return NULL; } string buffer; while(getline(fin,buffer)){ //讀取 alticle.append(buffer); //新的行添加到alticle尾部 } fin.close(); return alticle; }
主要是寫入生成的字符對,方便調(diào)試
// 寫文件,記錄詞組對 void exportData(Wordpair *pairlist, int len, int prelen, int suflen){ char path[32]; sprintf(path,"./word-pairs(%dx%d).txt",prelen,suflen); ofstream fout(path, ios::out); for(int i=0; i<len; i++){ fout<<pairlist[i]; //在這里就體現(xiàn)了重載<<運(yùn)算符的好處 } fout.close(); cout<<"詞組對已經(jīng)寫入文件< "<<path<<" >"<<endl; }
讀取到文件后,將字符串從下標(biāo)0開始,讀取前綴+后綴的長度,然后從1開始讀取前綴+后綴的長度。循環(huán)的次數(shù)應(yīng)該是字符串總長度 - (前綴長度+后綴長度 -1),以保證下標(biāo)不會溢出。
Wordpair *alticle2Wordpair(Wordpair *pairlist, int &length,string alticle, int prefix_len, int suffix_len){ for(int i=0; i<alticle.length()/2-prefix_len-suffix_len+1; i++){ bool hasrecord = false; string prefix=alticle.substr(i*2,prefix_len*2); // i為什么要×2?因?yàn)樵赿evcpp中發(fā)現(xiàn)一個(gè)中文字符相當(dāng)于兩個(gè)英文字符,不乘2會亂碼。 string suffix=alticle.substr((i+prefix_len)*2,suffix_len*2); for(int j=0;j<length;j++){ if(pairlist[j].prefix == prefix){ // 如果已經(jīng)有了這個(gè)前綴,則添加后綴 pairlist[j].push(suffix); hasrecord = true; break; } } if(!hasrecord){ // 沒有這個(gè)前綴則詞組對列表長度增加 Wordpair pair(prefix, suffix); pairlist = append2list(pairlist, pair, length); length++; } } return pairlist; }
在此基礎(chǔ)上,對每次讀文件都進(jìn)行一次,就能獲取全部文件的字符對。
得到語料庫之后,需要根據(jù)語料庫拼接出文章。我這里采用的方法有點(diǎn)問題,當(dāng)完全防止出現(xiàn)循環(huán)文本的時(shí)候,文章過短,當(dāng)放開一點(diǎn)對循環(huán)文本的時(shí)候,循環(huán)文本總是出現(xiàn),算法上想不通。希望有大佬提供一點(diǎn)思路。
// 判斷前綴是否在列表內(nèi),有則返回下標(biāo),沒有則返回-1 int hasrecord(Wordpair *pairlist, int len, string preword){ for(int i=0; i<len; i++){ if(preword == pairlist[i].prefix){ return i; } } return -1; } // 拼接文章 void createAlticle(Wordpair *pairlist, int len, string startword, int prefix_len, int suffix_len){ string preword = startword; int i=0; int index = hasrecord(pairlist, len, preword); string alticle = preword; int alticle_len = prefix_len; //長度(中文字符標(biāo)準(zhǔn)) while(index != -1){ string newword = pairlist[index].maxFrequency(); // 避免循環(huán) if(alticle.find(newword)==string::npos //表示這個(gè)前綴沒有在文章中出現(xiàn)過 // || alticle_len - alticle.rfind(newword) > 600 //表示相同的詞之間最少間隔多少。加上這個(gè)條件后有循環(huán),注釋后文章顯著變短 ){ alticle.append(pairlist[index].maxFrequency()); alticle_len += suffix_len; preword = alticle.substr((alticle_len-prefix_len)*2, alticle_len*2);} else{ preword = pairlist[index+1].maxFrequency(); } index = hasrecord(pairlist, len, preword); } cout<<alticle<<endl; ofstream fout(CREATE_ALTICLE, ios::out); fout<<alticle; fout.close(); cout<<"文章寫入文件 < "<<CREATE_ALTICLE<<" >"<<endl; }
以上就是關(guān)于“基于C++如何編寫一個(gè)文章生成器”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。