从语法中生成句子的常用方法是什么?
我想要一种与解析器相反的算法。也就是说,给定一个正式的无上下文语法(比如LL),我想生成一个符合该语法的任意句子。我在这里使用句子来表示任何有效的文本正文,所以它实际上可以是一个完整的程序(即使它没有任何意义 - 只要它的语法正确)。
示例语法:
program : <imports> NEWLINE? <namespace>
imports : ("import" <identifier> NEWLINE)*
namespace : "namespace " <identifier> NEWLINE "{" <classes> "}"
identifier: (A-Za-z_) (A-Za-z0-9_)*
...
示例生成的程序:
import jkhbhhuob
import aaaaa888_
namespace u8nFGubgykb
{ class ui0op_np { ... }
}
答案 0 :(得分:14)
以下是使用NLTK:
的Python示例from nltk import parse_cfg, ChartParser
from random import choice
def produce(grammar, symbol):
words = []
productions = grammar.productions(lhs = symbol)
production = choice(productions)
for sym in production.rhs():
if isinstance(sym, str):
words.append(sym)
else:
words.extend(produce(grammar, sym))
return words
grammar = parse_cfg('''
S -> NP VP
PP -> P NP
NP -> Det N | Det N PP | 'I'
VP -> V NP | VP PP
V -> 'shot' | 'killed' | 'wounded'
Det -> 'an' | 'my'
N -> 'elephant' | 'pajamas' | 'cat' | 'dog'
P -> 'in' | 'outside'
''')
parser = ChartParser(grammar)
gr = parser.grammar()
print ' '.join(produce(gr, gr.start()))
该示例改编自book。生成的句子在语法上是正确的,但仍然是完全胡言乱语。
答案 1 :(得分:3)
我不知道有一个“通用”算法可以做到这一点。随机程序生成用于遗传编程,因此您可以查找基于语法的GP系统,并了解它们如何处理程序生成。我会做一个递归规则生成算法,如伪代码:
void GenerateRule(someRule)
{
foreach (part in someRule.Parts)
{
if (part.IsLiteral) OutputLiteral(part);
if (part.IsIdentifier) Output(GenerateIdentifier(part)));
if (part.IsRule) GenerateRule(part.Rule);
}
}
这假设您已将所有部分读入某些数据结构。您还需要处理重复(随机生成它们发生的次数)和可选规则(翻转硬币以查看它们是否存在)。
编辑:哦,如果规则有多个选项,您只需选择其中一个选项,然后以相同的方式处理它。因此,如果某些规则是(Literal | Variable),您可以在两者之间随机选择。
答案 2 :(得分:2)
脱离我的头顶:
我会递归地工作(基本上与递归的正确解析器相反)使用一些启发式关于如何处理范围((...)
:可能随机选择)选项(?
:见[< em>],重复(''泊松分布?)。文字("..."
)简单地写入输出,而子句(`&lt; ...&gt;')生成递归。
除非你想保证某种完整的保险,否则这不应该太难。即使这样,只需生成束数据也是一种帮助......
[*]在处理
等规则时,您需要包含少于50%的时间选项以防止无限回归 nonterm: otherstuff <nonterm>?
plinth的好抓。
同样重复,抛出强烈收敛的分布。
如果输入语法以BNF格式显示,则需要首先解析输入语法。最简单的事情是使用映射(name, string)
,然后从最高级别的令牌开始(你可能认为这意味着第一个......)。
这会给你:
(“program”,“&lt; imports&gt; NEWLINE?&lt; namespace&gt;”)
(“进口”,(“导入”&lt;标识符&gt; NEWLINE)*)
...
你从“程序”开始,点击“&lt; imports&gt;”所以你重复...回来时,请“NEWLINE?”,所以掷骰子然后写或不写,点击“&lt; namespace&gt;”所以重复......回来后你就完成了。
我发现自己怀疑以前已经做过这件事。如果您只需要输出,我会在网上搜索...也许是http://portal.acm.org/citation.cfm?doid=966137.966142,尽管那里有大量的解析器生成器会使搜索空间变得混乱......请尝试this paper。 / p>
BTW--你当地的大学可能已经在线订阅这些期刊,所以你可以通过连接图书馆免费获得它们。
答案 3 :(得分:2)
您的解决方案应遵循语法的归纳结构。你如何为以下每一个产生一个随机的话语?
如果您记下用于表示语法的数据结构,这将更加清晰。您的相互递归生成器函数集的结构将非常接近地反映该数据结构。
处理无限递归有点冒险。最简单的方法是生成话语流并保持深度截止。或者,如果您使用像Haskell这样的惰性语言,您可以生成所有话语,并根据需要剥离尽可能多的有限语言(比原始问题更棘手,但非常有趣)。
答案 4 :(得分:1)
我的第一个建议是广泛的第一次搜索。只需设置规则图并搜索它们即可。你将从最小的程序开始吐出程序,然后逐渐变大。但是,您可能会发现,您的语法会针对给定数量的规则吐出指数级更多的程序,并且您在使用DFS的程序中可能不会超过30个左右的标记。
深度优先搜索的问题在于,第二个你有一个左递归规则,你的搜索将陷入无限循环。
另一个大问题是语法正确的程序距离语义正确的程序还有很长的路要走。除了最基本的情况之外,生成后一种类型可能完全不可行。
答案 5 :(得分:1)
您将遇到的问题是图的递归性质使您可以生成无限大小的正确语法。您可能希望做一些事情,例如在语法中设置节点类型的哈希值,以及允许自己命中该节点的次数的数量和限制。然后深入探索心脏的内容。
答案 6 :(得分:1)
“回想起来,应该使用现成的表达式生成器来生成随机ARM汇编指令以进行比较。而是以增量方式构建Perl脚本,获取每个ARM指令定义并生成实例。然而,增量内部方法的一个优点是简单的替换可以检测到简单的错误,并且可以逐步进行错误搜索。“
我担心我不记得是什么让我改变了主意,我怀疑它会与你的具体需求相关,但我建议你更加努力寻找一种先前存在的解决方案。要自己写这样的东西需要较少的纪律,但它总是需要比你预期的更长的时间。
答案 7 :(得分:-1)
不是答案,但请查看维基百科上的语法生成条目: http://en.wikipedia.org/wiki/Context-free_grammar_generation_algorithms
它描述了一些常用的算法。
答案 8 :(得分:-1)
虽然这个想法很好(我之前已经多次考虑过),但实际情况是,如果没有一些样本数据和/或大量的发电机约束/努力限制,这是一项相当大的工作。
人们可能会发现手工编写样本更容易。 :)