10

对我来说,Interpreter 模式听起来很像被称为 Greenspun 第十条规则的反模式:

任何足够复杂的 C 或 Fortran 程序都包含一个临时的、非正式指定的、充满错误的、缓慢的 Common Lisp 一半的实现。

也就是说,如果您需要使用解释器,您可能会创建一些缓慢、临时且指定不明确的东西。正确的解决方案是从一开始就使用正确的语言。

或者,也可以将一种众所周知的、明确指定的语言嵌入到您的应用程序中,例如 Guile(GNU 可嵌入方案)。或者使用 Haskell 作为嵌入式领域特定语言。

但我在实践中还没有看到这一点——您在构建自己的嵌入式语言方面有什么经验?这是个好主意吗?它比嵌入已经存在的语言更好吗?

(我不是特别喜欢 lisp。这很好,但 C、Haskell 和 python 以及许多其他语言也是如此。)

4

10 回答 10

31

解释器模式中没有任何内容表明它必须是您正在解释的另一种编程语言的语法。如果您需要解析一个简单的数学表达式,那么解释器就是这样。

知道何时使用模式可以防止它成为反模式。

于 2009-06-20T00:45:40.013 回答
14

如果被滥用,任何设计模式都是反模式。

解释器模式的良好用途:

  • 软件编译器
  • SQL 评估引擎
  • 图形计算器输入解析器
  • XML 解析器

这些都是解决评估语言中单词问题的程序,无论该语言是什么

于 2009-06-20T01:09:10.450 回答
8

请记住,“解释器模式”是 OOP 中的一种特定设计模式。

它与“解释器”或它们的一般用途无关。

于 2009-06-20T08:07:53.543 回答
3

在某些时候,一个项目可能需要一个配置系统。一开始通常只需要一些简单的东西,比如键值对,以便系统可以连接到本地数据库,或类似的东西。如果这就是所需要的,通常只需为 INI 或 CSV 的方言编写一个简单的解析器并继续前进。但是随着系统的发展和成熟,配置更加重要。这是正常且健康的,将功能和责任重构到正确的抽象层。从这里开始,开发人员甚至(喘不过气的)用户很快就会想要一种更具表现力的配置语言。甚至在任何人都没有注意到之前,该系统已经内置并使用了图灵完备的语言。

这是 Greenspunning 的基本模式。一切都经过精心设计,直到最后一点,一种临时语言被推入了真正的计算工作领域。

任何大小合适的系统都应该包含至少一半的 clisp。

提前知道是一大步。编写大型系统的一种非常方便的方法是选择一种很好的、​​易于破解的解释性语言并用它来编写你的系统。这恰好正是 TCL 旨在做的事情,但如今很难让任何人支持该语言的大型项目。另一方面,现在有许多其他语言可以提供所有这些好处。

这样做的好处是活动文件模式。简单配置的编写方式与系统可用的语言解析器兼容。由于文件是由语言解析的,因此可以轻松嵌入更复杂的逻辑。

django 的 settings.py 就是一个例子。出于某种原因,django 在猜测 django 项目的安装位置方面并不是很聪明。在配置文件中使用少量标准 python,这可以在一般情况下以可移植的方式处理,这将适合几乎所有可能的用户。尽管如此,大部分的 settings.py 文件看起来像 .ini 样式配置文件中典型的普通旧键 = 值对。

与解释器模式的关系是,这些成熟的语言可能会为某些病态用途获得正确的模式。一旦你知道你需要解析一些东西,就想出一个很好的理由不使用现有的语言。

于 2009-06-20T01:56:41.993 回答
2

解释器模式并不意味着编写一个完整的通用脚本语言。如果您需要,显然使用人们已经写过好书的知名语言会更有意义(或者最重要的是,允许用户选择语言)。

解释器模式更多地是关于持久化对象图的想法,但选择人类可读和可编辑的持久化格式(例如用指向几个对象的指针2 + 3表示一个对象)。即使这从未作为一种“语言”暴露给客户,它仍然有助于调试、支持、现场黑客攻击等。其他常见的例子是查询语言,例如 [N]Hibernate 中的 HQL - 没有任何现有的语言可以很好地描述该抽象级别的查询。AdditionInteger

