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


    这几天在论坛上看到了一个很有意思的项目,一个生成“狗屁不通”的文章的程序。经过本人确定其的确是“狗屁不通”后,随后又好奇其实现,于是在其GitHub项目里链接找到源码,研究了半晌。

    我看的是网页版的,就是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("
    ");
    }
    

    我们会很快注意到这个有点复杂的函数中的那一个while循环,我们先来看她。

    while( 章节长度 < 6000 )
    

    这里的判断条件刚开始迷惑了我很久,让我一直以为这是在控制段落的字符数,其实不是的,她是控制本次生成段落的总字数(也就是说这个循环执行一次会生成很多的段落,并且他们的总字符数是6000(不一定一定是6000))。

    我们在看while外面的那个for循环:

    for(let 空 in 主题)
    

    “主题”是一个字符串,这个循环就是遍历主题中的每一个字符,遍历的次数就是这个for循环执行的次数,也就是他内部的(我们上面提到的)while循环执行的次数。

    那么我么现在就可以知道了一件很重要的事情了:生成文章的总字数 = 输入的字符数*6000 (大约)。

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

    while中有三个if else

    他们通过生成的随机数控制文章中名人名言和论述所占的部分(这是个令我很惊讶的算法)。

    最后,是程序的启动开关

    console.log(生成文章())
    

    ​ 2019年11月6日晚 20:52

  • 相关阅读:
    多项式乘法
    容斥计算多重组合
    D. Tokitsukaze, CSL and Stone Game
    优惠买商品(dp、greedy)
    数星星(单点更新,求前缀和)
    信息推送(单点更新,求前缀和)
    互相送礼物
    Codeforces Round #611 (Div. 3)E. New Year Parties
    多源bfs
    mysql事务和锁
  • 原文地址:https://www.cnblogs.com/WcxyBlog/p/11808607.html
Copyright © 2020-2023  润新知