Регулярное выражение в алфавитном порядке с использованием обратных ссылок

Недавно я столкнулся с головоломкой, чтобы найти регулярное выражение, которое соответствует:

Строки длиной 5 символов, состоящие из строчных букв латинского алфавита в порядке возрастания ASCII.

Допустимые примеры включают:

aaaaa
abcde
xxyyz
ghost
chips
demos

Недопустимые примеры включают:

abCde
xxyyzz
hgost
chps

Мое текущее решение является kludgy. Я использую регулярное выражение:

(?=^[a-z]{5}$)^(a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*)$

который использует не потребляющую группу захвата для утверждения длины строки 5, а затем проверяет, состоит ли строка из строчных букв латинского алфавита в порядке (см. рубрику).

Вместо этого я хотел бы использовать обратные ссылки внутри классов символов. Что-то вроде:

^([a-z])([\1-z])([\2-z])([\3-z])([\4-z])$

Логика решения (см. Rubular) в моей голове состоит в том, чтобы захватить первый символ [a-z], использовать это как обратная ссылка во втором классе символов и так далее. Однако \1, \2... в классах символов, по-видимому, относятся к значениям ASCII 1, 2... эффективно совпадающим с любой строкой из четырех или пяти символов.

У меня есть 2 вопроса:

  1. Могу ли я использовать обратные ссылки в своих классах символов для проверки строк в порядке возрастания?
  2. Есть ли какое-нибудь менее хакерское решение этой головоломки?

person Jedi    schedule 30.06.2017    source источник
comment
Насколько я могу судить, ваше глупое решение настолько приятно, насколько это возможно, потому что классы персонажей плохо сочетаются с вашими обратными ссылками (но мне нравится ваша логика, кажется, это хорошая функция). Есть ли у вас конкретная среда для запуска этого регулярного выражения (только Ruby или независимо)? Я уверен, что мастера регулярных выражений SO скоро появятся, чтобы добавить свой опыт.   -  person mickmackusa    schedule 01.07.2017
comment
Нет обратных ссылок внутри классов персонажей. Причина в том, что классы символов составляются во время компиляции. Определяющим фактором является то, что обратная ссылка является динамической, и то, что исключает ее внутри класса, — это оператор диапазона. Итак, они говорят... Нет динамического диапазона, иначе движок выйдет из строя в исключении C++.   -  person    schedule 13.07.2017
comment
came across a puzzle to find a regular expression - Обязательно процитируйте эту ссылку, чтобы мы могли посмеяться над загадкой..   -  person    schedule 13.07.2017
comment
На самом деле, это довольно просто в регулярном выражении Perl. Вы можете делать всевозможные вещи, такие как подсчет, последовательность, логические значения, вычитание и т. д.... (?{..}). Если вы думаете, что будете использовать Perl, то это... выполнимо. Кстати, ваше первое регулярное выражение просто отлично.   -  person    schedule 13.07.2017
comment
Еще одна вещь, которую следует отметить, это то, что в классах символов оператор range относится к отдельным символам (min, max), а не к набору символов, например, \pL-z выдает ошибку построения. Таким образом, ссылка может содержать несколько символов.   -  person    schedule 13.07.2017
comment
Почему demos включен в недопустимый список?   -  person anubhava    schedule 14.07.2017
comment
хорошо, ваше регулярное выражение выглядит довольно хорошо для меня. Вы можете немного улучшить его, используя: ^(?=[a-z]{5}$)a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*$   -  person anubhava    schedule 14.07.2017
comment
дубликат https://stackoverflow.com/questions/3171671/regex-5-digits-in-increasing-order   -  person A1m    schedule 14.07.2017
comment
Вопрос не может быть закрыт из-за награды, но это неверная ссылка.   -  person mickmackusa    schedule 14.07.2017


Ответы (4)


Я отправляю этот ответ скорее как комментарий, чем как ответ, поскольку он имеет лучшее форматирование, чем комментарии.

В связи с вашими вопросами:

  1. Могу ли я использовать обратные ссылки в своих классах символов для проверки строк в порядке возрастания?

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

Скобки и обратные ссылки нельзя использовать внутри классов символов

Скобки нельзя использовать внутри классов символов, по крайней мере, в качестве метасимволов. Когда вы помещаете круглую скобку в класс символов, она рассматривается как буквальный символ. Таким образом, регулярное выражение [(a)b] соответствует a, b, ( и ).

Обратные ссылки также нельзя использовать внутри класса символов. \1 в регулярном выражении типа (a)[\1b] — это либо ошибка, либо литерал 1 без необходимости экранируется. В JavaScript это восьмеричный экран.

Относительно вашего 2-го вопроса:

  1. Есть ли какое-нибудь менее хакерское решение этой головоломки?

Имхо, ваше регулярное выражение прекрасно, вы могли бы немного сократить его в начале, например так:

(?=^.{5}$)^a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*$
    ^--- Here

демонстрация регулярных выражений

