Sonntag, 3. März 2013

Name That Cow

Aus dem OpenClipart-Project
Das Name That Cow“-Problem klingt recht spannend, allerdings ist ein Problem, dass mir die Datentabelle mit den 5.000 „acceptable cattle names“ nicht zur Verfügung steht.

Problembeschreibung

Auf einer texanischen Rinderfarm soll für die Seriennummer einer Kuh eine Liste weiblicher Vornamen vorgeschlagen werden, wobei die Übersetzung sich am Tastenfeld eines Telefons orientiert, d. h. für die Zahl 2 kann im Vornamen der Buchstabe A, B oder C stehen.

Ich verwende meine Telefontastatur, wie ich sie auf meinem Telefon vorfinde.

1 2
ABC
3
DEF
4
GHI
5
JKL
6
MNO
7
PQRS
8
TUV
9
WXYZ

Diese Änderung sollte unproblematisch sein. Die richtige Funktionsweise meines Programms kann später über die Tastatur überprüft werden. Wirft mir das Programm für den Code 23353 z. B. den Vornamen Adele aus, dann kann ich prüfen, ob die Buchstaben jeweils in den den Zahlen zugeordneten Buchstabenlisten enthalten sind.

Folglich stellt sich folgendes Problem: Schreibe ein Programm das für die Nummer eines Rinds alle validen Namen ausgibt, die für diese Nummer generiert werden können. Sonst lautet die Ausgabe „No matching names found“. Seriennummern dürfen maximal 9 Zahlen lang sein. Das Programm endet, wenn eine 0 eingegeben wird.

Vorüberlegungen

Die Aufgabenstellung führt zu folgenden Vorüberlegungen:
  • Die Seriennummer eines Rindes darf keine 0 oder 1 enthalten, weil ich diesen Ziffern keinen Buchstaben zuweisen kann.
  • Die Verwendung des RE-Moduls bietet sich m. E. an. Die dynamische Generierung eines RE-Ausdrucks ist m. E. erforderlich. (Ging einfacher)
  • Möglicherweise macht es Sinn die Vornamen in Listen entsprechend der Länge aufzuteilen. Das könnte helfen die Performanz zu verbessern.

Datendatei aufbauen

Ich habe mir - leider manuell - aus der Wikipedia eine Liste mit - laut Wikipedia - 1.761 weiblichen Vornamen (Stand März 2013) geholt und aufbereitet. Aus dieser Liste soll das Programm geeignete Namen auswählen.

Die Vornamensliste wird aus dem Quelltext der Kategorienseiten heraus aufgebaut:
with open("Männlicher Vorname.txt") as f, open("Männliche Vornamen.txt","w") as g:

    for line in f:
        
        line = line.replace("\n","")
        # title="Zsuzsanna"
        if "title" in line:

            line = line.split('title="')[1]
            vorname = line.split('"')[0]

            vorname = vorname.replace(" (Vorname)","")
            vorname = vorname.replace(" (Name)","")

            print(vorname)
            vorname += "\n"
            g.write(vorname)
Das war gerade etwas Handarbeit, aber noch im Rahmen und einfacher und schneller als sich beim Pywikipediabot einzulesen.

Quelle: Deutschsprachige Wikipedia (2. März 2013)


Die entsprechende Übersicht mit denn männlichen Vornamen bietet - laut Wikipedia - 3.390 männliche Vornamen (Stand März 2013).

Quelle: Deutschsprachige Wikipedia (2. März 2013)


Aus beiden Listen kann man die benötigte Vornamensliste aufbauen.

Ein- und Ausgabe

Die Eingabe stellt man sich bei HP in etwa in folgender Form vor:
Program Input
232
252473
727225
0

Program Output
Possible names for #232 are: Ada
Possible names for #252473 are: Blaise, Claire
Possible names for #727225 are: Pascal
Sieht leistbar aus.

Mein Programm

Das re-Modul kommt hier nicht zum Einsatz, weil es Treffer für Strings findet, nicht aber in Listen. Eigentlich hätte ich gerne alle Listenelemente bekommen und muss daher - gefühlt - einen kleinen Umweg nehmen.
# Code wars IV
# Problem 9: Name That Cow


