【问题标题】:How do I generate sentences from a formal grammar?如何从形式语法生成句子?
【发布时间】:2010-10-10 20:40:12
【问题描述】:

从语法生成句子的常用方法是什么?

我想要一种与解析器相反的算法。也就是说,给定一个正式的上下文无关语法(比如 LL),我想生成一个符合该语法的任意句子。我在这里使用 sentence 来表示任何有效的文本主体,因此它实际上可以是一个完整的程序(即使它没有任何意义——只要它在语法上是正确的)。

语法示例:

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 { ... }
}

【问题讨论】:

标签: compiler-construction computer-science grammar parsing


【解决方案1】:

这是一个使用 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。生成的句子在语法上是正确的,但仍然完全是胡言乱语。

【讨论】:

  • 对于最新版本的 nltk,使用import CFG 代替import parse_cfg,然后使用CFG.fromstring(...) 代替parse_cfg(...)
  • 需要添加 elifs 来解释所有可能的“叶子”,因为我们不能期望它们都是文字。但我假设 python 中有库可以生成符合给定正则表达式的随机字符串。我不知道 nltk 还提供什么,比如?或 * 但您希望每个都有一个 elif。
【解决方案2】:

我不知道有一个“通用”算法可以做到这一点。随机程序生成用于遗传编程,因此您可以寻找基于语法的 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),您会在两者之间随机选择。

【讨论】:

    【解决方案3】:

    您的解决方案应遵循语法的归纳结构。您如何为以下每个生成随机话语?

    • 终端符号
    • 非终结符
    • 右手边的序列
    • 选择右侧
    • 右侧星形闭合

    如果你写下你用来表示语法的数据结构,这一切都会更清楚。您的一组相互递归生成器函数的结构将非常接近地反映该数据结构。

    处理无限递归有点冒险。最简单的方法是生成话语流并保持深度截止。或者,如果您使用像 Haskell 这样的惰性语言,您可以生成 所有 话语,并根据需要剥离尽可能多的有限话语(一个比原始问题更棘手的问题,但非常有趣)。

    【讨论】:

      【解决方案4】:

      在我的头顶:

      我会使用一些关于如何处理范围((...):可能随机选择)选项(?:见 [],下面),重复(''泊松分布?)。文字 ("...") 被简单地写入输出,而子标记 (`<...>') 生成递归。

      除非您想保证某种完整的覆盖范围,否则这应该不会太难。即便如此,仅生成 一堆 数据也会有所帮助...


      [*] 在处理诸如此类的规则时,您需要在少于 50% 的时间内包含选项,以防止无限回归

       nonterm:  otherstuff <nonterm>?
      

      plinth 的好消息。

      与重复一样,抛出一个强收敛的分布。


      如果输入文法以 BNF 形式呈现,则需要先解析输入文法,如此处所示。最简单的做法是使用映射(name, string),然后从最高级别的令牌开始(您可能认为这意味着第一个...)。

      这给了你:

      ("program", " NEWLINE?")

      ("imports", ("import" NEWLINE)*)

      ...

      你从“程序”开始,点击“”,所以你会重复......回来时,点击“NEWLINE?”,所以掷骰子写与否,点击“”所以重复...返回时你就完成了。


      我发现自己怀疑以前有人这样做过。如果您只需要输出,我会搜索网络...也许http://portal.acm.org/citation.cfm?doid=966137.966142,尽管那里的大量解析器生成器使搜索空间变得混乱...尝试this paper

      顺便说一句——你们当地的大学可能有这些期刊的在线订阅,所以你可以通过图书馆免费获得它们。

      【讨论】:

        【解决方案5】:

        您将遇到的问题是图形的递归性质使得您可以生成无限大小的正确语法。您可能想要做一些事情,例如在您的语法中设置节点类型的哈希,其中包含您允许自己点击该节点的次数和限制。然后深度优先搜索到你心中的内容。

        【讨论】:

        • 是的。这就是为什么我建议从收敛函数中提取重复次数。这也是为什么我最初对选项的建议(50/50)是错误的。现在得去解决这个问题......
        【解决方案6】:

        我的第一个建议是广度优先搜索。只需设置规则图并搜索它们。您将从最小的程序开始吐出程序,然后慢慢变大。但是,您可能会发现,对于给定数量的规则,您的语法会以指数方式吐出更多的程序,并且您可能不会在使用 DFS 的程序中获得超过 30 个左右的标记。

        深度优先搜索的问题在于,一旦你有了左递归规则,你的搜索就会陷入无限循环。

        另一个大问题是语法正确的程序距离语义正确的程序还有很长的路要走。除了最基本的情况外,生成后一种类型可能完全不可行。

        【讨论】:

          【解决方案7】:

          像往常一样,我建议不要重新发明轮子。我已经为 ARM 汇编器编写了其中的一个,但我对此表示遗憾(软件:实践和经验 2007 年 4 月):

          “回想起来,应该使用现成的表达式生成器来生成随机 ARM 汇编指令以进行比较。取而代之的是,逐步构建 Perl 脚本,获取每个 ARM 指令定义并生成实例。然而,增量内部方法的一个优势是简单的替换可以检测到简单的错误,并且可以逐步进行错误搜寻。”

          恐怕我不记得是什么让我改变了主意,而且我怀疑它是否与您的特定需求相关,但我确实建议您更加努力地寻找预先存在的解决方案。自己写这样的东西需要更少的纪律,但它总是比你预期的要花更长的时间。

          【讨论】:

            【解决方案8】:

            不是答案,但请查看有关语法生成的维基百科条目: http://en.wikipedia.org/wiki/Context-free_grammar_generation_algorithms

            它描述了一些常用的算法。

            【讨论】:

            • 这不是他想要做的事情
            • Beardo 是对的——我想反过来,即从语法中生成一串文本。
            【解决方案9】:

            虽然这个想法很好(我之前曾多次想过),但现实情况是,如果没有一些样本数据和/或大量生成器约束/努力限制,这是一项艰巨的工作。

            人们可能会发现手工编写样本更容易。 :)

            【讨论】:

            • 对于许多应用程序,手工生成的序列和语法生成的序列的组合达到了最佳效果。我想到的一个应用是描述任何一种状态机,然后生成一系列状态转换。
            猜你喜欢
            • 2012-01-18
            • 2013-02-07
            • 2023-03-15
            • 2016-10-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-08-16
            相关资源
            最近更新 更多