问题背景
给定一组特定的语法规则、语料单词,而后依此,不断替换所有概念词,直到生成一句具体的句子。
概念词包括:句子、主语、主语s、代号等,这些指向某一个语法概念,实际不应出现在最终句子中的词。
具体词包括:苹果、小狗、喜欢、吃掉、漂亮、一个、但是、而且等,这些构成最终句子的词。
算法思路
-
确定语法规则、语料单词。
“=”左边,是一个概念词;“=”右边,是这个概念词,对应可能的情况,各种情况之间用“|”隔开。
grammar_rule = """
复合句子 = 句子 连词 复合句子 | 句子
句子 = 主语s 谓语 宾语s
谓语 = 喜欢 | 讨厌 | 吃掉 | 玩 | 跑
主语s = 主语 和 主语 | 主语
宾语s = 宾语 和 宾语 | 宾语
主语 = 冠词 定语 代号
宾语 = 冠词 定语 代号
代号 = 名词 | 代词
名词 = 苹果 | 鸭梨 | 西瓜 | 小狗 | 小猫 | 滑板 | 老张 | 老王
代词 = 你 | 我 | 他 | 他们 | 你们 | 我们 | 它
定语 = 漂亮的 | 今天的 | 不知名的 | 神秘的 | 奇奇怪怪的
冠词 = 一个 | 一只 | 这个 | 那个
连词 = 但是 | 而且 | 不过
"""
-
将1中的语法规则,转换为字典形式,方便后续程序使用
其中,“=”左边就是字典的“键”,“=”右边就是字典的“值”。我们把“=“右边的内容,用”|“分割成列表。
def parse_grammar(rule):
grammar = {}
for line in rule.split('\n'):
if len(line.strip()) == 0: continue # 如果是空行,就跳过
ic(line)
target, expands = line.strip().split('=') # 分割“=”左右两边,左边为键,右边为值
grammar[target.strip()] = expands.split('|') # 将“=”右边的,用“|”分割成一个列表
return grammar
-
语句生成
-
实现的原理,就是从语法规则中的“复合句子”开始,不断将其中的概念词进行替换,直到句子中没有概念词为止。
-
举个例子,从
复合句子
开始。它对应了两种可能句子 连词 复合句子
或者句子
。用这两种任意选择一种进行替换。简单起见,选择后者。那么句子
替换为主语s 谓语 宾语s
。 -
其中
主语s
可以随机替换为主语 和 主语
或者主语
。后面的谓语
和宾语s
,同样通过查询,就可以将整句替换为主语 谓语 宾语
(这里随机替换了一种最简单的,方便讲解)。 -
主语 谓语 宾语
–》冠词 定语 代号 喜欢 冠词 定语 代号
–》这个 漂亮的 名词 喜欢 一个 神秘的 名词
–》这个 漂亮的 小猫 喜欢 一个 神秘的 苹果
-
def gene(target, grammar):
if target not in grammar: return target
expand = random.choice(grammar[target])
return ''.join([gene(e, grammar) for e in expand.split()])
-
调用运行
gene('复合句子', parse_grammar(grammar_rule))
总结延伸
句子生成算法,通过一个给定的语法结构、语料单词,来生成一句完整的句子,是一个很好的上手锻炼代码能力的小算法。
熟练了之后,还可以考虑自己写一个数学表达式的语法规则。