Regulären Ausdruck in Python prüfen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Ich habe noch eine kleine Verständnisfrage:

Wenn ich die Konstante ^a(a|b|c) *b$ in der Match Zeile manuell eingebe, dann gibt mir die Methode richtigerweise True zurück. Jetzt habe ich den Code wie folgt geschrieben:

Code: Alles auswählen

import re

class Rega:
    
    def __init__(self,alphabet, RA):
        self.alphabet = alphabet
        self.regulaerer_ausdruck = RA

    def print_alphabet(self):
        for i in self.alphabet:
            print(i)

    def regulaerer_ausdruck(self):
        return self.regulaerer_ausdruck

    def passendes_wort(self, wort):
        if re.match(ra.regulaerer_ausdruck, wort) == None:
            return False
        else:
            return True

ra = Rega(["a","b","c"], "^a(a|b|c) *b$")

ra.print_alphabet()

print(ra.regulaerer_ausdruck)
print(ra.passendes_wort("abcb"))
In der init Methode habe ich mir ja den regulären Ausdruck in self.regulaerer_ausdruck geschrieben. Diesen habe ich dann mit ra.regulaerer_ausdruck in der Match Zeile statt der Konstanten verwendet. Wenn ich das jetzt ausführe gibt er mir aber komischerweise False zurück. Normalerweise müsste doch aber auch True zurückkommen, weil der Ausdruck ja noch der gleiche ist, nur dieses Mal eben in einer Variablen steht. Übersehe ich da etwas?
Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

Dein Regulärer Ausdruck ist ja jetzt auch ein anderer.

RA ist kein sinnvoller Name für einen Parameter. Benutze keine Abkürzungen. Konvention ist auch, dass Variablennamen klein geschrieben werden. regulaerer_ausdruck wäre besser.
Die Methode `regulaerer_ausdruck` ist quatsch. Zum einen sollten Methodennamen eine Tätigkeit sein, weil sie ja was machen, zum anderen wird die Methode in __init__ von dem Attribut gleichen Namens überdeckt, zum dritten macht die Methode auch nichts sinnvolles, da sie nur etwas zurückgibt, was man auch direkt über Attributzugriff erreicht.

Was ist der Nutzen des Alphabets?
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Okay, ich werde es mir aneignen. Aber wieso ist der Ausdruck denn jetzt ein anderer? Ich habe ihn doch nur in eine Variable geschrieben? Den Nutzen des Alphabeths kann ich nicht sagen, wir sollen die Klasse nur als Übung programmieren.
Benutzeravatar
__blackjack__
User
Beiträge: 14013
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Unplayable: Der reguläre Ausdruck ist jetzt ein anderer weil es eben nicht mehr der gleiche ist. *Warum* Du den verändert hast, musst Du schon selbst wissen. Bezihungsweise passt der in Deinem ersten Beitrag ja schon nicht auf die Beispieldaten. Auch Leerzeichen haben eine Bedeutung.
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Vielen Dank! Habe auf das Leerzeichen gar nicht geachtet
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das ist so ziemlich das übelste Beispiel für OOP, das ich seit langem gesehen habe... :o
Benutzeravatar
snafu
User
Beiträge: 6854
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Was mir noch fehlt, ist eine genauere Auswertung:

Code: Alles auswählen

if ra.passendes_wort("abcb") == True:
    passt = True
else:
    pass
if ra.passendes_wort("abcb") == False:
    passt_nicht = True
else:
    pass
try:
    if passt == True:
        print("Passt")
    else:
        if passt_nicht == True:
            print("Passt nicht")
        else:
            pass
except:
    print("Fehler")
Zuletzt geändert von snafu am Mittwoch 9. Oktober 2019, 21:32, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 14013
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Nochmal zum Alphabet: Das sollte kein Argument sein und vom Programmierer nicht angegeben werden müssen. Die Informationen stecken ja bereits im Muster des regulären Ausdrucks, und von da sollte man sich das auch holen wenn man es braucht.

Code: Alles auswählen

#!/usr/bin/env python3
import re
from sre_parse import LITERAL, parse as sre_parse, SubPattern


