codingBat repeatEnd используя регулярное выражение - PullRequest
3 голосов
/ 09 апреля 2010

Я пытаюсь понять регулярные выражения настолько, насколько могу, поэтому я придумал это решение на основе регулярных выражений для 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, чтобы сначала пройти весь путь до конца строки, а затем вложенный look-back для захвата последних N символов в \1. Обратите внимание, что это утверждение всегда будет верным.

  • |.: если первое утверждение не выполнено (т. Е. Впереди не менее N символов), то в любом случае соответствует символу; \1 будет пустым.

  • В любом случае символ всегда совпадает; замените его на \1.

Мои вопросы:

  • Допустима ли эта техника вложенных утверждений? (т. е. оглядываться во время предвкушения?)
  • Есть ли более простое решение на основе регулярных выражений?

Бонусный вопрос

Do repeatBegin (как определено аналогично).

Честно говоря, у меня проблемы с этим!

Ответы [ 3 ]

3 голосов
/ 09 апреля 2010

Хороший! Я не вижу способа значительно улучшить это регулярное выражение, хотя я бы реорганизовал его, чтобы избежать ненужного использования негативной логики:

".(?=.{N})|.(?=.*(?<=(.{N})))"

Таким образом, вторая альтернатива никогда не будет введена, пока вы не достигнете последних N символов, что, я думаю, делает смысл немного более ясным.

Я никогда не видел упоминаний, в которых говорилось бы, что все в порядке, но, как и Барт, я не понимаю, почему это не так. Иногда я использую lookaheads внутри lookbehinds, чтобы обойти ограничения для выражений lookbehind переменной длины.


РЕДАКТИРОВАТЬ: Я только что понял, что могу немного упростить регулярное выражение, поместив чередование в поле зрения:

".(?=.{N}|.*(?<=(.{N})))"

Кстати, вы рассматривали возможность использования format() для построения регулярного выражения вместо replace()?

return str.replaceAll(
  String.format(".(?=.{%1$d}|.*(?<=(.{%1$d})))", N),
  "$1"
);
1 голос
/ 09 апреля 2010

Ух ты, это какое-то страшное регулярное вуду! :)

  • Действительна ли эта техника вложенных утверждений? (т. е. оглядываться во время предвкушения?)

Да, это совершенно справедливо в большинстве реализаций PCRE, о которых я знаю.

  • Есть ли более простое решение на основе регулярных выражений?

Я не тратил на это слишком много времени, но не сразу понял, как это можно упростить или сократить с помощью одной замены регулярного выражения.

0 голосов
/ 09 апреля 2010

Есть ли более простое решение на основе регулярных выражений?

Это заняло у меня некоторое время, но в итоге мне удалось упростить регулярное выражение до:

"(?=.{0,N}$(?<=(.{N}))).|." // repeatEnd
          -or-
".(?<=^(?=(.{N})).{0,N})|." // repeatBegin

Как и ответ Алана Мура, это удаляет отрицательное утверждение, но даже не заменяет его положительным, так что теперь оно имеет только 2 утверждения вместо 3.

Мне также нравится тот факт, что случай "else" - это просто .. Я предпочитаю помещать основную часть моего регулярного выражения в «рабочую» сторону чередования и сохранять «нерабочую» сторону как можно более простой (обычно простой . или .*).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...