2012-04-05 6 views
15

ich, ob es sich fragen, ist möglich, eine optimale String-Generator Klasse nach der folgenden zweiten Gedanken Anforderungen umzusetzen:String Generation mit regex wie Kriterien


ich mit regulärem Ausdruck nicht wohl fühlen: Ich kann nicht mit einem Start Stück Code kommen, aber ich denke nur, einer naiven Implementierung, die eine TList als eine Basisklasse verwendet und einen Filter (Regex) gegen eine "brute force" erzeugte Zeichenkette verwendet.

Was sind die anderen optimalen Alternativen?


  • Reihenfolge: Zuerst durch Länge (kürzeste zuerst), dann lexikographisch.
  • Angabe des Zeichenbereichs, der bei der Generierung verwendet werden soll: Alle druckbaren oder jede mögliche Kombination aus [A-Z], [a-z], Zahlen, Sonderzeichen und eventuell Leerzeichen (Regex?).
  • String Länge begrenzt mit einem gegebenen Min/Max.
  • Raum der Suche mit Grenzen eingeschränkt: (regex) einen End-String mit der Möglichkeit der Filterung starten Zeichenfolge

Letzte Änderung

mit zu beginnen, umformuliert ich den Header mit Regex wie anstelle von Regex.

Ich erwäge zu revise die erste Voraussetzung wie es ist eine offene Tür, die-untractable Problem führen kann.

Ich brauche Anregungen und Hilfe für den richtigen Wortlaut.

Zweite Gedankenanforderungen bearbeiten getan. Noch offen für Vorschläge zur Verfeinerung.

+2

Da ich mit Delphi nicht vertraut bin, kann ich nur von einem allgemeinen Standpunkt aus sprechen. Der beste Weg, dies zu tun, wäre meines Erachtens, eine Regex in ein Diagramm zu zerlegen, das ihre äquivalente Zustandsmaschine darstellt (wikipedia sollte in der Lage sein, dort in die richtige Richtung zu weisen). Von dort können Wörter erzeugt werden, indem eine Tiefen-zuerst-Traversierung des Graphen durchgeführt wird (wobei berücksichtigt wird, dass es sehr wahrscheinlich zyklisch ist). Der Nachteil ist, dass wir keine Sprachen nutzen können, die in Regex-Unterstützung integriert sind. – jpm

+0

+1: Sehr interessant. Es ist ein wirklich generativer Ansatz, der meinem hochgradig uneffizienten Generator/Tester Weg widerspricht. Meiner Meinung nach gibt es überhaupt keinen Nachteil: Die eingebaute Regex-Unterstützung dient der Bewertung und das führt mich in die Irre, eine Lösung zu finden. Sie können erwägen, Ihren Kommentar als Antwort zu migrieren, ich finde es akzeptabel. Vielen Dank. – menjaraz

+2

