为什么模棱两可的语法不好?


30

我知道如果存在2个或更多的左或右派生树,则该语法是模棱两可的,但是我无法理解为什么它如此糟糕以至于每个人都希望摆脱它。


1
相关但不完全相同:softwareengineering.stackexchange.com/q/343872/206652(免责声明:我写下了接受的答案)
marstato

另请参阅:“ 找到明确的语法 ”。
Rob

1
实际上,明确的形式更适合于实际使用,明确的形式使用较少的生产规则,从而以较高的比率构建较小的树(因此,高效的编译器需要较少的解析时间)。大多数工具都会在语法外提供明确解决歧义的能力。
Grijesh Chauhan

3
“每个人都想摆脱它”。好吧,那不是真的。在与商业相关的语言中,随着语言的发展,通常会增加歧义。例如,C ++ std::vector<std::vector<int>>在2011年故意添加了歧义,该歧义>>以前需要在两者之间留一个空格。关键的见解是,与供应商相比,这些语言具有更多的用户,因此,解决用户的小麻烦可以使实现者进行大量工作。
MSalters

Answers:


52

考虑下面的文法算术表达式:

XX+XXXXXX/Xvarconst
考虑下面的表达式:
abc
它有什么价值?这是两个可能的解析树:

(X-X)-X 在此处输入图片说明

abc(ab)ca(bc)=ab+c

编译程序时,我们希望语法的解释是明确的。实施此操作的最简单方法是使用明确的语法。如果语法不明确,我们可以提供打破平局的规则,例如运算符优先级和关联性。这些规则可以通过以特定方式使语法明确表示出来。


解析使用语法树生成器生成的


12
@HIRAKMONDAL语法不明确的事实不是真正的问题。问题是两个不同的分析树具有不同的行为。如果您的语言语法不明确,但表达式的所有解析树在语义上都是等效的,那么这将不是问题(例如,以Yuval为例,并考虑唯一操作符的情况+)。
巴库里

14
@Bakuriu您所说的是正确的,但“在语义上等效”是一个很高的要求。例如,浮点算术实际上不是关联的(因此,两个“ +”树将是不等效的)。此外,即使答案以相同的方式出现,未定义的求值顺序在表达式可能具有副作用的语言中也很重要。因此,您所说的内容在技术上是正确的,但实际上,语法的歧义对使用该语法没有影响是非常不寻常的。
理查德·拉斯特

如今,某些语言会额外检查整数溢出,因此,甚至a + b + c的整数也取决于求值顺序。
gnasher729

3
更糟糕的是,在某些情况下,语法没有提供任何方式来实现替代含义。我已经在查询语言中看到了这一点,在这种情况下,对转义语法的选择(例如,对特殊字符加倍以对其进行转义)使得某些查询无法表达。
停止危害莫妮卡

12

与此相反的其他现有答案[ 12 ],的确应用的领域中,其中,不明确的文法有用。在自然语言处理(NLP)领域中,当您想用形式语法分析自然语言(NL)时,您会遇到一个问题,即NL本质上在不同层次上是模棱两可的[改编自Koh18,ch。6.4]:

  • 句法歧义:

    彼得追着那辆红色跑车的人

    是彼得还是红色跑车上的男人?

  • 语义歧义:

    彼得去了银行

    要坐的银行还是要取钱的银行?

  • 务实的责难感:

    两个男人提着两个袋子

    他们是将袋子一起携带还是每个人都携带两个袋子?

一般而言,针对NLP的不同方法处理方式有所不同,尤其是这些歧义性。例如,您的管道可能如下所示:

  1. 用歧义语法解析NL
  2. 对于每个产生的AST:运行模型生成以生成不明确的语义,并排除步骤1中可能出现的语法歧义
  3. 对于每个生成的模型:将其保存在缓存中。

您为每个句子执行此管道。例如,从您处理的同一本书中获得的文字越多,您就越能排除之前的句子中不可能的多余模型,这些模型可以保留到第3步。

与编程语言相反,我们可以放开每个NL句子具有精确语义的要求。相反,我们可以在较大文本的整个解析过程中保留多个可能的语义模型。有时,以后的见解会帮助我们排除以前的歧义。

如果您想让解析器能够输出歧义语法的多个派生词,请看一下Grammtical Framework。另外,[Koh18,ch。5]对其进行了介绍,显示了与上面我的管道类似的内容。但是请注意,由于[Koh18]是讲义,因此如果没有讲义,这些讲义可能不那么容易理解。


参考文献

[Koh18]:Michael Kohlhase。“基于逻辑的自然语言处理。2018/ 19冬季学期。讲义。” 网址:https : //kwarc.info/teaching/LBS/notes.pdf。课程说明的网址:https : //kwarc.info/courses/lbs/(德语)

[Koh18,ch。5]:请参见[Koh18]中的第5章,“实现片段:语法和逻辑框架”。

[Koh18,ch。6.4]参见[Koh18]中的第6.4章“歧义的计算作用”


谢谢一吨..我也有同样的疑问,你已经解决了.. :)
HIRAK MONDAL

1
更不用说布法罗水牛的问题 布法罗水牛的水牛...适合一定数量的水牛
哈根·冯·埃岑

