我知道如果存在2个或更多的左或右派生树,则该语法是模棱两可的,但是我无法理解为什么它如此糟糕以至于每个人都希望摆脱它。
std::vector<std::vector<int>>
在2011年故意添加了歧义,该歧义>>
以前需要在两者之间留一个空格。关键的见解是,与供应商相比,这些语言具有更多的用户,因此,解决用户的小麻烦可以使实现者进行大量工作。
我知道如果存在2个或更多的左或右派生树,则该语法是模棱两可的,但是我无法理解为什么它如此糟糕以至于每个人都希望摆脱它。
std::vector<std::vector<int>>
在2011年故意添加了歧义,该歧义>>
以前需要在两者之间留一个空格。关键的见解是,与供应商相比,这些语言具有更多的用户,因此,解决用户的小麻烦可以使实现者进行大量工作。
Answers:
考虑下面的文法算术表达式:
编译程序时,我们希望语法的解释是明确的。实施此操作的最简单方法是使用明确的语法。如果语法不明确,我们可以提供打破平局的规则,例如运算符优先级和关联性。这些规则可以通过以特定方式使语法明确表示出来。
+
)。
与此相反的其他现有答案[ 1,2 ],的确应用的领域中,其中,不明确的文法有用。在自然语言处理(NLP)领域中,当您想用形式语法分析自然语言(NL)时,您会遇到一个问题,即NL本质上在不同层次上是模棱两可的[改编自Koh18,ch。6.4]:
句法歧义:
彼得追着那辆红色跑车的人
是彼得还是红色跑车上的男人?
语义歧义:
彼得去了银行
要坐的银行还是要取钱的银行?
务实的责难感:
两个男人提着两个袋子
他们是将袋子一起携带还是每个人都携带两个袋子?
一般而言,针对NLP的不同方法处理方式有所不同,尤其是这些歧义性。例如,您的管道可能如下所示:
您为每个句子执行此管道。例如,从您处理的同一本书中获得的文字越多,您就越能排除之前的句子中不可能的多余模型,这些模型可以保留到第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章“歧义的计算作用”
以C ++中最令人头疼的解析为例:
bar foo(foobar());
这是foo
类型的函数声明bar(foobar())
(参数是返回的函数指针foobar
),还是foo
类型的变量声明int
并使用默认初始化进行初始化foobar
?
除非假定参数列表内的表达式不能解释为类型,否则在编译器中通过假定第一个来区分。
当您得到这样一个含糊的表达式时,编译器有2个选项
假设表达式是一个特定的派生词,并在语法中添加一些歧义词以表示其他派生词。
错误并要求消除歧义
第一个自然而然地消失了,第二个要求编译器程序员知道歧义。
如果不确定性未被发现,则可能有两个不同的编译器默认为该歧义表达式使用不同的派生。由于非显而易见的原因,导致代码不可移植。这导致人们认为这是其中一个编译器中的错误,而实际上这是语言规范中的错误。
我认为这个问题所包含的假设充其量只能是正确的边界。
在现实生活中,仅使用歧义语法是很常见的,只要它们不是(可以这么说)歧义即可。
例如,如果您查看使用yacc(或类似的词,例如bison或byacc)编译的语法,则在编译它们时会发现很多警告信息提示“ N移位/减少冲突”。当yacc遇到移位/归约冲突时,表示语法有歧义。
但是,移位/减少冲突通常是一个相当小的问题。解析器生成器将解决冲突,而采用“ shift”而不是reduce。如果您想要的话,语法就很好了(在实践中,它似乎做得很好)。
通常在这种一般顺序的情况下发生移位/减少冲突(对非终端使用大写,对终端使用小写):
A -> B | c
B -> a | c
当我们遇到a时c
,会有一个歧义:我们应该c
直接将an 解析为an A
还是将其解析为a B
,而后者又是a A
?在这种情况下,yacc等将选择更简单/更短的路线,并将解析c
为A
,而不是c
-> B
-> A
路线。这可能是错误的,但如果是这样,则可能意味着您的语法中确实存在一个非常简单的错误,您根本不应该允许该c
选项A
。
现在,相比之下,我们可以拥有更多这样的东西:
A -> B | C
B -> a | c
C -> b | c
现在,当我们遇到a时c
,是否将c
a B
或a 视为冲突C
。自动解决冲突策略选择我们真正想要的东西的机会要少得多。这些都不是“转移”,都是“减少”,因此这是“减少/减少冲突”(习惯于使用yacc的人通常认为比转移/减少冲突要大得多)。
因此,尽管我不确定我是否能说出任何人真的欢迎他们的语法模棱两可,但至少在某些情况下,它很小,没有人真正在乎它。概括地说,他们可能喜欢消除所有歧义的想法-但不足以始终实际执行。例如,包含较小歧义的小而简单的语法可能比消除歧义的较大,更复杂的语法更可取(特别是当您进入实际从语法生成解析器并发现无歧义的实际领域时,语法会生成无法在目标计算机上运行的解析器)。