Dies scheint eine dumme Möglichkeit zu sein, reguläre Ausdrücke zu verwenden. Ich denke, dass ein vereinfachtes Generator-Ausdruckssystem, das ein Generator-Objekt erzeugt, das einigen Regex-Features (die '[AZ] .'' und' [AZ] * '(bis zu einer festen Grenze) unterstützen, allein ähnlich wäre –

Antwort

3

ich dies tun würde, indem die minimalen Deterministic Finite Automaton für die Sprache zu konstruieren.Wenn Sie mit einer Regex beginnen, kann dies automatisch von Thompson's Construction gefolgt von Subset Construction und Minimierung erfolgen. Siehe zum Beispiel this description.

Let P = { < START, [""] > } be a set of pairs <State, list of strings> 
for n = 0, 1, ... Max 
    Let P' = {} be a new set 
    while P is not empty 
    Remove the pair <s, L> from P 
    For each transition s -- c --> t in alpha order of c 
     if t is an accepting state, 
      output l + c for each string l in L 
     put <t, L + c> in P' (** i.e. append c to each string in L) 
    end 
    Set P = P' 
end 

Beachten Sie, dass der Schritt markiert ** Bedürfnisse wahr sein Set Insertion, als Duplikate leicht nach oben zuschneiden können:

Mit einem DFA in der Hand, können Sie so etwas wie dieser Algorithmus verwenden.

Dies ist ein Kernalgorithmus. P kann mit der Ausgabelänge exponentiell wachsen, aber dies ist nur der Preis für die Verfolgung aller Möglichkeiten für eine zukünftige Ausgabezeichenfolge. Die von Ihnen erwähnten Einschränkungen für Auftrag, Größe und Platz können Sie sicherstellen, indem Sie die sortierte Reihenfolge in den Listen L beibehalten und die Suche abbrechen, wenn Ressourcenlimits erreicht werden.

bearbeiten

ist hier ein Spielzeug Java-Beispiel, wo ich hart, um die DFA für einfache binäre Gleitkomma-Literale mit optionalen Minuszeichen codiert haben. Dies verwendet ein etwas anderes Schema als der Pseudocode, um eine strikte sortierte Reihenfolge der Ausgabe zu erhalten und Zeichenbereiche zu berücksichtigen.

import java.util.Comparator; 
import java.util.TreeSet; 

public class Test{ 

    public static class DFA { 

     public static class Transition { 

      final int to; 
      final char lo, hi; // Character range. 

      public Transition(int to, char lo, char hi) { 
       this.to = to; 
       this.lo = lo; 
       this.hi = hi; 
      } 

      public Transition(int to, char ch) { 
       this(to, ch, ch); 
      } 
     } 

     // transitions[i] is a vector of transitions from state i. 
     final Transition [] [] transitions; 

     // accepting[i] is true iff state i is accepting 
     final boolean [] accepting; 

     // Make a fresh immutable DFA. 
     public DFA(Transition [] [] transitions, boolean [] accepting) { 
      this.transitions = transitions; 
      this.accepting = accepting; 
     } 

     // A pair is a DFA state number and the input string read to get there. 
     private static class Pair { 
      final int at; 
      final String s; 

      Pair(int at, String s) { 
       this.at = at; 
       this.s = s; 
      } 
     } 

     // Compare pairs ignoring `at` states, since 
     // they are equal iff the strings are equal. 
     private Comparator<Pair> emitOrder = new Comparator<Pair>() { 
      @Override 
      public int compare(Pair a, Pair b) { 
       return a.s.compareTo(b.s); 
      } 
     }; 

     // Emit all strings accepted by the DFA of given max length. 
     // Output is in sorted order. 
     void emit(int maxLength) { 
      TreeSet<Pair> pairs = new TreeSet<Pair>(emitOrder); 
      pairs.add(new Pair(0, "")); 
      for (int len = 0; len <= maxLength; ++len) { 
       TreeSet<Pair> newPairs = new TreeSet<Pair>(emitOrder); 
       while (!pairs.isEmpty()) { 
        Pair pair = pairs.pollFirst(); 
        for (Transition x : transitions[pair.at]) { 
         for (char ch = x.lo; ch <= x.hi; ch++) { 
          String s = pair.s + ch; 
          if (newPairs.add(new Pair(x.to, s)) && accepting[x.to]) { 
           System.out.println(s); 
          } 
         } 
        } 
       } 
       pairs = newPairs; 
      } 
     } 
    } 

    // Emit with a little DFA for floating point numbers. 
    public void run() { 
     DFA.Transition [] [] transitions = { 
      { // From 0 
       new DFA.Transition(1, '-'), 
       new DFA.Transition(2, '.'), 
       new DFA.Transition(3, '0', '1'), 
      }, 
      { // From 1 
       new DFA.Transition(2, '.'), 
       new DFA.Transition(3, '0', '1'), 
      }, 
      { // From 2 
       new DFA.Transition(4, '0', '1'), 
      }, 
      { // From 3 
       new DFA.Transition(3, '0', '1'), 
       new DFA.Transition(4, '.'), 
      }, 
      { // From 4 
       new DFA.Transition(4, '0', '1'), 
      } 
     }; 
     boolean [] accepting = { false, false, false, true, true }; 
     new DFA(transitions, accepting).emit(4); 
    } 

    public static void main (String [] args) { 
     new Test().run(); 
    } 
} 
+0

Ich habe gerade dieses interessante PHP-Projekt auf Github gefunden und kann es hier nicht teilen: [ReverseRegex] (https://github.com/icomefromthenet/ReverseRegex). – menjaraz

4

alte Frage, aber niemand hat es beantwortet, ist die Prämie noch aktiv, und ich habe bereits eine Lösung bereit, so ist hier eine mögliche Antwort:

Ich schrieb einmal ein little program, das dies tut.Es ist jedoch in C++/Qt (obwohl ich fast alle meine Programme in Delphi schreiben, dass man in C++), bietet keine Unterstützung für Unicode und macht keine Ordnung

garantiert

es wie folgt funktioniert:

  1. Alle? {} + * |() Operatoren werden erweitert (bis zu einer maximalen Grenze), so dass nur Zeichenklassen und Rückreferenzen übrig bleiben.

    z.B. [a-c]+|t*|([x-z]){2}foo\1|(a|b)(t|u) wird [a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z][x-z])foo\1|at|au|bt|bu

    (die | in letztere Ausdruck nur Notation sind, das Programm jede alternative subregex hält in einer Liste)

  2. Rückverweise auf mehrere Zeichen von Rückreferenzierungen auf einzelne Zeichen ersetzt werden.

    z.B. Der obige Ausdruck wird [a-c]|[a-c][a-c]|[a-c][a-c][a-c]|[a-c][a-c][a-c][a-c]||t|tt|tt|ttt|ttt|([x-z])([x-z])foo\1\2|at|au|bt|bu

    Jetzt entspricht jeder alternative Unterregex eine Zeichenfolge fester Länge.

  3. Für jede der Alternativen sind alle Kombinationen von gedruckten Zeichen aus den Klassen Kommissionierung:

    z.B. der obige Ausdruck wird a|b|c|aa|ba|..|cc|aaa|baa|...|ccc|aaaa|...|cccc||t|tt|tt|ttt|ttt|xxfooxx|yxfooyx|...|zzfoozz|at|au|bt|bu

Sie wahrscheinlich shortlex um Garantien wie folgt hinzufügen:

  1. Sortieren Sie die Zeichen in den Klassen alphabetisch

  2. sortieren die erhaltenen Alternativen in Schritt 2 oben für Länge

    (es gibt exponentiell viele Alternativen tiven, aber in der Regel ihre Anzahl ist vernachlässigbar im Vergleich zu der Anzahl der resultierenden Strings)

  3. Sort/exchange die Charakterklassen und Rückreferenzierungen, so dass alle Bezugspunkte rückwärts

  4. die möglichen Zeichenketten für eine einzelne Fest Aufzählen Längenalternative wie zuvor, aber beginnen Sie bei der letzten Zeichenklasse, anstatt zuerst alphabetisch zu sortieren.

    (dies würde nicht funktionieren, nach vorne gerichtet, ob es irgendwelche Rückreferenzierungen waren)

  5. Wenn es mehrere Alternativen von gleicher Länge sind, aufzuzählen, sie in „parallel“, vergleichen Sie die aktuellen Strings und drucken die alphabetisch niedrigsten . (d. h. Zusammenführen der bereits sortierten Listen für jede Alternative.)

    Dies kann z.B. durch das Erkennen von unterschiedlichen Präfixen und sicheren Zeichenklassen in dem Suffix, die aufgezählt werden können, ohne die Reihenfolge zu beeinflussen. (Zum Beispiel ein [a-z] | b [a-z] hat verschiedene Präfixe und die [a-z] kann ohne Vergleiche aufgezählt werden)