您写道“相反”,但根据我的回答,我将其称为硬币的另一面。解析自然语言及其歧义语法非常困难,以至于传统的解析器无法做到这一点!
6

1
@ComFreek我在这里应该更精确一些。简要了解GF(感谢链接!)表明,它读取具有三个扩展名的上下文无关语法(例如允许重复),并返回所有可能派生的列表。自20世纪50年代以来,已经有很多算法可以做到这一点。但是,能够处理完全通用的CFG意味着最坏的运行时会崩溃,并且在实践中,即使使用通用解析器(例如GLL),软件工程师也会尝试使用CFG的子集(例如LL语法),被更有效地解析。
戴维斯洛

1
@ComFreek因此,并不是说计算机不能处理CFG(尽管自然语言并不是真正的上下文无关,并且实际上有用的机器翻译使用的是完全不同的技术)。就是说,如果您需要解析器处理歧义,那就排除了某些捷径,这些捷径会使它更加高效。
戴维斯洛

10

即使有明确定义的方式处理歧义(例如歧义表达式是语法错误),这些语法仍然会带来麻烦。一旦将歧义引入语法中,解析器就无法再确定它获得的第一个匹配是确定的。它需要继续尝试所有其他方式来解析语句,以排除任何歧义。您也不会处理诸如LL(1)语言这样的简单事物,因此您不能使用简单,小型,快速的解析器。语法中的符号可以多种方式读取,因此您必须做好很多回溯的准备。

在某些受限制的域中,您也许可以证明所有解析表达式的方法都是等效的(例如,因为它们表示关联操作)。(a + b)+ c = a +(b + c)。


9

确实IF a THEN IF b THEN x ELSE y意味着

IF a THEN
    IF b THEN
        x
    ELSE
        y

要么

IF a THEN
    IF b THEN x
ELSE
    y

?又叫悬而未决的问题


1
这是一个很好的例子,表明即使从人类的角度来看,即使是无歧义的语法(如Java,C,C ++等),也允许明显的(!)歧义。即使我们在形式上和计算上都还不错,但现在我们遇到了更多的UX /无bug开发问题。
ComFreek

5

以C ++中最令人头疼的解析为例:

bar foo(foobar());

这是foo类型的函数声明bar(foobar())(参数是返回的函数指针foobar),还是foo类型的变量声明int并使用默认初始化进行初始化foobar

除非假定参数列表内的表达式不能解释为类型,否则在编译器中通过假定第一个来区分。

当您得到这样一个含糊的表达式时,编译器有2个选项

  1. 假设表达式是一个特定的派生词,并在语法中添加一些歧义词以表示其他派生词。

  2. 错误并要求消除歧义

第一个自然而然地消失了,第二个要求编译器程序员知道歧义。

如果不确定性未被发现,则可能有两个不同的编译器默认为该歧义表达式使用不同的派生。由于非显而易见的原因,导致代码不可移植。这导致人们认为这是其中一个编译器中的错误,而实际上这是语言规范中的错误。


5

我认为这个问题所包含的假设充其量只能是正确的边界。

在现实生活中,仅使用歧义语法是很常见的,只要它们不是(可以这么说)歧义即可。

例如,如果您查看使用yacc(或类似的词,例如bison或byacc)编译的语法,则在编译它们时会发现很多警告信息提示“ N移位/减少冲突”。当yacc遇到移位/归约冲突时,表示语法有歧义。

但是,移位/减少冲突通常是一个相当小的问题。解析器生成器将解决冲突,而采用“ shift”而不是reduce。如果您想要的话,语法就很好了(在实践中,它似乎做得很好)。

通常在这种一般顺序的情况下发生移位/减少冲突(对非终端使用大写,对终端使用小写):

A -> B | c
B -> a | c

当我们遇到a时c,会有一个歧义:我们应该c直接将an 解析为an A还是将其解析为a B,而后者又是a A?在这种情况下,yacc等将选择更简单/更短的路线,并将解析cA,而不是c-> B-> A路线。这可能是错误的,但如果是这样,则可能意味着您的语法中确实存在一个非常简单的错误,您根本不应该允许该c选项A

现在,相比之下,我们可以拥有更多这样的东西:

A -> B | C
B -> a | c
C -> b | c

现在,当我们遇到a时c,是否将ca B或a 视为冲突C。自动解决冲突策略选择我们真正想要的东西的机会要少得多。这些都不是“转移”,都是“减少”,因此这是“减少/减少冲突”(习惯于使用yacc的人通常认为比转移/减少冲突要大得多)。

因此,尽管我不确定我是否能说出任何人真的欢迎他们的语法模棱两可,但至少在某些情况下,它很小,没有人真正在乎它。概括地说,他们可能喜欢消除所有歧义的想法-但不足以始终实际执行。例如,包含较小歧义的小而简单的语法可能比消除歧义的较大,更复杂的语法更可取(特别是当您进入实际从语法生成解析器并发现无歧义的实际领域时,语法会生成无法在目标计算机上运行的解析器)。


伙计,希望我在5个月前对转移减少冲突有一个很好的解释!^^; +1
HotelCalifornia
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.