def iter_literals(nodes):
    for node in nodes:
        if isinstance(node, (tuple, list, SubPattern)):
            if node[0] == LITERAL:
                yield chr(node[1])
            else:
                yield from iter_literals(node)


class RegularExpression:
    def __init__(self, pattern):
        self._regex = re.compile(pattern)

    @property
    def pattern(self):
        return self._regex.pattern

    @property
    def alphabet(self):
        return "".join(sorted(set(iter_literals(sre_parse(self.pattern)))))

    def matches(self, text):
        return self._regex.fullmatch(text) is not None


def main():
    regular_expression = RegularExpression("a(a|b|c)*b")
    for character in regular_expression.alphabet:
        print(character)
    print(regular_expression.pattern)
    print(regular_expression.matches("abcb"))


if __name__ == "__main__":
    main()
“The best book on programming for the layman is »Alice in Wonderland«; but that's because it's the best book on anything for the layman.” — Alan J. Perlis
Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

@snafu: Vorsicht, Anfänger könnten die Ironie darin nicht verstehen.
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Danke für die äußerst fachliche Hilfe! Aber ich habe es eben so als Arbeitsauftrag bekommen, an den ich mich halten soll, auch wenn das im Auge eines erfahrenen Programmierers keinen Sinn ergibt bzw keine gute Übung ist.
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Hallo nochmal,

in einem weiteren Teil der Aufgabe soll nun folgendes gemacht werden:

Ich soll alle Kombinationen aus den Elementen des Alphabets von Länge x bis Länge y erstellen. Man möchte also z.B. aus dem Alphabet a und b die Kombinationen mit Länge 1 bis 2 erstellen, was dann dieses Ergebnis bringt: "a","b","aa","ab","ba","bb" Anschließend soll ich alle diese generierten Wörter mit einem beliebigen regulären Ausdruck überprüfen. Ich habe mir mit der Rekursion immer sehr schwer getan, aber ich würde es zum Üben gerne noch ein Mal rekursiv machen.

Das hier ist mein Ansatz:

Code: Alles auswählen

def kombinationen_erstellen(startwert, endewert, zaehler, liste, alphabet):
    a = liste                                                                 # Kopie der übergebenen, leeren Liste
    ergebnis = []                                                         # Liste, in die passende Kombinationen geschrieben werden
    while zaehler  <= endewert:                                 # solange die Länge der Wörter kleiner als der Endewert
        for element in alphabet:                                 # gehe jedes Element im Alphabet durch
            a.append(element)                                     # füge das Element in die temporäre Liste ein
            ergebnis.append(element)                        # füge das Element in die Ergebnisliste ein
            kombinationen_erstellen(startwert, endewert, zaehler+1, liste, alphabet)    #rekursiver Aufruf mit zaehler + 1
            del a[-1]                                                         # letztes Element der temporären Liste löschen
            
    
    print(a)


kombinationen_erstellen(1,2,1,[],["a","b"])
Ich habe es ein Mal so weit versucht, wie ich es selbstständig hin bekomme. Jedoch weiß ich nicht, wie ich jetzt vorgehen muss, um weitere Elemente anzuhängen
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du versuchst hier zwei Dinge gleichzeitig zu tun, und das geht in die Hose. Schreib eine Funktion die in der Lage ist, aus einem gegebenen Alphabet alle kartesischen Produkte (so nennen sich diese Kombinationen) mit einer gegebenen Laenge zu erzeugen. Das ist rekursiv, und einfach: die Funktion produkt(n, alphabet) ist die Kombination aus allen Worten aus produkt(n-1, alphabet) plus allen Elementen aus dem Alphabet. Die Ergebnisse dieser Funktion kannst du optimalerweise per "yield" nach aussen reichen, zur Not aber auch in einer riesen Liste aufsammeln.

Und danach baust du einfach ne for-schleife die deine gewuenschten n von x bis y zusammenpappt.

