CodingBat repeatEnd с использованием regex
Я пытаюсь понять регулярное выражение как можно больше, поэтому я придумал это решение на основе regex для codingbat.com repeatEnd
:
С учетом строки и int N верните строку из N повторений последних N символов строки. Вы можете предположить, что N находится между 0 и длиной строки, включительно.
public String repeatEnd(String str, int N) {
return str.replaceAll(
".(?!.{N})(?=.*(?<=(.{N})))|."
.replace("N", Integer.toString(N)),
"$1"
);
}
Объяснение по его частям:
-
.(?!.{N})
: утверждает, что совпадающий символ является одним из последних N символов, убедившись, что за ним не осталось N символов.
-
(?=.*(?<=(.{N})))
: в этом случае используйте lookforward, чтобы сначала пройти весь путь до конца строки, затем вложенный lookbehind, чтобы захватить последние N символов в \1
. Обратите внимание, что это утверждение всегда будет истинным.
-
|.
: если первое утверждение не выполнено (т.е. впереди по крайней мере N символов), то в любом случае совпадение с символом; \1
будет пустым.
-
В любом случае символ всегда сопоставляется; замените его на \1
.
Мои вопросы:
- Является ли эта методика вложенными утверждениями действительными? (т.е. оглядываясь назад во время просмотра?)
- Есть ли более простое решение на основе регулярных выражений?
Бонусный вопрос
Do repeatBegin
(как аналогично определено).
Я честно испытываю проблемы с этим!
Ответы
Ответ 1
Хороший! Я не вижу возможности значительно улучшить это регулярное выражение, хотя я бы реорганизовал его, чтобы избежать ненужного использования отрицательной логики:
".(?=.{N})|.(?=.*(?<=(.{N})))"
Таким образом, вторая альтернатива никогда не вводится до тех пор, пока вы не достигнете конечных N символов, что, я думаю, делает намерение немного яснее.
Я никогда не видел ссылки, в которой говорится, что все в порядке, чтобы вложить поиски, но, как и Барт, я не понимаю, почему этого не будет. Я иногда использую lookaheads внутри lookbehinds, чтобы обойти ограничения для выражений lookbehind переменной длины.
EDIT: Я просто понял, что могу немного упростить регулярное выражение, поместив чередование в lookahead:
".(?=.{N}|.*(?<=(.{N})))"
Кстати, рассмотрели ли вы использование format()
для создания регулярного выражения вместо replace()
?
return str.replaceAll(
String.format(".(?=.{%1$d}|.*(?<=(.{%1$d})))", N),
"$1"
);
Ответ 2
Ого, что-то страшное reugx voodoo там!:)
- Является ли эта методика вложенными утверждениями действительными? (т.е. оглядываясь назад во время просмотра?)
Да, это прекрасно действует в большинстве реализаций PCRE, о которых я знаю.
- Есть ли более простое решение на основе регулярных выражений?
Я не тратил на это слишком много времени, но я не понимаю, как это можно упростить или сократить с помощью замены одного регулярного выражения.
Ответ 3
Существует ли более простое решение на основе регулярного выражения?
Мне потребовалось некоторое время, но в итоге мне удалось упростить регулярное выражение:
"(?=.{0,N}$(?<=(.{N}))).|." // repeatEnd
-or-
".(?<=^(?=(.{N})).{0,N})|." // repeatBegin
Как и ответ Алана Мура, это устраняет отрицательное утверждение, но даже не заменяет его положительным, поэтому теперь он имеет только 2 утверждения вместо 3.
Мне также нравится тот факт, что случай "else" является просто простым .
. Я предпочитаю накладывать основную часть своего регулярного выражения на "рабочую" сторону чередования и держать "нерабочую" сторону максимально простой (обычно простой .
или .*
).