Buchstabenverdreher

Code-Stücke können hier veröffentlicht werden.
Antworten
Milcheiweiß
User
Beiträge: 9
Registriert: Donnerstag 3. Oktober 2019, 12:34

Hi Ihr,
Ich habe die Aufgabe ein Programm zu schreiben das in einem Text bei allen Wörtern die Buchstaben zufällig vertauscht wobei der erste und der letzte Buchstabe gleich bleibt. Basiert auf einer Studie die besagt, das man es lesen kann solange der erste und letzte Buchstabe stimmt und sonst nur die anderen Buchstaben vetauscht. Also zB: "Es kmmot auf die Vrpunekacg an".

Ich dachte mir das ungefähr so:

Code: Alles auswählen

from random import shuffle
s = input("Give me a string: ")
print(s)
x = s.split()
print(x)
print(shuffle(x))
Da kommt am Ende nur None raus.
Ich weiß das so gar nicht rauskommen kann was ich brauche, aber ich habe keine Ahnung wie ich weiter machen soll.
Ich habe auch schon anderes ausprobiert, aber da kam ich auf nichts sinnvolles.

Ich dachte mir ich müsste es so machen:
1. eine Variabale einem Input geben (gemacht)
2.den Input in eine Liste aufteilen damit die Wörter einzeln anwählbar sind(gemacht)
3. die Wörter einzeln Anwählen und mit Variable[1:-1] dafür sorgen das der erste und letzte Buchstabe gleich bleibt.
4. die Buchstaben der angewählten Wörter zufällig vertauschen und als neue Liste speichern.
5.Die neue Liste drucken.

Kann man das irgendwie mit Python machen oder habe ich da schon einen Fehler in meiner Denkweise?

vielen Dank und Grüße,

M.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hast du dir mal die Dokumentation von shuffle angeschaut? Was gibt die denn zurueck, und was hat das in dem Moment fuer Konsequenzen fuer deinen Code?
Milcheiweiß
User
Beiträge: 9
Registriert: Donnerstag 3. Oktober 2019, 12:34

__deets__ hat geschrieben: Dienstag 8. Oktober 2019, 14:48 Hast du dir mal die Dokumentation von shuffle angeschaut? Was gibt die denn zurueck, und was hat das in dem Moment fuer Konsequenzen fuer deinen Code?
Wenn ich es richtig verstehe bringt der print(shuffle(x)) Befehl nichts.
Ich muss zuerst shuffle(x) anwenden und dann print(x)
die Liste x wird durch das shuffle verändert.

Aber wie schaffe ich es die Wörter einzeln anzuwählen und die Buchstaben zu shufflen?
Benutzeravatar
__blackjack__
User
Beiträge: 13167
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Milcheiweiß: Teil das Problem in kleinere Teilprobleme auf und die dann wieder, solange bis Du Teilprobleme hast, die Du mit wenigen Zeilen Code in einer Funktion lösen kannst. Das dann testen und nicht mit dem nächsten Teilproblem weitermachen, bevor das nicht funktioniert.

Und verwende gleich von Anfang an korrekte Namen. Richtige, ausgeschriebene Namen, nicht `s` oder `x`. Wenn `x` korrekt benannt wäre, wäre das direkt beim `shuffle()` schon aufgefallen, dass das offensichtlich falsch ist! Gute Namen sind keine Kosmetik die man später macht, sondern wirklich wichtig beim Programmieren.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Für mich stellt sich auch die Frage, ob du mit "Text" lediglich einfache Zeichenketten meinst, oder tatsächlich "echte" Texte. Letzteres ist deutlich schwieriger, da es bestimmte Randfälle gibt (zum Beispiel Satzzeichen, Zahlen, Abkürzungen, etc.), an die man denken muss. Um das mal an einem Satz aus dem englischen Wikipediaartikel über Python zu illustrieren:

Code: Alles auswählen

In [1]: from random import shuffle                                                                                                                            

In [2]: def scrable(word): 
	...                                                                                         

In [3]: text = "The Python 2 language, i.e. Python 2.7.x, is \"sunsetting\" on January 1, 2020, and the Python team of volunteers will not fix security issues
   ...: , or improve it in other ways after that date. With the end-of-life, only Python 3.6.x and later, e.g. Python 3.8 which should be released in October 
   ...: 2019 (currently in beta), will be supported."                                                                                                         

In [4]: scrabled_text = ...                                                                                     

