问题背景
给定一组特定的语法规则、语料单词,而后依此,不断替换所有概念词,直到生成一句具体的句子。
概念词包括:句子、主语、主语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))
总结延伸
句子生成算法,通过一个给定的语法结构、语料单词,来生成一句完整的句子,是一个很好的上手锻炼代码能力的小算法。
熟练了之后,还可以考虑自己写一个数学表达式的语法规则。