[英]How does this regex find triangular numbers?
這是一系列教育正則表達式文章的一部分,這是對嵌套引用概念的溫和介紹。
前幾個三角形數字是:
1 = 1
3 = 1 + 2
6 = 1 + 2 + 3
10 = 1 + 2 + 3 + 4
15 = 1 + 2 + 3 + 4 + 5
有很多方法可以檢查數字是否為三角形。 有一種有趣的技術使用正則表達式如下:
^(\\1.|^.)+$
進行匹配
以下是一些片段,表明它適用於多種語言:
$r = '/^(\1.|^.)+$/';
foreach (range(0,50) as $n) {
if (preg_match($r, str_repeat('o', $n))) {
print("$n ");
}
}
for (int n = 0; n <= 50; n++) {
String s = new String(new char[n]);
if (s.matches("(\\1.|^.)+")) {
System.out.print(n + " ");
}
}
Regex r = new Regex(@"^(\1.|^.)+$");
for (int n = 0; n <= 50; n++) {
if (r.IsMatch("".PadLeft(n))) {
Console.Write("{0} ", n);
}
}
所以這個正則表達式似乎有效,但有人可以解釋一下嗎?
這是模式的示意性細分:
from beginning…
| …to end
| |
^(\1.|^.)+$
\______/|___match
group 1 one-or-more times
(…)
括號定義捕獲組1,並且該組與+
重復匹配 。 此子模式使用^
和$
錨定 ,以查看它是否可以匹配整個字符串。
組1嘗試匹配this|that
替換 :
\\1.
,也就是說,第1組匹配(自引用!),加上“任意”字符之一 , ^.
,也就是說,開頭只是“任何”一個字符 請注意,在第1組中,我們引用了第1組匹配的內容! 這是嵌套/自引用 ,是本示例中介紹的主要思想。 請記住,當重復捕獲組時,通常它只保留最后一次捕獲 ,因此本例中的自引用基本上表示:
“嘗試匹配我上次匹配的內容,再加上一次。這就是我這次匹配的內容。”
與遞歸類似,必須有一個帶有自引用的“基本案例”。 在第一次迭代+
,1組沒有捕獲任何東西(這是不一樣的話說,它開始與一個空字符串)。 因此,引入第二個替換,作為“初始化”組1的一種方式,即當它在字符串的開頭時允許捕獲一個字符。
因此,當用+
重復時,組1首先嘗試匹配1個字符,然后是2,然后是3,然后是4,等等。這些數字的總和是三角形數字。
請注意,為簡化起見,我們使用了與輸入相同的重復字符組成的字符串。 現在我們知道這個模式是如何工作的,我們可以看到這個模式也可以匹配像"1121231234"
, "aababc"
等字符串。
另請注意,如果我們發現n是三角形數,即n = 1 + 2 + ... + k ,則組1在末尾捕獲的字符串的長度將為k 。
這兩點都顯示在以下C#片段中( 也可在ideone.com上看到 ):
Regex r = new Regex(@"^(\1.|^.)+$");
Console.WriteLine(r.IsMatch("aababc")); // True
Console.WriteLine(r.IsMatch("1121231234")); // True
Console.WriteLine(r.IsMatch("iLoveRegEx")); // False
for (int n = 0; n <= 50; n++) {
Match m = r.Match("".PadLeft(n));
if (m.Success) {
Console.WriteLine("{0} = sum(1..{1})", n, m.Groups[1].Length);
}
}
// 1 = sum(1..1)
// 3 = sum(1..2)
// 6 = sum(1..3)
// 10 = sum(1..4)
// 15 = sum(1..5)
// 21 = sum(1..6)
// 28 = sum(1..7)
// 36 = sum(1..8)
// 45 = sum(1..9)
並非所有口味都支持嵌套引用。 始終熟悉您正在使用的風格的怪癖 (因此,無論何時提出與正則表達式相關的問題,它幾乎總是有助於提供此信息)。
在大多數情況下,標准正則表達式匹配機制會嘗試查看模式是否可以匹配輸入字符串的任何部分 (可能,但不一定是整個輸入)。 這意味着您應該記住在必要時始終使用^
和$
錨定您的模式。
Java略有不同,因為String.matches
, Pattern.matches
和Matcher.matches
嘗試將模式與整個輸入字符串進行匹配。 這就是為什么在上面的片段中可以省略錨點的原因。
請注意,在其他上下文中,您可能需要使用\\A
和\\Z
錨點。 例如,在多行模式下 , ^
和$
匹配輸入中每行的開頭和結尾。
最后一件事是,在.NET正則表達式,其實你可以得到所有重復捕獲組提出的中間捕獲。 在大多數風格中,你不能:所有中間捕獲都丟失了,你只能保留最后一個。
*
和+
(.NET!) 匹配的重復次數 通過非常輕微的修改,您可以使用此處介紹的相同技術來查找二次方的功能。
這是您想要利用的基本數學屬性:
解決方案如下(但請先嘗試自己解決!!!!)
(請參閱PHP , Java和C#中的 ideone.com):
^(\\1\\1|^.)*.$
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.