def codeliste_aufbauen(data):
    ''' Bekommt die Daten und gibt
    einzelne Codes in einer Liste zurück

    '''
    data = data.replace(" ","")
    data = data.split("\n")
    if data[-1] == "0":
        data.pop()

    return data


def ergebnis_ausgeben(code,namensliste):
    ''' Erledigt die Ausgabe in der Konsole

    '''
    if namensliste:        # Wahr, wenn die Liste nicht leer ist!
        print("Possible names for #{} are: {}.\n".format(code,", ".join(namensliste)))
    else:
        print("No matching names found for #{}.\n".format(code))


def vornamenliste_aufbauen():
    ''' Holt sich die Daten aus den Vornamenslisten

    Offene Probleme:
    [  ]   Es werden alle Namen geholt, nicht nur die, die
           auch möglich sind, z. B. < 10 oder ohne Leerzeichen
           und ohne Sonderzeichen
    '''
    vornamen = []
    dateien = ["Männliche Vornamen.txt","Weibliche Vornamen.txt"]
    
    for datei in dateien:
        with open(datei) as f:
            for line in f:
                line = line.replace("\n","")
                vornamen.append(line)
    vornamen.sort()

    # Trainingsmaterial
    '''
    Wähle code = 382 -> EVA
    vornamen = ['Bob', 'Eda', 'Eda', 'Eka', 'Eka', 'Eva', 'Ewa', \
                'Ida', 'Iga', 'Ina', 'Ina', 'Ira', 'Ira', 'Isa', \
                'Isa', 'Iva', 'Lea', 'Lia', 'Mia', 'Nea', 'Nia', \
                'Noa', 'Néa', 'Oda', 'Ola', 'Ola', 'Ona', 'Oya', \
                'Pia', 'Ria', 'Rob', 'Uta']
    '''
    return vornamen


def treffer_suchen(code,vornamen):
    ''' Suche alle Treffer in der Vornamensliste für
    den dekodierten code und gibt die Liste zurück
    '''
    codeliste = {"2":["A","B","C"],
                 "3":["D","E","F"],
                 "4":["G","H","I"],
                 "5":["J","K","L"],
                 "6":["M","N","O"],
                 "7":["P","Q","R"],
                 "8":["T","U","V"],
                 "9":["W","X","Y","Z"]}

    results = [] # Nimmt die Ergebnisse auf
    
    if "1" in code or "0" in code:
        # Der 1 sind keine Buchstaben zugeordnet
        return []
    else:

        for vorname in vornamen:

            match = True
            
            if len(vorname) != len(code):
                # Kann nicht passen
                pass
            elif " " in vorname:
                # Kann nicht passen
                pass
            else:
                for letter, i in zip(list(vorname),list(code)):
                    if not letter.upper() in codeliste[i]:
                        match = False
                        # Muss nicht weiterprüfen
                        break
                        # Nächster Vorname!
                    
                
                if match == True:
                    results.append(vorname)
        
        return results


def main():
    """ Mainfunktion

    """
    data = """114
    2662
    6886
    42664
    42662
    58473
    58472
    5642662
    382
    232
    252473
    727225
    6424235
    0"""

    # Übungsmaterial
    # data = """382"""

    # Daten aufbereiten
    codes = codeliste_aufbauen(data)
    vornamen = vornamenliste_aufbauen()

    # Treffer suchen
    for code in codes:
        treffer = treffer_suchen(code,vornamen)
        ergebnis_ausgeben(code,treffer)


if __name__ == "__main__":
    main()
Zu verbessern wäre hier wohl die Funktion treffer_suchen(code,vornamen). Mich würde interessieren, ob es eine bessere Lösung - z. B. mittels Datenbank oder re-Modul geben könnte. Eine Musterlösung von HP gibt es für die frühen Code wars leider noch nicht.

Verbesserungsvorschläge und Feedback

Ich habe im Python-Forum und zwar hier um Kritik gebeten und bekommen.

Keine Kommentare:

Kommentar veröffentlichen