In [5]: print(text)                                                                                                                                           
The Python 2 language, i.e. Python 2.7.x, is "sunsetting" on January 1, 2020, and the Python team of volunteers will not fix security issues, or improve it in other ways after that date. With the end-of-life, only Python 3.6.x and later, e.g. Python 3.8 which should be released in October 2019 (currently in beta), will be supported.

In [6]: print(scrabled_text)                                                                                                                                  
                                                                                                                           
The Pthyon 2 lgaaugne, ie.. Pyothn 2..7x, is "tgnisentus" on Juaarny 1, 2200, and the Pothyn taem of voetluenrs wlil not fix stuicery isssue, or imvopre it in oehtr ways atfer taht daet. With the en-filedf-o, olny Phtoyn 36..x and lerat, eg.. Pytohn 3.8 whcih sluohd be reeealsd in Ocbtoer 2019 (ctnrleruy in be)at, will be sdporeupt.



Im günstigsten Fall würde man den Text daher zuvor tokenisieren. Für eine einfache Programmieraufgabe ist das aber vermutlich overkill.
Benutzeravatar
__blackjack__
User
Beiträge: 13167
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Im Fall von solchem Text lohnt es sich `re.sub()` anzuschauen und daran zu denken das man für das Ersetzungsargument auch eine Funktion angeben kann.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

__blackjack__ hat geschrieben: Mittwoch 9. Oktober 2019, 13:23 Im Fall von solchem Text lohnt es sich `re.sub()` anzuschauen und daran zu denken das man für das Ersetzungsargument auch eine Funktion angeben kann.
Das würde mich ja jetzt schon interessieren, was dir das genau vorschwebt, aber wir wollen ja nicht zu viel verraten... ;)

Das Ergebnis mit Tokenizer aber ohne regex sähe jedenfalls schon eher so aus, wie man sich das bei "normalem" Text so vorstellt:

Code: Alles auswählen

In [9]: print(scrambled_text)                                                                                                                                 
The Ptohyn 2 laagnuge, i.e. Pthoyn 2.7.x, is "sstnitnueg" on Jaanruy 1, 2020, and the Pytohn team of vuetoerlns will not fix sruticey iusses, or ivomrpe it in oehtr wyas atfer that date. Wtih the end-of-lfie, olny Pyohtn 3.6.x and laetr, e.g. Ptoyhn 3.8 wihch suhold be reeaesld in Obcoter 2019 (crlrtuney in btea), will be stpopreud. 
Zuletzt geändert von nezzcarth am Mittwoch 9. Oktober 2019, 19:16, insgesamt 1-mal geändert.
Benutzeravatar
__blackjack__
User
Beiträge: 13167
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Mir schwebte so etwas vor wie ``scrambled_text = re.sub('pattern für worte', scramble_matched_word, text)`` mit einer Funktion `scramble_matched_word()` die ein ”Match”-Objekt entgegennimmt und ein ”buchstabenverdrehtes Wort” zurück gibt.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Sirius3
User
Beiträge: 17781
Registriert: Sonntag 21. Oktober 2012, 17:20

So ähnlich wie der Zahlendreher:

Code: Alles auswählen

re.sub("[0-9]+", lambda m: m.group(0)[::-1], "Das Jahr hat 365 Tage und der Tag 24 Stunden.")
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Jetzt habe ich verstanden, was du/ihr mein(s)t. Ist jedenfalls etwas "leicht-gewichtiger" als mein Ansatz :)
Milcheiweiß
User
Beiträge: 9
Registriert: Donnerstag 3. Oktober 2019, 12:34

Also ich habe es jetzt für das erste Wort geschafft:

Code: Alles auswählen

from random import shuffle
text = input("Give me a string: ")
print(text)

text_list = text.split()
print(text_list)
print("".join(text_list))
text_list_0 = text_list[0]
print(text_list_0)

text_list_0_split = list(text_list_0)
print(text_list_0_split)
text_list_0_split_innen = text_list_0_split[1:-1]
print(text_list_0_split_innen)
shuffle(text_list_0_split_innen)
print(text_list_0_split_innen)

text_list_0_split_innen = "".join(text_list_0_split_innen)

text_shuffled_0 = [text_list_0_split[0], text_list_0_split_innen, text_list_0_split[-1]]
print(text_shuffled_0)
text_shuffled_0_joined = "".join(text_shuffled_0)
print(text_shuffled_0_joined)
Jetzt habe ich die Idee das mit einer for Schleife für alle zu machen und dann am Ende alles auszudrucken so das ich einen Satz habe.
Jatzt stecke ich aber fest weil ich das nicht so einfach in eien for Schleife übertragen kann.
Hat jemand eine Idee wie ich da weitermachen sollte?
Sirius3
User
Beiträge: 17781
Registriert: Sonntag 21. Oktober 2012, 17:20

