【问题标题】:Elegant command-parsing in an OOP-based text game基于 OOP 的文本游戏中的优雅命令解析
【发布时间】:2011-01-13 12:16:05
【问题描述】:

我正在玩用 Ruby 编写 MUD/文本冒险(请不要笑)。谁能给我任何关于解析输入文本的优雅、基于 oop 的解决方案的指示?

我们在这里谈论的只是“把魔杖放在桌子上”更复杂的事情。但一切都需要柔软;我想稍后轻松地扩展命令集。

我目前的想法,稍微简化一下:

  1. 每个项目类别(盒子、桌子、房间、播放器)都知道如何识别“属于”它的命令。

  2. 游戏类理解一种特定于领域的语言,涉及诸如“将对象 X 移入对象 Y”、“显示对象 X 的描述”等动作。

  3. 游戏类询问房间中的每个项目是否识别输入命令。先说是就赢了。

  4. 然后它将控制权传递给项目类中处理命令的方法。此方法重新表述 DSL 中的命令,将其传递回游戏对象以使其发生。

必须有陈旧、优雅的方式来做这些事情。不过,谷歌似乎无法搜索任何内容。

【问题讨论】:

  • 作为一个脚注,我当前计划的毛病是实现涉及两个对象的操作。
  • 您可以从 stackoverflow.com/questions/tagged/natural-language 开始,然后看看它会导致什么。
  • 塔!我刚刚将该标签添加到问题中。
  • 我的第一次打勾! :) 我将同时研究有限状态机和解释器模式。在我看来,后者非常符合我目前的想法。但魔鬼会在细节中,也就是解析。我有很多想法。谢谢大家。

标签: ruby language-agnostic oop nlp


【解决方案1】:

Interpreter design pattern 是我所知道的最面向对象的解析方法,但我相信编译器专家会指出更强大的算法。

【讨论】:

  • 我对设计模式一无所知,我的谷歌搜索没有看到这个。我一定会看看这个,ta。
  • 确实,解释器模式看起来就像是编译过程中的表达式树!好的。对于编译器来说,这似乎是一个简单的实现。 (字符串token解析后)
  • 我仍在努力将这种模式应用到我的项目中,但它看起来好像是我正在摸索的想法。然而,它没有说明的是如何解析命令字符串。
【解决方案2】:

将其拆分为标记,因为格式始终为:
[命令] [object1] ([引用] [object2])

您可以在房间中的 [object1] 上调用方法 [command] 并将其传递给 [object2](如果适用)。

【讨论】:

  • 这太诱人了。但是,“打开灯”。或者,就此而言,能够接受“开灯”真是太好了。
  • 您可以通过简单地从字符串中删除通常称为“停用词”的内容来做到这一点。诸如“the”、“an”、“on”之类的词。在这种情况下,“现在打开那里的灯”这句话剩下的就是“打开灯”。当然,这可能太多,但在这种特定情况下,“off”这个词的缺席会告诉你你需要知道的一切。
  • @Jorg:实际上,这几乎就是我目前的方法所做的,只是它检测的是“起始词”。所以(为了简化一点)灯光类寻找 /light.*(on|off)/。也许你有正确的方法,但我没有。
  • “停用词”的概念来自搜索引擎,这些词根本不会提供任何有用的信息。因此,它们通常会从索引和搜索查询中过滤掉。它肯定必须在这里进行调整。你可以看看 Git 版本控制系统中的自然语言日期解析器。 看起来它可以解析非常复杂的表达式,例如“大约一个小时前”,但它真正所做的只是简单地丢弃任何它不理解的东西,所以它只是以“小时”结束被简单地定义为3600 就是这样。效果很好。
【解决方案3】:

对于命令解释器,我比较喜欢这种简单而不是优雅的模式。动态语言中的模式往往比 GOF 模式包含更少的框和线。

class Thing

  # Handle a command by calling the method "cmd_" + command.
  # Raise BadCommand exception if there is no method for that command.

  def handle_command(command, args)
    method_name = "cmd_#{command}"
    raise BadCommand, command unless respond_to?(method_name)
    send(method_name, args)
  end

  def cmd_quit(args)
    # the code for command "quit"
  end

  def cmd_list(args)
    # the code for command "list"
  end

  ...

end

这样,添加一个新的命令就是添加一个新的方法。无需调整表格或案例语句。

【讨论】:

  • 这看起来像是访客、战略和指挥之间的怪异三路,但就像你说的:在一种表达语言中(我不买“动态”部分 很多,它在 Scala、F# 或 Haskell 中可能看起来同样优雅​​)这没什么大不了的。请记住:模式只不过是您希望编写的程序,但您的语言不允许您编写。好吧,Ruby确实让你。
  • @Jorg,说得好。我挖掘了模式的定义。是你的吗?
  • 我不这么认为。那个精确的措辞可能是我的,但这个想法是普遍的。事实上,如果您将 GoF 副本翻到第 4 页并阅读第 1.1 节的最后一段,它实际上是在那里拼写出来的。对此的经典咆哮是blog.plover.com/prog/design-patterns.html,回复cincomsmalltalk.com/userblogs/ralph/blogView?entry=3335803396,然后是blog.plover.com/prog/johnson.html。当然还有著名的 Norvig 演讲:norvig.com/design-patterns
  • @Jog,这是一个巨大链接的聚宝盆。谢谢!
【解决方案4】:

好的。所以你需要一个语义? (转是一个动作,点亮一个物体,在一个论点上......(我与你对 dbemerlin 的评论有关)。

为什么不定义语法?嗯...我猜 lex 和 yacc 不是一个选项? (因为它根本不是 OOP,但你想要做的是“编译”用户输入以产生一些东西 - 执行一些更改房间数据的代码,并输出结果)

您可以为您的对象及其操作进行 OOP 设计(例如,所有对象都有一个 .describeMe() 方法..),除此之外,还有一个输入解析器+编译器。

我是不是完全跑题了?

编辑:在查看了 Marc Seeman 指出的解释器模式之后,这似乎是您在 OOP 中想要的方式。 (但你会在某种程度上重新发明轮子)

【讨论】:

  • 我确实看过 lex 和 yacc。但他们让我头疼,这应该是一个有趣的项目。另外,如果我理解正确,他们会将我与比英语实际语法严格得多的语法联系起来。我想我的黄金目标是让用户能够输入“放在桌子上放魔杖”并被理解......或者至少让游戏尝试将桌子放在魔杖上;)
【解决方案5】:

听起来你需要一个解析器。

将输入字符串拆分为标记(单词)。然后将令牌一次一个地馈送到状态机。我发现下推自动机是编写这样一个 stm 的相当直观和强大的方式。

【讨论】:

  • 关于我如何开始研究状态机概念的任何建议?当然,维基百科条目是给定的。
  • 我收回维基百科的评论。下推自动机的页面看起来很吸引人,但它是用高等数学编写的。
  • @Shadow: stackoverflow.com/questions/1669/learning-to-write-a-compiler 是 SO 上编译器资源的规范页面。但你需要的是专业的,可能不会得到很好的对待。这个答案在技术上是正确的,但不是很有帮助,因为自然语言处理是出了名的困难,并且有自己的文化和语言。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 2012-07-27
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
相关资源
最近更新 更多