Und ein Tipp gibt's obendrauf: ich programmiere seit ~20 Jahren Python. Und in dieser ganzen Zeit kann ich an einer Hand abzaehlen, wie oft ich "del" benutzen musste. Wann immer du also versucht bist etwas dadurch zu machen, dass auf bestehenden Listen insbesondere durch loeschen arbeitet, solltest du innehalten und es anders machen. Das ist eigentlich niemals noetig.
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Hey, danke erstmal für die Antwort. Ich wollte es ja auch so machen wie du gesagt hast und das oben gezeigte Programm soll ja zuerst auch die ganzen Kombinationen generieren. Aber ich scheitere eben schon daran. Für mich ist es schwer zu verstehen wie ich nun weiter verfahren muss
Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

Bei Rekursion mußt Du zwei Fragen beantworten: wie sieht der Start aus? Und wie kommt man von Schritt n auf Schritt n+1? Beschreibe das mal erst mit eigenen Worten.
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Also dann würde ich es so beschreiben:

Zu Beginn habe ich eine leere Liste. Außerdem habe ich n Elemente im Alphabet. Nun schreibe ich jedes Element im Alphabet in eine Teilliste und eine Ergebnisliste. Die Teilliste ist immer dann voll, wenn len(teilliste) == der Zählvariable ist. Diese zählt ja von x bis y hoch. Wenn das der Fall ist, wandert die Liste in die Ergebnisliste und ich habe diese Kombination fertig. Dann muss ich an die Teilliste ja die anderen Elemente aus dem Alphabet anhängen.

So hätte ich es beschrieben
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du wirst nicht weit kommen mit diesem Ansatz, in dem du Listen versuchst zu *veraendern*. Ich habe dir das oben auch schon fuer einen anderen Fall beschrieben, bei dem du del verwandt hast. Es ist so gut wie immer falsch, datenstrukturen so massiv zu manipulieren. Und solange du den Moment wann das dann doch mal ok ist nicht selbst erkennst, solltest du immer so vorgehen, dass du *neue* Datenstrukturen schaffst.

Fuer dein konkretes Problem heisst das: statt zu versuchen eine Liste zu manipulieren, bis sie eine Laenge hat, baust du immer NEUE Listen.

Fangen wir mal so an: nehmen wir mal an, du hast eine magische Funktion "magische_loesung", und die loest dir dein Problem fuer n-1 lange Sequenzen deines Alphabetes a. Wie baust du dann die Funktion kartesisches_produkt fuer n?

Code: Alles auswählen

def kartesisches_produkt(n, a):
       ... # irgendwo kommt ein aufruf magische_loesung(n-1, a)
       ...
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Okay, wir haben es eben so beigebracht bekommen, dass wir immer das letzte angefügte Element löschen. Irgendwie kommt es mir so vor als würde jeder das anders machen als wir im Unterricht :?

Ich habe jetzt an folgendes gedacht:

Code: Alles auswählen

def kartesisches_produkt (n, a):
        while n >= 0:
                magische_loesung(n-1), a)

        return ergebnis

Sirius3
User
Beiträge: 18255
Registriert: Sonntag 21. Oktober 2012, 17:20

Was meinst Du mit dem "immer" beim Löschen?
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das was du da geschrieben hast macht ja nicht wirklich was. Vor allem soll dein kartesisches produkt doch etwas zurueckliefern. Tut es aber nicht. ergebnis wird nicht definiert. Was du tun musst, wenn du die Loesungen fuer n generieren willst, und irgendwie die Loesungen fuer n-1 bekommst, ist

- ueber jeden Buchstaben des Alphabets iterieren
- und den an jede Loesung des Problems mit n-1 anhaengen.
- all die dadurch entstandenen len(alphabet) * len(alle_loesungen_n-1) Loesungen zurueckgeben.

Code: Alles auswählen

def kartesisches_produkt(n, alaphabet):
    ergebnis = []
    for buchstabe in alphabet:
        for loesung in magische_loesungen(n-1, alphabet):
            ergebnis.append([buchstabe] + loesung)
    return ergebnis
Und jetzt ist die Frage: wie schreibt man die magische_loesung?
Unplayable
User
Beiträge: 51
Registriert: Mittwoch 24. Februar 2016, 22:09

Kurze Verständnisfrage: Wieso denn genau n-1? Das verstehe ich noch nicht wirklich
Antworten