正则表达式的真正威力(2)

上下文有关文法

接下来再看乔姆斯基谱系上一层:上下文有关语言。

上下文有关语言中的产生式都具有以下形式:

α A β → α γ β

这种混合字符初看很复杂,其实很简单。核心依然具有形式A → γ,这点跟上下文相关文法一致。不同的是两边多出了αβ。这两者就构成了上下文(文法的名称由此得来)。所以A可以被γ替代的条件是其左右也有αβ

为了清晰的说明这点,试着解析下面的规则:

a b A -> a b c
a B c -> a Q H c
H B -> H C

翻译出来就是:

只有左边有`a b`时,`A`才可以用`c`替换
只有左边是`a`右边是`c`时,`B`才可以用`Q H`替换
只有走边是`H`时,`B`才可以用`C`替换

上下文相关文法在“一般”编程中是很少遇到的。在自然语言处理中比较重要(因为自然语言显然不是上下文无关的。单词在不同的上下文中意义不同)。然而即便在自然语言处理中,人们也经常使用“温和的上下文相关语言”,因为可以有效建模语言又可以快速解析。

要理解上下文相关文法有多么强大,我们先看一下另一种文法类型,它和上下文相关文法有基本一样的表达能力:非收缩文法。

非收缩文法每一个产生式规则的形式都是α -> β,其中αβ都是任意的符号串,唯一的限制是:右边的符号长度不能小于左边的符号。

因此非收缩文法允许任意形式的规则只要不将输入变短。例如A B C -> H Q就不合法,左边有三个符号而右边只有两个。这样就算缩短了。如果将上面的规则反过来就合法了。

上下文相关文法和非收缩文法的等值关系使得你可以用上下文相关文法匹配几乎一切。只要不变短。

为何两者是等效的,看下面示例:

// 非收缩文法
A B -> C D
// 可以转化为下面的上下文相关文法
A B -> A X
A X -> Y X
Y X -> Y D
Y D -> C D

正则表达式可以匹配上下文相关语言吗?

这次我不敢打包票了。当然是可以匹配一些的,然而我不确定是否可以匹配全部。

正则可以轻易匹配的上下文相关语言示例是上文提到的上下文无关语言示例的变体。例如{a^n b^n c^n, n>0}一些a后面跟着相同数量的bc,这就变成了上下文相关了。

对应的PCRE正则是:

/^
	(?=(a(?-1)?b)c)
	 a+(b(?-1)?c)
$/x

如果去掉开头的断言(?=...),正则只剩下a+(b(?-1)?c)。这部分检查了任意数量的a,随之是相同数量的bc(?-1)是相对子模式引用,代表了“最后一个定义的子模式”,也就是(b(?-1)?c)

开头的(?=...)就是所谓的零宽度前顾断言。它检查接下来的文本是否匹配模式,然而并不会真正消耗文本。因此文本会同时被两个模式进行匹配。a+(b(?-1)?c)部分验证了bc的数量是相同的,(a(?-1)?b)c部分验证了ab的数量是相同的。两个模式同时确保了三个字符数量是相同的。

通过上面的示例你可以看到“上下文”的概念是如何在正则中体现的,那就是使用断言。如果再回过头看上下文相关文法的定义,就知道这种类型的产生式

α A β → α γ β

可以转化为下面定义的正则规则:

(? (?<= α ) γ (?= β ) )

这个式子说A可以替换为γ,当且仅当左边有个α右边有个β

有了前面的示例,你可能会觉得上下文相关文法都可以轻易的转化为正则表达式,然而并不是的。原因就是后顾断言((?<= ...))有一个很关键的限制:他们需要固定宽度。也就是说断言匹配的文本长度需要提前确知。例如你可以这样写(?<= a(bc|cd) ),但是不能这样写(?<= ab+)。前者中的断言再每种情况下都匹配三个字符,后者中可以匹配ababbabbb等。这样的话引擎无法确定从何处开始匹配,所以这是不允许的。

这就决定了上下文无关文法到正则的转化不是那么轻松。因为这种文法基本都需要变宽的后顾断言。

然而上下文无关文法到正则不能直接转换并不代表正则不能匹配所有。例如上面的{a^n b^n c^n, n>0}语言的文法也可以使用变宽的后顾断言表示。但是我们依然可以避免,因为正则表达式不局限于指定文法规则。可能对于所有其他的上下文相关文法也是如此。说实话我也不确定。

所以最后我们能得出什么结论呢?正则表达式至少可以匹配一些上下文无关语言,但是不确定是不是全部。

你可能感兴趣的:(编程语言)