Erster Schritt wäre, eine Funktion zu schreiben, die genau ein Wort verdreht.

Dazu solltest Du bessere Variablennamen wählen: was ist `text_list_0`? Ich würde sagen, das ist ein Wort. Was ist `text_list_0_split_innen`? Das Wortinnere. Usw. Durch diese kleinen Änderungen ist der Code schon viel lesbarer.
`text_shuffled_0_joined` ist dann einfach das verwürfelte Wort.
Milcheiweiß
User
Beiträge: 9
Registriert: Donnerstag 3. Oktober 2019, 12:34

Ich habe es jetzt mit einer for Schleife bis zu einem, denke ich, guten Punkt gebracht:

Code: Alles auswählen

from random import shuffle
import random
text = input("Give me a string: ")
print(text)

text_list = text.split()
print(text_list)
print("".join(text_list))

for i in text_list:
    if i[-1] == "!":
        innen = ''.join(random.sample(i[1:-2], len(i)-3))
        all = i[0], innen, i[-2:-1]
    else:
        innen = ''.join(random.sample(i[1:-1], len(i)-2))
        all = i[0], innen, i[-1]

    all_word = "".join(all)
    print(all_word)
meine Strategie wäre jetzt alle Wörter in eine Liste zu bekommen und sie danach wieder mit dem join Befehl zu einem Text zu machen.
Hat jemand eine Idee wie ich die Wörter alle in eine Liste bekomme?
Benutzeravatar
__blackjack__
User
Beiträge: 13167
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Milcheiweiß: Bei den Namen geht noch was. Grundatentypen sollten nicht in Namen vorkommen. `text_list` wäre ja auch eher treffender `words`. Und `i` für eine Laufvariable die keine ganze Zahl ist, geht gar nicht. Das ist ein `word`.

Das ``if``/``else`` hat in beiden Zweigen fast das gleiche stehen und nur auf "!" zu testen ist ein bisschen wenig. Was ist mit ".", "?", ","? Mindestens.

Listen haben eine `append()`-Methode. Damit kann man Elemente an eine Liste anhängen, also zum Beispiel Wörter in eine Liste bekommen.

Du solltest das wirklich auf kleinere Teilprobleme aufteilen und die dann jeweils mit Funktionen lösen statt das alles als einem, nicht wirklich leicht testbaren, Codeklumpen runterzuhacken.

Edit: Das mit dem `random.sample()` ist Unsinn. Das ist total unübersichtlich das zu verwenden und dann da auch noch das zweite Argument richtig zu berechnen zu müssen.

Was Du auf jeden Fall auch testen/berücksichtigen solltest ist das es auch Sprachen gibt, mit Worten die nur *ein* Zeichen lang sind. Macht Dein Code da das richtige?
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
Milcheiweiß
User
Beiträge: 9
Registriert: Donnerstag 3. Oktober 2019, 12:34

Hi Ihr,
vielen Dank für eure Hilfe bisher.
Ich habe jetzt etwas womit ich so gut wie fertig bin.
Ich weiß es ist zu umständlich und die Benennung der Variablen könnte besser sein.
Es läuft auf jeden Fall.
Nur habe ich das gefühl das er niemals zu dem Else kommt und sich immer nur in dem if und dem elif bewegt.
Wörter mit 4 Buchstaben werden nie geändert und bei längeren bleiben immer die letzten zwei Buchstaben gleich...
Kann mir jemand sagen warum das so ist?

Code: Alles auswählen

from random import shuffle
import random
text = input("Give me a string: ")
print(text)

text_list = text.split()
print(text_list)
print("".join(text_list))
word_list = []
for i in text_list:
    if len(i) < 3:
        word_list.append(i)
    elif i[-1] == "!" or "." or "?" or ",":
        innen = ''.join(random.sample(i[1:-2], len(i)-3))
        all = i[0], innen, i[-2:]
        all_word = "".join(all)
        print(all_word)
        word_list.append(all_word)
    else:
        innen = "".join(random.sample(i[1:-1], len(i)-2))
        all = i[0], innen, i[-1]
        all_word = "".join(all)
        print(all_word)
        word_list.append(all_word)

