废话生成器

废话生成器

> 编程

废话生成器(pipotron)是一种算法,它把预先定义好的词语首尾相接,随机拼出一个句子。废话生成器的主要目的是娱乐:由于文本是随机生成的,它的意思和读起来的感觉通常都既古怪又好笑。

拥有一个废话生成器看起来可能毫无用处,但视情况而定,它那些不着边际的内容可以吓唬敌人、逗笑敌人,甚至让敌人目瞪口呆。每一次的目的都是扰乱对手,并趁对手分心之际施展一套制胜战术。

要制作一个废话生成器,首先要编写一个数据库,也就是一些数组,里面列出可能出现在句子里的词语;然后编写算法本身,由它从数据库中随机挑选词语,拼成一个句子。当然,为了得到令人满意的句子,生成的文本在语法、动词变位和拼写上都必须正确,尽管句子的意思本身可以离奇又荒诞。

废话生成器算法的复杂度可以相差极大:它既可以只生成由一个预先写好的主语加一个动词组成的简单句子,也可以生成复杂的句子,包含多个分句、主谓一致、阴阳性和单复数的处理、各种状语……

简易废话生成器示例

理论就讲到这里,下面来实践一下。我们将(用 LeekScript)编写一个非常简单的废话生成器,属于上面提到的第一类。

数据库 我们的废话生成器生成的句子将由以下部分组成:

因此我们需要两个数组:数组 sujets(主语)和数组 verbes(动词):

global sujets = []; global verbes = [];

接下来是制作废话生成器最有趣的环节之一:挑选数据库的内容。为了让生成的句子不至于千篇一律,我们要在数据库里放入尽可能多的词语。

比如,我们在 sujets 中放入以下字符串:

在 verbes 中放入:

这些只是例子,你当然可以在数据库里放任何你想要的词(尽情发挥你的创意吧),但是注意:为了确保句子正确,只能放彼此搭配的动词和主语!例如,我的所有动词都变位成了第三人称单数,因为我的所有主语都是单数名词短语。如果我把 "le chien"(这条狗)换成 "les chiens"(这些狗),就可能得到 "les chiens dort" 这样的句子,这在法语里显然是错误的。

此外,强烈建议每个数组只存放数组名所指的那一类字符串。例如,在 verbes 中我只放了动词,没有放带副词的动词(比如 "court rapidement",意为“跑得很快”)或带直接宾语的动词。在 sujets 中,我没有放单独的名词或形容词,只放了完整的主语。在更复杂的废话生成器里,这个好习惯能让你的代码更清晰,并减少语法错误。

现在,我们就有了一个由主语和动词组成的完整数据库:

global sujets = [ "le chien", "un avion", "la grosse tarte", "Luke Skywalker", "un poney rose", "le kangourou de ma grand-mère", "un grand ballon velu rempli de mayonnaise roumaine" ]; global verbes = [ "court", "glisse", "crie", "grandit", "dort", "miroite", "s'assoit" ];

算法

有了数据库,就可以开始编写废话生成器的算法了,我们把它放在函数 getPipotron 中。这个算法需要:

我建议你创建一个函数 randTab,从非关联数组中随机取出一个值。正是这个函数负责完成废话生成器标志性的随机选词。它大概是这样的:

function random(array){ return array[randInt(0, count(array))]; }

现在可以创建一个变量 phrase,用来存放要说的句子:

function getPipotron(){ var phrase = random(sujets) + " " + random(verbes); } function random(array){ return array[randInt(0, count(array))]; }

问题在于,我们的 phrase 还算不上一个真正的句子:它开头没有大写字母,结尾也没有句号。因此,我们需要创建一个函数 addPonctuation 来处理这个句子:

function addPonctuation(@text){ text += "."; //首字母大写 text = toUpper(charAt(text, 0)) + substring(text, 1, length(text) - 1); }

最后只需对 phrase 调用这个函数,再返回 phrase,我们的废话生成器就完成了!

global sujets = [ "le chien", "un avion", "la grosse tarte", "Luke Skywalker", "un poney rose", "le kangourou de ma grand-mère", "un grand ballon velu rempli de mayonnaise roumaine" ]; global verbes = [ "court", "glisse", "crie", "grandit", "dort", "miroite", "s'assoit" ]; function getPipotron(){ var phrase = random(sujets) + " " + random(verbes); addPonctuation(phrase); return phrase; } function random(array){ return array[randInt(0, count(array))]; } function addPonctuation(@text){ text += "."; //首字母大写 text = toUpper(charAt(text, 0)) + substring(text, 1, length(text) - 1); }

我们本可以直接在数据库里把主语的首字母写成大写,并在动词末尾加上句号,但现在这种做法更动态、更灵活。设想一下,如果我们想生成更复杂的句子,在动词后面再接一个直接宾语:假如动词里已经“预先带了句号”,句号就会出现在句子中间、直接宾语之前,而整段文字的结尾反倒没有句号。你应该明白了:先造好句子再加标点,是一个值得养成的好习惯,等你制作更精巧的废话生成器时就会用得上。

测试这个废话生成器时你会发现,句子的组合非常有限,两次得到一模一样的句子并不少见。这是因为这个废话生成器实在太简单了:只有 7 个主语 × 7 个动词 = 49 种可能的句子。这正是把废话生成器做得更复杂的好处:得到更长、更多样、也更好笑的句子。

社区中的一些废话生成器

更多信息请见这个关于废话生成器的主题。

注:我知道较真的读者要说什么了——这篇介绍没有讲随机和伪随机的区别。没错,理论上,真正的废话生成器应该只由真正随机的选择构成,而 rand 和 randInt 函数实际返回的是伪随机数。不过在这里,这点差别可以忽略不计,这些函数用在我们的废话生成器上完全够用。