Buchstabenverdreher meine lösung

Code-Stücke können hier veröffentlicht werden.
Antworten
Sarapheus
User
Beiträge: 7
Registriert: Montag 20. August 2018, 22:13

Hey, ich habe vor kurzem mit Python angefangen und mir zur Aufgabe gemacht ein paar Codesnippets selber nachzuschreiben.
hier meine Lösung für einen Buchstabenverdreher, bei dem der Letzte und der Erste Buchstabe eines Wortes gleich bleiben die "Inneren" jedoch zufällig vertauscht werden.

Über Tipps und Kritik wäre ich sehr Dankbar :)

Code: Alles auswählen

import random
random.seed

wort = input ("Geben sie ein Wort ein: ")
neues_wort = ""
buchstabenliste = []

for buchstabe in range(len(wort) ):
    if buchstabe == 0:
        neues_wort += wort[buchstabe]
    elif buchstabe != len(wort) -1:
        buchstabenliste.append(wort[buchstabe])
        print(buchstabenliste)
    if buchstabe == len(wort) -1:####
        for anordnung in range(len(buchstabenliste)):
            zufall = random.randint(0,len(buchstabenliste) -1)
            neues_wort += buchstabenliste[zufall]
            buchstabenliste.remove(buchstabenliste[zufall])
        neues_wort += wort[buchstabe]

print(neues_wort)
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sarapheus: Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Wenn man dann noch das mit dem Buchstaben verdrehen in eine eigene Funktion verschiebt, kann man das Modul auch importieren und diese Funktion interaktiv oder programmatisch testen. Zum Beispiel in einer Python-Shell oder mit Unittests.

Dann kann man beispielsweise schnell feststellen, dass die Funktion einen Fehler hat:

Code: Alles auswählen

In [40]: buchstaben_mischen("a")                                                
Out[40]: 'aa'
Da sollte nur "a" bei heraus kommen, würde ich mal sagen.

Das ``random.seed`` hat keinen Effekt. Es hätte selbst dann nicht wirklich einen Effekt wenn man diese Funktion tatsächlich aufrufen würde, denn das passiert auch schon automatisch wenn das `random`-Modul das erste mal importiert wird. Wo immer Du das her hast, das scheint von einem Programmierer geschrieben worden zu sein der das so von woanders kennt.

Grunddatentypen haben in Namen nichts zu suchen. Es kommt öfter vor das man den Datentyp im Laufe der Programmentwicklung mal ändert, dann hat man entweder falsche, irreführende Namen im Programm, oder man muss überall alle betroffenen Namen ändern. Das macht nur unnötig Arbeit und ist fehleranfällig.

Hinter dem Namen `buchstabe` vermutet der Leser einen Buchstaben, aber keinen Index.

Der Name `anordnung` wird nicht verwendet. Die Schleife dient ja nur dazu den Code eine bestimmte Anzahl mal auszuführen. Wenn man so eine Variable nicht braucht, ist die Konvention sie `_` zu nennen, damit der Leser weiss dass die nicht verwendet wird, und sich nicht wundert ob der Code fehlerhaft oder unvollständig ist.

Wenn man bei `randint()` grundsätzlich 1 vom zweiten Argument abzieht, will man eigentlich `randrange()` verwenden.

Das ``buchstaben.remove(buchstaben[zufall])`` ist unnötig ineffizient. `remove()` sucht den Index von dem Wert der übergeben wurde, aber diesen Index kennen wir ja schon: `zufall`. ``del buchstaben[zufall]`` wäre da der direkte Weg.

Zwischenbilanz:

Code: Alles auswählen

def buchstaben_mischen(wort):
    neues_wort = ""
    buchstaben = []
    for i in range(len(wort)):
        if i == 0:
            neues_wort += wort[i]
        elif i != len(wort) - 1:
            buchstaben.append(wort[i])
        if i == len(wort) - 1:
            for _ in range(len(buchstaben)):
                j = random.randrange(0, len(buchstaben))
                neues_wort += buchstaben[j]
                del buchstaben[j]
            neues_wort += wort[i]
    
    return neues_wort
Was hier furchtbar unübersichtlich ist, ist das *alles* in dieser ``for``-Schleife steckt. Auch die Behandlung vom ersten und letzten Buchstaben die sich ja vom Rest unterscheidet. Aus dem Code wird nur mit Mühe ersichtlich das es eigentlich: Erster Buchstabe + gemischte Mitte + letzter Buchstabe ist. Wobei erster und letzter Buchstabe eben nicht wirklich *in* die Schleife hinein gehört.

Wenn man das nacheinander macht und die ``for``-Schleife über die Anzahl der Mittelbuchstaben durch eine einfacherere ``while``-Schleife ersetzt, kann das so aussehen:

Code: Alles auswählen

def buchstaben_mischen(wort):
    neues_wort = ""
    
    mittel_buchstaben = []
    for i in range(1, len(wort) - 1):
        mittel_buchstaben.append(wort[i])
    
    while mittel_buchstaben:
        j = random.randrange(0, len(mittel_buchstaben))
        neues_wort += mittel_buchstaben.pop(j)
    
    return wort[0] + neues_wort + wort[-1]
Das erstellen der `mittel_buchstaben` kann man durch „slicing“ und `list()` deutlich abkürzen, und statt die Buchstaben dann einzeln zufällig zu wählen und aus der Liste zu entfernen, kann man auch einfach `random.shuffle()` zum mischen des Listeninhalts verwenden, womit das hier übrig bleibt:

Code: Alles auswählen

def buchstaben_mischen(wort):
    mittel_buchstaben = list(wort[1:-1])
    random.shuffle(mittel_buchstaben)
    wort = wort[0] + "".join(mittel_buchstaben) + wort[-1]
    return wort
Da ist immer noch der Fehler aus der ursprünglichen Funktion enthalten ("a" → "aa") und ein neuer — diese Funktion kommt nicht mit der leeren Zeichenkette als eingabe klar. Beides lässt sich aber leicht beheben und das überlasse ich mal als Aufgabe. 🙂
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Anstatt shuffle() könnte man hier auch sample() nehmen:

Code: Alles auswählen

from random import sample

def get_mixed(word):
    if len(word) <= 2:
        return word
    mixed = sample(word[1:-1], len(word) - 2)
    return word[0] + ''.join(mixed) + word[-1]
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@snafu: Würde ich nicht machen. Ist komplexer im Aufruf und ein Sample das grundsätzlich genau so gross ist wie die Population ist irgendwie ein recht degenerierter Fall von „Sample“.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Mag sein, spart aber den list()-Aufruf und wird übrigens auch in der Doku so empfohlen:
To shuffle an immutable sequence and return a new shuffled list, use sample(x, k=len(x)) instead.
https://docs.python.org/3/library/rando ... om.shuffle

Aber man muss natürlich nicht alles machen, was die Doku sagt. Beide Ansätze haben IMHO ihre Berechtigung und ob das vom "philosophischen" Standpunkt her ein ungewöhnlicher Einsatz eines Samples ist, spielt doch für das Ergebnis keine Rolle. Ist halt etwas ungünstig, dass shuffle() nur als Inplace-Operation verfügbar ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Das `list()` spart man nur selbst, ausgeführt wird es in `sample()` dann trotzdem. Da würde ich mir eher eine `shuffled()`-Funktion schreiben, analog zu `sorted()`, die dann auch mit beliebigen endlichen iterierbaren Objekten klar kommt.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sarapheus
User
Beiträge: 7
Registriert: Montag 20. August 2018, 22:13

Vielen dank für die Antworten, ich setze mich gleich mal ran und optimiere den code :)
LG
Antworten