person Federico Piazza    schedule 14.07.2017
comment
Думаю, стоит сказать, почему обратные ссылки в классах персонажей невозможны. Обратная ссылка может указывать на совпадающую подстроку, которая может быть любой длины, даже пустой. Таким образом, такой код, как [\1-z], может дать [-z], [f-z], [foo-z] и т. д. Это не имеет большого смысла, и шаблон придется перекомпилировать на лету, что предотвратит оптимизацию. - person Lucas Trzesniewski; 15.07.2017
comment
Беспокоит, когда цитируют документы, когда они не приводят причин того, что что-то нельзя использовать таким фундаментальным образом. Ссылки внутри классов символов являются таким примером. Но вы всегда можете использовать свои собственные рассуждения для такого случая, и довольно легко понять, почему в этом случае. - person ; 16.07.2017

Если вы хотите использовать Perl (!), это сработает:

/^([a-z])((??{"[$1-z]"}))((??{"[$2-z]"}))((??{"[$3-z]"}))(??{"[$4-z]"})$/
person NetMage    schedule 14.07.2017
comment
Но... это обман! :) Если вы используете Perl, то я думаю, вы могли бы просто закодировать утверждение напрямую: /^([a-z]{5})$(?(?{$1 eq (join "", sort split qr##, $1)})|(?!))/ - person Lucas Trzesniewski; 15.07.2017
comment
В какой-то момент я думаю, что сомнительно, используете ли вы решение Regex. Я думаю, что мой ближе к континууму :) - person NetMage; 17.07.2017

Поскольку кто-то сломал лед, используя Perl, я думаю, что это Perl-решение.


Обратите внимание, что это базовое решение, не связанное с регулярными выражениями, которое
просто помещается в конструкции кода внутри регулярных выражений Perl.
Интересно, что если наступит день, когда вам понадобится синергия
регулярного выражения/кода это хороший выбор.
Тогда возможно, что вместо простого символа [a-z] вы можете
использовать вместо него очень сложный шаблон и использовать проверку против последнего.
Это это сила!!


Регулярное выражение ^(?:([a-z])(?(?{ $last gt $1 })(?!)|(?{ $last = $1 }))){5}$

Perl-код

use strict;
use warnings;


$/ = "";

my @DAry = split /\s+/, <DATA>;

my $last;

for (@DAry)
{
    $last = '';
    if ( 
      /
         ^                             # BOS
         (?:                           # Cluster begin
              ( [a-z] )                     # (1), Single a-z letter
                                            # Code conditional
              (?(?{
                   $last gt $1                  # last > current ?
                })
                   (?!)                          # Fail
                |                              # else,
                   (?{ $last = $1 })             # Assign last = current
              )
         ){5}                          # Cluster end, do 5 times
         $                             # EOS
      /x )
    {
        print "good   $_\n";
    }
    else {
        print "bad    $_\n";
    }
}

__DATA__

aaaaa
abcde
xxyyz
ghost
chips
demos
abCde
xxyyzz
hgost
chps

Выход

good   aaaaa
good   abcde
good   xxyyz
good   ghost
good   chips
good   demos
bad    abCde
bad    xxyyzz
bad    hgost
bad    chps
person Community    schedule 15.07.2017

Ах, ну, это конечное множество, так что вы всегда можете перечислить его с чередованием! Это выдает регулярное выражение «грубой силы» в небольшом REPL Perl:

#include <stdio.h>

int main(void) {
  printf("while (<>) { if (/^(?:");
  for (int a = 'a'; a <= 'z'; ++a)
    for (int b = a; b <= 'z'; ++b)
      for (int c = b; c <= 'z'; ++c) {
        for (int d = c; d <= 'y'; ++d)
          printf("%c%c%c%c[%c-z]|", a, b, c, d, d);
        printf("%c%c%czz", a, b, c);
        if (a != 'z' || b != 'z' || c != 'z') printf("|\n");
      }
  printf(")$/x) { print \"Match!\\n\" } else { print \"No match.\\n\" }}\n");
  return 0;
}

И сейчас:

$ gcc r.c
$ ./a.out > foo.pl
$ cat > data.txt
aaaaa
abcde
xxyyz
ghost
chips
demos
abCde
xxyyzz
hgost
chps
^D
$ perl foo.pl < data.txt
Match!
Match!
Match!
Match!
Match!
Match!
No match.
No match.
No match.
No match.

Регулярное выражение составляет всего 220 КБ или около того ;-)

person Gene    schedule 20.07.2017
comment
Ааааааааааааааааааааа Ваше регулярное выражение сломало Rubular (не может сделать постоянную ссылку). :-) Да, это 219 КБ raw и работает! - person Jedi; 20.07.2017
comment
Спасибо за это. Используя необработанный текст регулярного выражения (из корзины для вставки @Jedi), я пропустил его через этот тройной инструмент и сделал полноценное регулярное выражение. Это немного уменьшило размер (160 тыс. ">сжато, 840 КБ отформатировано). Вот тест Python. - person ; 23.07.2017
comment
Кроме того, я сгенерировал полные 142 506 возможных строк, а затем использовал полное регулярное выражение trie для проверки производительности: Regex1: <omitted> Options: < none > Completed iterations: 1 / 1 ( x 1 ) Matches found per iteration: 142506 Elapsed Time: 0.18 s, 178.87 ms, 178871 µs - person ; 23.07.2017