Wie funktioniert das Java-regex erkennen Palindrome?
Dies ist der Dritte Teil in einer Reihe von Bildungs-regex-Artikel. Es folgt Wie funktioniert dieser regex finden, dreieckige zahlen? (wo verschachtelten Referenzen wird vorgestellt) und Wie können wir mit a^n b^n mit Java-regex?
(wo die lookahead - "counting" - Mechanismus wird weiter herausgearbeitet). Dieser Teil stellt eine spezifische form der geschachtelten Behauptung, die, wenn Sie kombiniert mit verschachtelten Referenzen ermöglicht Java-regex-match, was die meisten Menschen glauben, dass es "unmöglich": Palindrome!!
Die Sprache der Palindrome ist nichtregelmäßige; es ist tatsächlich Kontext-frei (für ein gegebenes alphabet). Das heißt, die modernen regex-Implementierung erkennt mehr als nur die regulären Sprachen, und Perl/PCRE die rekursive Muster und .NET-balancing-groups können leicht erkennen Palindrome (siehe: Fragen).
Jedoch Java-regex-engine unterstützt keine dieser "erweiterte" Funktionen. Und noch "jemand" (*wink*) geschafft zu schreiben, der folgende regex, die scheint, um den job zu erledigen just fine (siehe auch auf ideone.com):
public class Palindrome {
//asserts that the entirety of the string matches the given pattern
static String assertEntirety(String pattern) {
return "(?<=(?=^pattern$).*)".replace("pattern", pattern);
}
public static void main(String[] args) {
final String PALINDROME =
"(?x) | (?:(.) add)+ chk"
.replace("add", assertEntirety(".*? (\\1 \\2?)"))
.replace("chk", assertEntirety("\\2"));
System.out.println(PALINDROME);
//(?x) | (?:(.) (?<=(?=^.*? (\1 \2?)$).*))+ (?<=(?=^\2$).*)
String[] tests = {
"", //true
"x", //true
"xx", //true
"xy", //false
"xyx", //true
"xxx", //true
"xxyx", //false
"racecar", //true
"step on no pets", //true
"aManaPlanaCanalPanaMa", //true
"this is impossible", //FALSE!!!
};
for (String test : tests) {
System.out.printf("[%s] %s%n", test, test.matches(PALINDROME));
}
}
}
Also das scheint zu funktionieren, aber wie?
Referenzen
java.util.regex.Pattern
- regular-expressions.info/Freespacing
(?x)
, Lookarounds(?=...)
/(?<=...)
, etc.
DER GESUNDE MENSCHENVERSTAND ALARM!!!!!
Dies ist nicht der beste Weg, um erkennen Palindrome; es ist
O(N^3)
am besten. Die Durchführung dieser Fund in einer mehr Allzweck-Programmiersprache ist effizienter und einfacher.Würden Sie nicht wollen, um verwenden regex zu erkennen Palindrome aus den gleichen Gründen, die Sie würde nicht wollen, zu verwenden regex zum finden von Primzahlen. Das heißt, Sie würden Studie, wie ein nicht-rekursiver, nicht-bilanzkreis regex erkennen kann, Palindrome aus den gleichen Gründen würden Sie Studie, wie ein regex verwendet werden kann für primality testing: es macht Spaß, es ist herausfordernd, es ist lehrreich.
Fragen
- Wie Sie überprüfen, ob ein string ein Palindrom mit regulären Ausdrücken? - es ist "unmöglich"! (es sei denn...)
- Wie um zu überprüfen, ob der übergebene string ist Palindrom? - nicht-regex-Lösungen in vielen Sprachen
- Wie zu bestimmen, ob eine Zahl eine Primzahl mit regex?
- Diskussion der Serie: meta.stackexchange.com/questions/62695/... ; Auch, kein Zweifel, es sind Tippfehler/Fehler, die in diesem langen Artikel. Bitte hinterlassen Sie Kommentare und feedbacks, wie ich es Tue-plan ständig aktualisiert.
- Ich möchte nur erwähnen, dass die Verwendung von regulären Ausdrücken zu erkennen Palindrome ist eine besonders dumme Idee. Es gibt viel bessere Möglichkeiten, es zu tun. Verwenden Sie dies, um sich selbst zu erziehen, die auf regulären Ausdrücken, sondern Teil der Bildung ist, zu wissen, wenn Sie nicht zu verwenden. Nicht versuchen, den Regen auf Ihrer parade, @poly, ich bin sicher, dies ist ein guter Artikel 🙂
- Dieser Artikel ist nicht wirklich über Palindrome, für fast den gleichen Gründen, die Fabeln sind nicht wirklich darüber reden, Löwen-und/oder Gesang Esel. Es gibt Moral und Lektionen, hier gewesen zu sein, und betrachten dies als nur ein Palindrom Erkennung regex-lediglich Kratzer an der Oberfläche.
- Ich Stimme mit @paxdiablo und möchte hinzufügen, dass zu tun, komplizierte Dinge allgemein mit regexes ist eine dumme / schlechte Idee. IMO, dieses Frage / Antwort sollte vorangestellt werden, die Warnung NICHT TUN in blinkende rote Punkt 26 Großbuchstaben.
- Ich denke, es gibt einen GROßEN Unterschied zwischen den Fragen "Wie muss ich Parsen dieses XML-fragment mit regex?" und "Wie können wir erkennen Palindrome mit (Java -) regex?". Mit der ehemaligen, die Leute wissen es nicht besser, aber mit der letzteren, die Menschen wissen es nicht die beste Lösung, sondern Sie Fragen, weil Sie lernen wollen. Dies ist die gleiche treibende motivation, fasziniert die Menschen zu verstehen, z.B. primality Prüfung mit regex. Es ist OFFENSICHTLICH nicht die "richtige" Lösung, sondern es ist untrennbar mit Bildung. Das heißt, ich füge ein "COMMON SENSE ALERT" - Warnung auf der nächsten revision irgendwann später heute.
- Ich bin glücklich mit dieser Frage, gegeben, dass es nicht die Regeln zu befolgen, wie es in der FAQ. Ich würde mich auch hassen zu sehen, es gehen, weil nur so verdammt viel Arbeit in ihm und die Antwort. Ich wurde nur darauf hin, dass Palindrom-Erkennung mit einem regex war nicht die beste Verwendung von es. Wahrscheinlich hätte ich nicht so kritisch, wenn ich die Zeit hatte, um das ganze zu Lesen, anstatt nur den Titel und das erste para 🙂
- Teil 4: stackoverflow.com/questions/3693698/...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Das Große Bild
Werden wir den ersten Blick auf diese regex vom großen ganzen-Algorithmus, und dann nehmen Sie einen genaueren Blick auf die konkrete Umsetzung im Detail später. Die regex ist eine fast direkte übersetzung des folgenden Java-code:
Dies ist natürlich nicht die einfachste/effizienteste Java-code zu überprüfen, Palindrome, aber es funktioniert, und die meisten faszinierend, es ist fast direkt übersetzbar, regex mit einer nahezu eins-zu-eins-Zuordnung. Hier ist die regex wieder reproduziert hier für die Bequemlichkeit, kommentierte hervorzuheben ist die auffallende ähnlichkeit:
Anlage: kommentierte und erweiterte version der source code auf ideone.com
(Fühlen Sie sich frei, zu ignorieren, die details von
assertEntirety
für heute: denken Sie nur als black-box-regex-Mechanismus stellen Sie eine Behauptung auf die gesamte Zeichenfolge unabhängig davon, wo wir gerade sind.)Also der grundlegende Algorithmus ist, dass wir versuchen, zu bauen ein suffix, subject, um ein Palindrom Einschränkung, als wir Scannen die Zeichenfolge von Links nach rechts. Wir dann prüfen, ob wir in der Lage sind, zu bauen, die den kompletten string in dieser Art und Weise. Wenn wir können, dann ist der string ist ein Palindrom. Auch, als Sonderfall, der leere string ist trivial ein Palindrom.
Einmal das große Bild-Algorithmus verstanden wird, können wir untersuchen, wie die regex-pattern implementiert.
Was ist mit all den
String.replace
?Regex-patterns in Java sind letztlich nichts als strings, das heißt, Sie können abgeleitet werden durch string-Manipulationen die Möglichkeit eine beliebige Zeichenfolge kann. Ja, wir können sogar mit regex zu generieren, die ein regex-Muster-eine Art meta-regexing Ansatz, wenn man so will.
Betrachten Sie dieses Beispiel der Initialisierung einer
int
konstant (was letztlich nichts enthält aber eine Reihe):Nummer zu
X
ist ein integer-literal: wir können deutlich sehen was die Nummer ist. Dies ist nicht der Fall mitY
die einen Ausdruck verwendet, statt, und doch ist diese Formel scheint zu vermitteln, eine Idee, was diese Zahl darstellt. Auch ohne korrekte Benennung dieser Konstanten werden wir dennoch auf die Idee kommen, dassY
wahrscheinlich stellt die Anzahl der Sekunden in einer Woche, auch wenn wir vielleicht nicht sofort wissen, was der numerische Wert ist. Auf der anderen Seite, mitX
wir wissen genau, dass die Nummer, aber wir bekommen weniger eine Idee von dem, was es darstellt.Die Verwendung von string-Ersetzungen im snippet ist eine analoge situation, aber für strings regex-Muster. Anstatt explizit schreiben das Muster als eine literal-Zeichenfolge, manchmal systematische und logische Ableitung ("Formel") der Wert von einfacheren teilen kann, ist viel mehr sinnvoll. Dies gilt insbesondere mit regex, wo es oft wichtig ist, dass wir verstehen, was das Muster bedeutet, als dass Sie in der Lage, um zu sehen, was es sieht aus wie ein string-literal (das ist nicht viel von einem Hingucker wie auch immer, was mit all den zusätzlichen backslashes).
Einen Teil des snippets wird hier wiedergegeben erneut für die Bequemlichkeit:
Ohne Zweifel die "Formel" ist viel besser lesbar als die etwaigen string "Wert" in diesem Fall.
Gibt es sicherlich viel mehr raffinierte Möglichkeiten, um programmatisch erzeugt ein regex-Muster, und es ist sicherlich möglich zu schreiben in einer Weise, die verschleiert, statt akzentuiert Ihre Bedeutung, aber achtsam Nutzung auch einfache string-Ersetzungen können sich noch Wundern (hoffentlich in diesem Beispiel gezeigt).
Lektion: Betrachten Sie die programmatische Generierung von regex-mustern.
Wie funktioniert
add
Arbeit?Den
(?:(.) add)+
konstruieren, woadd
ist eine Behauptung, die nicht irgendeine Art von "zählen", wurde bereits ausführlich diskutiert und in den beiden vorangegangenen teilen. Zwei Besonderheiten sind erwähnenswert, aber:(.)
erfasst in der Gruppe 1, so dass Rückverweis späterassertEntirety
anstatt nur zu schauen Voraus von unserer aktuellen positionMuster angewendet
assertEntirety
imadd
ist folgende:Beachten Sie, dass die Gruppe 2 ist selbst-referenzierend, optional mit einem Bezeichner, einer Technik, die bereits im Teil 2 der Serie. Unnötig zu sagen, die Gruppe 2 ist unsere "Zähler" in dieses Muster: es ist ein suffix, das werden wir versuchen zu wachsen, nach Links, auf jeder iteration der "loop". Als wir die Iteration auf jeden
(.)
Links nach rechts, versuchen wir voranstellen, dass die gleichen Zeichen (mit Rückverweis auf\1
) zu unserem suffix.Erneut aufrufen die Java-code übersetzung der oben genannten Muster, reproduziert hier für die Bequemlichkeit:
Die Tatsache, dass
\2?
ist optional bedeutet, ein paar Dinge:\2?
ist Teil des suffix-Muster (und damit erscheint später in der gesamten Muster), das Präfix müssen Sie nur ungern, daher.*?
statt.*
. Dies ermöglicht\2?
um die Ausübung Ihrer Habgier.?
kann in der gleichen Art problematisch zurücksetzen?+
, aber das ist hier nicht zutreffend,Der Dritte Punkt, der erarbeitet wird, weiter in den nächsten Abschnitt.
Lektion: Sorgfältig analysieren die Wechselwirkungen zwischen gierig/nur ungern Wiederholungen in den teilen der Muster.
Fragen
.*?
und.*
für regexWarum brauchen wir eine
chk
phase?Wie angedeutet, die im vorherigen Abschnitt, die optional und backtrackable
\2?
bedeutet, dass unser suffix schrumpfen können, unter gewissen Umständen. Untersuchen wir ein solches Szenario Schritt für Schritt für diesen Eingang:Können wir ändern unsere Muster (und den entsprechenden Java-code) weglassen
chk
phase, und sehen, dass in der Tat dies ist, was passiert:Wie gesagt
"xyxyzyx"
, die NICHT ein Palindrom, wird fälschlicherweise gemeldet, weil wir nicht überprüfen, ob die wachsende suffix wurde schließlich die vollständige Zeichenfolge (die es offensichtlich nicht in diesem Fall). Diechk
phase (das ist einassertEntirety
des Musters\2
) ist daher eine absolute Notwendigkeit in unserem setup. Wir müssen bestätigen, dass in der Tat haben wir es geschafft, zu wachsen, unsere suffix Weg. Wenn dies der Fall ist, dann haben wir uns selbst ein Palindrom.Lektion: Sorgfältig analysieren, eventuell unbeabsichtigte Nebenwirkungen der optionale selbst-Referenz-matching.
Der Hauptgang:
assertEntirety
Während es ist nett, dass wir können, schreiben Sie ein Java-regex-Muster zu erkennen Palindrome, alles hier, außer
assertEntirety
bereits abgedeckt in den vorherigen teilen der Serie. Die einzige neue Sache hier ist dieses mysteriöse schwarze box, diese leistungsfähigen Mechanismus, der magisch uns erlaubt, zu tun, was sonst "unmöglich".Den
assertEntirety
Konstrukt basiert auf den folgenden meta-Muster von verschachtelten lookarounds:Dem Namen "lookaround" impliziert die Relativität unserer aktuellen Lage: wir sind auf der Suche um uns, vielleicht vor oder hinter, von wo wir stehen. Durch die Verschachtelung ein lookahead in einem lookbehind auf diese Weise sind wir in der Lage, die sprichwörtliche "fliege in den Himmel" und betrachten das ganze Bild.
Abstrahiert diesem meta-Muster in
assertEntirety
ist ein bisschen wie das schreiben Vorverarbeitung substitution von Makros. Mit verschachtelten lookarounds überall wohl weh tut, Lesbarkeit und Wartbarkeit, also wir fassen es inassertEntirety
, die nicht nur versteckt die Komplexität der inneren Funktionsweise, aber auch betont weiter Ihre Semantik, indem Sie ihm einen passenden Namen.Lektion: Betrachten Sie abstrahiert meta-Muster zu verstecken die Komplexität und vermitteln Semantik.
Anhang: die Auf unendliche Länge lookbehind in Java
Aufmerksame Leser hätte bemerkt, dass
assertEntirety
enthält eine.*
in einer lookbehind, wodurch die theoretische maximale Länge unendlich. Nein, in Java nicht offiziell unterstützen, unendliche Länge lookbehind. Ja, es wurde adequatedly hier zeigt sich, es funktioniert trotzdem. Offiziell ist es kategorisiert als ein "bug", sondern "jemand"(*Zwinker*) könnte auch überlegen, es zu einem "hidden feature".Es ist sicherlich möglich, dass dieser "bug" wird "fixiert" in der Zukunft. Die Entfernung von dieser versteckten Funktion wird brechen diese spezielle Lösung um die Java-regex-Palindrom-problem.
Fragen