正如其他人所建议的那样,XML 通常用作此的基本语法(尤其是在将持久对象图设想为“配置文件”时,另请参见 Microsoft 的 XAML),但它实际上是一个次优选择。UTF-8(或类似)中的 JSON 会更干净、更容易解析、更具可读性和可编辑性,因此对于大多数应用程序来说可能更好。但是有一些很棒的轻量级解析器库,它们采用类似 BNF 的语法描述,然后可以将文本解析成类似 DOM 的可步行结构,所有这些都在运行时进行,因此编写高度特定的简洁语法是没有问题的。

于 2009-06-20T08:46:31.063 回答
1

你显然不是 Lisp 的“狂热者”,因为符合该描述的男孩和女孩通常可以知道 Lisp 是一种编译语言。Lisp 出现于 1958 年。1961 年的 Lisp 1 手册已经描述了编译。

口译很有用;它为我们提供了语义,而无需先编写编译器。这种语义为编译语义提供了一个参考模型:理想情况下,解释程序和编译程序做同样的事情。当我们发现他们不这样做时,我们要么解决它,要么以某种方式描述和证明这种情况。

解释可用于引导编译的 Lisp 系统,而不需要现有的 Lisp 实现,而是使用另一种语言,例如 C。解释避免了用引导语言编写 Lisp 编译器的需要。

ANSI Common Lisp 的 CLISP 实现就是一个很好的例子。要构建 CLISP,您只需要一个 C 编译器。CLISP 的 Lisp 编译器是用 Lisp 编写的。因此,当然,您没有任何东西可以运行该编译器。解决方案是使用用 C 编写的解释器来解释编译器。

在运行解释时,编译器编译大部分 Lisp 库。其结果就是 CLISP 所称的“半编译内存映像”:包含已编译例程但一些解释例程的映像。(我认为,编译器本身仍然是解释代码)。

然后使用这个半编译的映像来编译剩余的代码,从而得到完全编译的映像。

如果没有解释器,CLISP 的编译器只能通过坚持首先安装另一个 Lisp 实现来引导。(您需要 C 编译器来引导 GCC 的方式)。否则,CLISP 的编译器必须用 C 语言编写,以便使用现有的 C 编译器编译编译器,然后将其应用于 Lisp 代码进行编译,然后才能运行。

头脑正常的人都不想用 C 语言编写 Lisp 编译器,并且在 Lisp 并不普遍的世界中,需要 Lisp 实现来构建 Lisp 实现是一个很大的缺点。在 Lisp-compiler-in-C 助推模型下会发生什么,用 C 语言编写的 Lisp 编译器将是一个完全最小且可能不完整的编译器,它会发出质量差的代码,仅足以启动助推,并且是“真正的”编译器仍然是用 Lisp 编写的。

于 2016-06-22T16:28:11.477 回答
0

也许 Lisp 编译器的实现包括解释器模式的示例。

例如,我认为您不应该说“轮子”是一种反模式,即使您通常应该购买现成的轮子而不是重新发明它们。

于 2009-06-20T00:48:14.207 回答
0

解释器是 JavaCC 解析器生成器背后的想法。我认为它工作正常。

Interpreter 是一种比 Singleton 更受尊敬的 GoF 模式。那是需要在岛上投票的人。也许纪念品也是如此。

于 2009-06-20T00:53:46.503 回答
0

发明 XML 的原因之一是让我们免于编写所有 EDI 解释器。但至少范围是明确定义的,我们所有人都大致平等地(至少足够)有效地做到了。至少我仍然有足够的错觉,认为这是正确的做法。

听起来你正在尝试开始一个新的都市传奇?你天生就反对领域特定语言吗?

于 2009-06-20T00:57:20.973 回答
-1

通常编译器/解释器的阶段如下所示:

  1. 句法
  2. 解析树
  3. AST
  4. 编译或解释

在 Lisp 中,1 和 2 合并为 3。

因此,很明显,复杂的程序中可能嵌入了一种自定义语言,它是“一种临时的、非正式指定的、充满错误的、对一半 Common Lisp 的缓慢实现”。

然而,我不得不说,手写 AST 树是令人不快的,并且不是所有语言的终结,无论有多少 Lispers 声称它是。

于 2009-06-20T01:31:25.040 回答