【狗屁不通文章生成器】代碼分析 (javaScript)


這幾天在論壇上看到了一個很有意思的項目,一個生成“狗屁不通”的文章的程序。經過本人確定其的確是“狗屁不通”后,隨后又好奇其實現,於是在其[GitHub]項目里(https://github.com/menzi11/BullshitGenerator)找到源碼,研究了半晌。

我看的是網頁版的,就是JS文件里的核心代碼。

作者使用了大量的中文變量名,請謹慎閱讀,若有不適請立即離開

聲明的String數組--組成文章的核心內容

let 主題 = "一天掉多少根頭發";    //用戶輸入的文字(會圍繞該文字“生成”文章)

let 論述 = [ 
    "現在,解決主題的問題,是非常非常重要的。 所以, ",
    "我們不得不面對一個非常尷尬的事",
    //略,這里有很多的這種論述   ];
    
  let 名人名言 = [
    "伏爾泰曾經說過,不經巨大的困難,不會有偉大的事業。這不禁令我深思",
    "富勒曾經說過,苦難磨煉一些人,也毀滅另一些人。這不禁令我深思",
    		//略,這里有很多名人名言];
    		
   let 后面墊話 = [
    "這不禁令我深思。 ",
    "帶着這句話,我們還要更加慎重的審視這個問題: ",
    "這啟發了我, ",];
    
    let 前面墊話 = [
    "曾經說過",
    "在不經意間這樣說過",
];

上面的字符串數組是此程序的“基石”,也是可以通過修改其內容從而改變文章“內容”的重要備份

聲明的幾個函數--“合成”文章的核心算法

function 隨便取一句(列表){   //30*0.8  = 24
    let 坐標 = Math.floor( Math.random() * 列表.length );  //向下取整(小於等於她的最接近他的整數)
    return 列表[坐標];    //數組中某一句
};

上面的函數用於從個數組中取句子。

傳入的參數就是上面定義的數組的名字,同時返回一句。

function 隨便取一個數(最小值 = 0,最大值 = 100){
    let 數字 = Math.random()*( 最大值 - 最小值 ) + 最小值;   //math返回一個0.0-1.0之間的隨機數
    return 數字;
}

上面的函數用於生成1-100的隨機數。后面會講到他的作用

function 來點名人名言(){
    let 名言 = 隨便取一句(名人名言)   //名人名言是數組名
    名言 = 名言.replace("曾經說過", 隨便取一句(前面墊話) )   //前面墊話和后面墊話均是數組名
    名言 = 名言.replace("這不禁令我深思", 隨便取一句(后面墊話) )
    return 名言;
}

該函數的作用就和他的名字一樣,取一條名人名言。(會用到“隨便取一句”這個函數。

我們可以看到,在他的函數里面,當他取到名人名言的時候,對這個句子的前面墊話和后面墊話進行了替換。保證了名人名言某種程度地多樣性。(名人名言中的每句名人名言都有“曾經說過”和“這不禁令我深思”);

function 來點論述(){
    let 句子 = 隨便取一句(論述);
    句子 = 句子.replace(RegExp("主題", "g"),主題);   //g表示全局匹配   //使用正則進行替換,有點不懂
    return 句子;
}

上面的函數用於 提取“論述”。

在提取到“論述”后,該函數使用正則對每個論述中的“主題”兩字用讀者輸入的主題變量進行了替換。

也就是說,我們輸入的關鍵詞全部都會集中在這個函數生成的論述中。

function 增加段落(章節){
    if(章節[章節.length-1] === " "){
        章節 = 章節.slice(0,-2)     //去掉章節后面的多余的字符  ,便於后面再后面增加句號。
    }
    return "  " + 章節 + "。 "
}

該函數用於“增加段落”。

對“章節”進行縮進和去掉末尾的空格,並加上句號。

核心函數

function 生成文章(){
    let 文章 = []
    for(let 空 in 主題){   //遍歷字符串的每個元素(可能沒有用到空,他只是用於控制循環的次數
        let 章節 = "";
        let 章節長度 = 0;   //章節長度值得是字符的個數
        while( 章節長度 < 6000 ){
			//這個循環並不是只添加一個段落,當循環結束后會添加很多個段落。
			//當添加的文字總數達到6000時會跳出循環,
            let 隨機數 = 隨便取一個數();
            if(隨機數 < 5 && 章節.length > 200){
                章節 = 增加段落(章節);    //增加章節(章節長度>200時就添加章節了)。
                文章.push(章節); 
                章節 = "";    //<5的隨機數進行的操作    //有很小的幾率添加到文章中
            }else if(隨機數 < 20){
                let 句子 = 來點名人名言();
                章節長度 = 章節長度 + 句子.length;
                章節 = 章節 + 句子;   //>5 且<20的隨機數進行的操作    //添加名人名言:有五分之一的幾率
            }else{
                let 句子 = 來點論述();
                章節長度 = 章節長度 + 句子.length;
                章節 = 章節 + 句子;    //大於等於20的隨機數進行的操作
            }
        }
        章節 = 增加段落(章節);
        文章.push(章節);
    }
    return 文章.join("\n");
}

我們會很快注意到這個有點復雜的函數中的那一個while循環,我們先來看她。

while( 章節長度 < 6000 )

這里的判斷條件剛開始迷惑了我很久,讓我一直以為這是在控制段落的字符數,其實不是的,她是控制本次生成段落的總字數(也就是說這個循環執行一次會生成很多的段落,並且他們的總字符數是6000(不一定一定是6000))。

我們在看while外面的那個for循環:

for(let 空 in 主題)

“主題”是一個字符串,這個循環就是遍歷主題中的每一個字符,遍歷的次數就是這個for循環執行的次數,也就是他內部的(我們上面提到的)while循環執行的次數。

那么我么現在就可以知道了一件很重要的事情了:生成文章的總字數 = 輸入的字符數*6000 (大約)。

*************************************************************

while中有三個if else

他們通過生成的隨機數控制文章中名人名言和論述所占的部分(這是個令我很驚訝的算法)。

最后,是程序的啟動開關

console.log(生成文章())

​ 2019年11月6日晚 20:52


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM