^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$
Оскільки це всього лише один регулярний вираз, Retina запуститься в режимі Match та повідомить про кількість знайдених відповідностей, що 1
стосується дійсних послідовностей та в 0
іншому випадку. Це не є конкурентоспроможним порівняно з мовами для гри в гольф, але я цілком задоволений цим, побачивши, що я почав чудовисько з 260 байт.
Пояснення
^((.)(?<!\2.+))*
Цей біт споживає префікс унікальних літер змінної довжини, тобто відповідає потенційно неповному провідному фрагменту. Позадача забезпечує те, що жоден символ, відповідний цьому біту, раніше не з'являвся в рядку.
Тепер для решти даних ми хочемо зіставити шматки 7 без повторення символів. Ми можемо порівняти такий шматок, як цей:
(.)(?!.{0,5}\1)(.)(?!.{0,4}\2)(.)(?!.{0,3}\3)...(.)(?!.?\5).
Тобто ми співставляємо символ, який не з’являється для інших 6 символів, потім той, який не з’являється для інших 5 символів тощо. Але для цього потрібно досить жахливе повторення коду, і нам доведеться окремо узгоджувати кінцевий (потенційно неповний) фрагмент.
Балансуючі групи на допомогу! Інший спосіб узгодження
(.)(?!.{0,5}\1)
полягає в тому, щоб натиснути 5 порожніх сірників на стек захоплення і спробувати випорожнити його:
(){5}(.)(?!(?<-1>.)*\2)
*
Дозволяє як мінімум нуль повторень, точно так само як {0,5}
, і тому , що ми штовхнув п'ять захоплень, він не зможе вискочити більш ніж в 5 разів або. Це більше для одного екземпляра цього шаблону, але це набагато більше для багаторазового використання. Оскільки ми робимо спливаюче вікно у негативному пошуку , це не впливає на фактичний стек після завершення пошуку. Отже, після пошуку, у нас все ще є 5 елементів на стеці, незалежно від того, що сталося всередині. Крім того, ми можемо просто вивести один елемент зі стека перед кожним lookahead та запустити код у циклі, щоб автоматично зменшити ширину lookahead від 5 до 0. Так що дійсно довгий біт там насправді може бути скорочений до
(){7}((?<-1>)(.)(?!(?<-1>.)*\1\3))*
(Ви можете помітити дві різниці: ми натискаємо на 7 замість 5. Одне додаткове захоплення - це те, що ми з'являємося перед кожною ітерацією, а не після неї. Інша насправді необхідна, щоб ми могли вискакувати зі стека 7 разів (оскільки ми хочемо цикл, який потрібно виконати 7 разів), ми можемо виправити цю похибку по черзі всередині lookahead, гарантуючи, \1
що в стеку залишився хоча б один елемент.)
Краса цього полягає в тому, що він також може відповідати останньому неповному фрагменту, тому що ми ніколи не вимагали, щоб він повторювався 7 разів (це просто необхідний максимум, тому що ми не можемо вискакувати зі стека частіше за все). Отже, все, що нам потрібно зробити, це загорнути це в інший цикл і переконатися, що ми дійшли до кінця рядка
^((.)(?<!\2.+))*((){7}((?<-4>)(.)(?!(?<-4>.)*\4\6))*)*$