print(word_list)
sentence = " ".join(word_list)
print(sentence)
Sirius3
User
Beiträge: 17781
Registriert: Sonntag 21. Oktober 2012, 17:20

`text_list` ist immer noch ein schlechter Name, `i` noch ein viel schlechter. Gute Namen helfen beim Lesen und verstehen des Codes. Der Inhalt von elif und else ist immer noch fast identisch. Funktionsdefinitionen helfen hier.
`or` ist kein umgangsprachliches Oder sondern ein logisches Oder, das die linke Seite liefert, wenn sie sich zu einem "Wahr" auswerten läßt, sonst die rechte Seite. die rechte Seite besteht aus einem nicht-leeren String, ist also immer wahr, sodass der else-Block nie ausgeführt werden kann.
Was Du suchst, ist der `in`-Operator mit einer Liste.
Benutzeravatar
__blackjack__
User
Beiträge: 13167
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Hier mal was ich da so im Kopf hatte:

Code: Alles auswählen

#!/usr/bin/env python3
import random
import re

TEXT = """\
Ich habe die Aufgabe ein Programm zu schreiben das in einem Text bei allen
Wörtern die Buchstaben zufällig vertauscht wobei der erste und der letzte
Buchstabe gleich bleibt. Basiert auf einer Studie die besagt, das man es lesen
kann solange der erste und letzte Buchstabe stimmt und sonst nur die anderen
Buchstaben vetauscht. Also zB: "Es kmmot auf die Vrpunekacg an".
"""


def process_word(word):
    if len(word) > 3:
        middle_characters = list(word[1:-1])
        random.shuffle(middle_characters)
        word = word[0] + "".join(middle_characters) + word[-1]
    return word


def process_text(text):
    return re.sub(r"\w{3,}", lambda match: process_word(match.group()), text)


def main():
    print(process_text(TEXT))


if __name__ == "__main__":
    main()
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Interessant. :)

Hier mal mein Ansatz:

Code: Alles auswählen

#!/usr/bin/env python3
import fileinput
from random import shuffle

import somajo

LANGUAGE = 'de'


def scramble_word(word):
    if len(word) <= 3:
        return word
    head, *body, tail = word
    shuffle(body)
    return ''.join([head, *body, tail])


class TextScrambler:
    def __init__(self, tokenizer, scrambler):
        self.tokenizer = tokenizer
        self.scrambler = scrambler
        
    def __call__(self, text):
        result = []
        for token, category,extra in self.tokenizer.tokenize(text):
            result.append(self.scrambler(token) if category == 'regular' else token)
            if extra != 'SpaceAfter=No':
                result.append(' ')
            # OriginalSpelling etc. wird z.Z. nicht berücksichtigt.
        return ''.join(result)


def main():
    if not LANGUAGE in somajo.Tokenizer.supported_languages:
        raise ValueError('Language "{}" not supported.'.format(LANGUAGE))   
    text = '\n'.join(line for line in fileinput.input())
    tokenizer = somajo.Tokenizer(
            language=LANGUAGE,
            extra_info=True,
            token_classes=True
            )
    scramble_text = TextScrambler(tokenizer, scramble_word)
    scrambled_text = scramble_text(text)
    print(scrambled_text)


if __name__ == '__main__':
    main()
EDIT:
Resultat:

Code: Alles auswählen

$ ./scrambler.py text1.txt
Ich habe die Agfuabe ein Pmragorm zu sebciehrn das in eeinm Text bei aleln Weötrrn die Bhebstucan zfulliäg vtsuecarht weboi der etsre und der lettze Bcshtubae gclieh bieblt. Briesat auf eenir Sidtue die bsgaet, das man es lseen kann sgolane der esrte und lzttee Bhstuacbe smimtt und snsot nur die anedren Bahsbceutn vuhaectst. Aslo zB: "Es kmomt auf die Venpkrcaug an".
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Es ist erstaunlich, wie gut das Ergebnis der Regex-Lösung ist, wenn man bedenkt, wie einfach der Ausdruck ist, gerade auch im Vergleich zu der Komplexität eines Tokenizes. Ein kleiner Unterschied zwischen den beiden Ansätzen ist zum Beispiel im Englischen bei der Kontraktion "aren't" zu sehen. In der Regex-Version ist die Form "aern't" möglich, in der Tokenizer-Version nicht, da "are" und "n't" als zwei getrennte Tokens zu je drei Zeichen betrachtet werden. Man muss sich also überlegen, was man gerne möchte.
Antworten