Как это регулярное выражение находит треугольные числа?

Часть серии обучающих статей о регулярных выражениях, это краткое введение в концепцию вложенных ссылок.

Первые несколько треугольных чисел:

 1 = 1
 3 = 1 + 2
 6 = 1 + 2 + 3
10 = 1 + 2 + 3 + 4
15 = 1 + 2 + 3 + 4 + 5

Есть много способов проверить, является ли число треугольным. Вот этот интересный метод, в котором регулярные выражения используются следующим образом:

  • Учитывая n, мы сначала создаем строку длиной n, заполненную тем же символом.
  • We then match this string against the pattern ^(\1.|^.)+$
    • n is triangular if and only if this pattern matches the string

Вот несколько фрагментов, показывающих, что это работает на нескольких языках:

PHP (на ideone.com)

$r = '/^(\1.|^.)+$/';

foreach (range(0,50) as $n) {
  if (preg_match($r, str_repeat('o', $n))) {
     print("$n ");
  }
}

Java (на ideone.com)

for (int n = 0; n <= 50; n++) {
    String s = new String(new char[n]);
    if (s.matches("(\\1.|^.)+")) {
        System.out.print(n + " ");
    }
}

C # (на ideone.com)

Regex r = new Regex(@"^(\1.|^.)+$");

for (int n = 0; n <= 50; n++) {
    if (r.IsMatch("".PadLeft(n))) {
       Console.Write("{0} ", n);
    }
}

Итак, это регулярное выражение, похоже, работает, но может кто-нибудь объяснить, как?

Похожие вопросы


person polygenelubricants    schedule 02.09.2010    source источник
comment
Эта серия была начата с разрешения некоторых участников сообщества (заголовок meta.stackexchange.com/questions/62695/). Если прием хороший, я планирую продолжить рассмотрение других более сложных, а также более простых функций регулярного выражения.   -  person polygenelubricants    schedule 02.09.2010
comment
Если это должно быть образовательным и для сообщества, почему это не вики сообщества?   -  person wheaties    schedule 02.09.2010
comment
Я думаю, что усилия стоят определенной репутации. Пожалуйста, не навязывайте им вики сообщества. Какая разница, если поли увеличится с 44к до 50к, какая разница?   -  person jjnguy    schedule 02.09.2010
comment
С нетерпением жду сериал. Обратите внимание: если вас интересует краткое исследование происхождения регулярных выражений, я начал писать серию блогов об этом. К сожалению, я так и не закончил. blogs.msdn.com/b/ericlippert/archive/tags/regular + выражения   -  person Eric Lippert    schedule 02.09.2010
comment
Если это серия - как насчет того, чтобы изобрести тег, который упростил бы поиск всех «статей» этой серии !?   -  person Andreas Dolk    schedule 02.09.2010
comment
@Andreas: вы можете обсудить логистику этой серии на мета. Я бы не возражал против создания нового тега, но я был бы осторожен с метатегами (например, [beginner] был изгнан - blog.stackoverflow.com/2010/08/the-death-of-meta-tags). На данный момент я планирую обновить обсуждение меты ссылками на каждую статью из этой серии.   -  person polygenelubricants    schedule 02.09.2010
comment
Вышла вторая часть серии: stackoverflow.com/questions/3644266/   -  person polygenelubricants    schedule 05.09.2010
comment
Часть 3: stackoverflow.com/ questions / 3664881 /   -  person polygenelubricants    schedule 08.09.2010
comment
Этот вопрос был добавлен в Часто задаваемые вопросы о регулярных выражениях при переполнении стека в разделе Advanced Regex-Fu.   -  person aliteralmind    schedule 10.04.2014


Ответы (1)


Объяснение

Вот схематическая разбивка паттерна:

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 немного отличается тем, что _ 16_, _ 17_ и _ 18_ пытается сопоставить шаблон с целым строка ввода. Вот почему якоря можно не указывать в приведенном выше фрагменте.

Обратите внимание, что в других контекстах вам может потребоваться использовать якоря \A и \Z. Например, в многострочном режиме ^ и $ совпадают начало и конец каждая строка во входных данных.

И последнее: в регулярном выражении .NET вы МОЖЕТЕ фактически получить все промежуточные захваты, сделанные повторяющейся группой захвата. В большинстве вариантов вы не можете: все промежуточные захваты теряются, и вы можете сохранить только последние.

Связанные вопросы


Дополнительный материал: использование регулярных выражений для определения степени двойки !!!

С очень небольшими изменениями вы можете использовать те же методы, представленные здесь, чтобы найти степень двойки.

Вот основное математическое свойство, которым вы хотите воспользоваться:

  • 1 = 1
  • 2 = (1) + 1
  • 4 = (1+2) + 1
  • 8 = (1+2+4) + 1
  • 16 = (1+2+4+8) + 1
  • 32 = (1+2+4+8+16) + 1

Решение приведено ниже (но сначала попробуйте решить его самостоятельно !!!!)

(см. на ideone.com в PHP, Java и C #):

^(\1\1|^.)*.$

person polygenelubricants    schedule 02.09.2010
comment
Также обратите внимание, что тот факт, что существуют библиотеки регулярных выражений, которые могут соответствовать этому шаблону, по иронии судьбы свидетельствует о том, что так называемые регулярные выражения не являются регулярными! Формальное определение регулярного языка - это, грубо говоря, набор строк, которые точно совпадают с теми, которые соответствуют некоторой машине сопоставления, которая имеет ограниченное количество внутренних состояний, но знание того, что я сопоставил раньше, требует теоретически потенциально неограниченного количества состояний. - person Eric Lippert; 02.09.2010
comment
@Eric - Значит, вы бы назвали их неправильными выражениями? - person ChaosPandion; 02.09.2010
comment
@Eric, @Chaos Я задал вопрос об этом несоответствии в теоретической бета-версии StackExchange по информатике. . - person Greg Bacon; 02.09.2010
comment
Формальные регулярные выражения имеют только - person Brandon; 24.11.2011