Liste bearbeiten / Jeden 2. Buchstaben groß

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.
Antworten
Stift005
User
Beiträge: 17
Registriert: Montag 2. November 2020, 16:52

Hallo zusammen,

bin noch ein ziemlicher Anfänger und würde mich freuen, wenn mir jemand weiterhelfen könnte.
Das Projekt:

Ein Programm entwickeln, dass jeden 2. Buchstaben eines Satzes groß schreibt.
("Internet meme") =='iNtErNeT MeMe')

Meine Idee:
1. Den Satz als einzelne Wörter in einer Liste speichern und alle Buchstaben klein machen.
2. Die Einzelnen Wörter in Buchstaben zerteilen
3. Jeden 2. Buchstaben der Liste (buchstabenliste) auswählen und "groß machen".
4. Das ganze wieder zusammenfügen.

Aktuell hab ichs bis Punkt 2 geschafft...

Code: Alles auswählen

def meme_text(text):
    liste = text.split()
    liste = text.lower()
    meme = ""
    
    for x in liste:
        for y in x:
            meme = meme + x
    print(meme)
            
   
    return "platzhalter"
    
        
     
print(meme_text("Internet meme"))
Ausgabe:
internet meme
platzhalter

Wenn die Idee wirklich gar nix taugt, bin ich auch offen für andere Anregungen! Freu mich über jede Hilfe.
Sirius3
User
Beiträge: 17759
Registriert: Sonntag 21. Oktober 2012, 17:20

Kann man machen. Wobei da bei Deinem Code nicht so umgesetzt ist.
1. machst Du zwar, wirfst aber das Ergebnis gleich wieder weg.
2. ist dann das kleiner machen des ganzen Textes.
`liste` ist ein schlechter Name, weil zu generisch. liste ist erst `woerter`, dann `text_lower`.
`x` ist ein ganz schlechter Name für das, was es wirklich ist.
`y` dann ein genauso schlechter Name, denn bei guten Namen wäre gleich klar, dass die innere Schleife nichts macht.
Eine Funktion, die immer einen konstanten String zurückgibt, geht auch kürzer.

Einfacher geht es, den ganzen Text Zeichen für Zeichen durchzugehen, und sich mit enumerate auch gleich den Index geben zu lassen. Ist der Index gerade mach das eine bei ungerade das andere.
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Den Schritt den ganzen Text in Kleinbuchstaben kann man sich sparen wenn man danach die Zeichen abwechselnd in Gross- und Kleinbuchstaben umwandelt.

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import cycle


def memefy(text):
    return "".join(
        change_case(character)
        for change_case, character in zip(cycle([str.lower, str.upper]), text)
    )


def main():
    print(memefy("Internet Meme"))


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Eine Möglichkeit wäre es, Slicing zu verwenden. Das kannst du leicht ausprobieren mit text[::2].lower() bzw text[1::2].upper(). Die Buchstaben könnte man dann wieder zusammenfügen. Python bietet dafür die zip()-Funktion. Das kommt von Zipper (Reißverschluss) und spätestens, wenn man sich das Reißverschlussverfahren auf der Autobahn vor Augen führt, müsste die Funktionsweise klar sein. ;)

Allerdings hat zip() ein Problem: Es hört auf, sobald ein Element keinen "Partner" mehr findet. Wenn also bei ungerader Buchstabenzahl des Wortes z.B. 3 kleine Buchstaben rauskommen und 2 große, dann ignoriert er den letzten übrig gebliebenen Buchstaben, was natürlich hierbei nicht so praktisch ist. Als Lösung bietet sich alternativ zip_longest() aus dem itertools-Modul an, dem man nämlich eine Art Dummy-Wert mitgeben kann zum Ersetzen beim fehlenden Gegenstück.

Ich würde das aber sowieso mit cycle() lösen. Der liefert in einem "Kreisel" die übergebenen Argumente. Das wären in diesem Fall immer wieder die lower() und upper()-Methode für Strings. Die verknüpfe ich mit zip() zu den einzelnen Buchstaben und rufe die einzeln mit "ihrer" Konvertier-Funktion auf. Darauf wende ich join() an, um die einzelnen Buchstaben wieder zu einer Zeichenkette zusammen zu fügen.

Code: Alles auswählen

from itertools import cycle

def convert_characters(text, converters):
    return "".join(
        converter(character) for converter, character
        in zip(cycle(converters), text)
    )
Und das lässt sich dann halt flexibel mit beliebigen Konvertern benutzen, hier wie gesagt lower() und upper().

Code: Alles auswählen

def main():
    text = "Dies ist ein Beispieltext"
    converters = [str.lower, str.upper]
    print(convert_characters(text, converters))
    converted_words = [
        convert_characters(word, converters)
        for word in text.split()
    ]
    print(*converted_words)

if __name__ == "__main__":
    main()
Wie man sieht, macht es natürlich einen Unterschied, ob man die Funktion auf den ganzen Text oder wortweise anwendet, da halt dementsprechend die Leerzeichen das Ergebnis beeinflussen. Diese sind ja auch Zeichen, die mitgezählt werden.
nezzcarth
User
Beiträge: 1638
Registriert: Samstag 16. April 2011, 12:47

Alternativ zu str.lower/str.upper kann man auch getattr verwenden:

Code: Alles auswählen

In [6]: def to_meme(s):
   ...:     return ''.join(getattr(char, op)() for op, char in zip(cycle(('upper
   ...: ','lower')), s))
Das eine ist eine Methode auf einem Typen, die man unintuitiver Weise auch separat mit einem Parameter aufrufen kann, das andere seltsame Metaprogrammierung. Beides aus meiner Sicht vielleicht nicht unbedingt die schönsten Ecken von Python. :)
Stift005
User
Beiträge: 17
Registriert: Montag 2. November 2020, 16:52

Erstmal danke für Eure Antworten!
Hmm.. Wir hatten bisher im Unterricht nur Schleifen und IF-Abfragen O.o
Von cycle oder gar pythonischer Metaprogrammierung hab ich noch nie was gehört.. :-D

Ich hatte mich gerade an einer anderen Idee festgebissen:
Mein String meme mit einer forschleife buchstabe für Buchstabe durchgehen und in einer neuen Variable speichern. Anschließend mit einer IF-Abfrage in Abhänigkeit vom letzten Buchstaben in der neuen Variable entscheiden ob der nächste groß oder klein sein muss.

Könnte das funktionieren? Wenn man mich verstanden hat.. Oo Glaub ich brauch langsam mal ne Pause :D
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@nezzcarth
Also falls das darauf abzielt, das Duck-Typing nicht zu verletzen, dann kann man auch einen Methodenaufruf via methodcaller("lower") bzw methodcaller("upper") festlegen und die beiden dann als Konverter an cycle() übergeben.

Der "Umweg", dass man die Methode eines Typen aufruft und ihr explizit eine Instanz des Typen übergibt, ist halt nicht so gängig, aber an sich schon vom Sprachdesign vorgesehen. Das ist ja genau der Parameter den man für self einsetzt, nur dass der Interpreter das in den meisten Anwendungsfällen hinter den Kulissen für einen übernimmt.
Benutzeravatar
__blackjack__
User
Beiträge: 13122
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@nezzcarth: Unintuitiv würde ich das nicht nennen. Das wäre es vielleicht wenn Python kein explizites `self` hätte, aber wenn man den Zusammenhang zwischen Funktionen als Attribute auf Klassen und Methoden auf Objekten verstanden hat, dann ist das IMHO sehr intuitiv. Das unschöne daran ist eher, dass man damit die Polymorphie über Bord wirft, wenn man an der Stelle schon eine feste Annahme über den Typ macht auf den das dann angewendet wird. Aber das kann man mit snafu's Hinweis auf `methodcaller()` ”reparieren”.

Code: Alles auswählen

#!/usr/bin/env python3
from itertools import cycle
from operator import methodcaller


def memefy(text):
    return "".join(
        change_case(character)
        for change_case, character in zip(
            cycle(map(methodcaller, ["lower", "upper"])), text
        )
    )


def main():
    print(memefy("Internet Meme"))


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hier eine Lösung ohne cycle() und ohne zip() und irgendwelche "komischen" Schleifeneinzeiler:

Code: Alles auswählen

def get_meme(text):
    result = []
    for index, character in enumerate(text):
        if (index % 2) == 0:
            result.append(character.lower())
        else:
            result.append(character.upper())
    return "".join(result)
Vielleicht blickst du da besser durch. Mit dem Modulo-Operator (%) solltest du dich auf jeden Fall mal genauer beschäftigen. Hinweis: Es hat nichts mit Prozent zu tun, aber der wird (meiner Erfahrung nach) gerne mal für Anfänger-Übungen eingesetzt. ;)
Stift005
User
Beiträge: 17
Registriert: Montag 2. November 2020, 16:52

@ snafu: Vielen dank! Das sieht mir schon mehr nach meiner Liga aus.
Von enumerate hab ich zwar bisher auch noch nichts gehört, aber da werde ich mich mal schlau machen.

Den Modulo-Operator kenne ich natürlich. Und bisher hatte ich auch eigentlich keine Probleme mit den Aufgaben die wir lösen sollten. Deshalb bin ich gerade etwas überrascht, dass hier dann doch son "Kracher" kam. Und etwas angefressen, weil ich keine eigene Lösung hab finden können.... -.-
Benutzeravatar
snafu
User
Beiträge: 6744
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

enumerate() zählt halt die Durchläufe mit, wenn man so will, indem es jedes Element mit einer aufsteigenden Nummer verbindet (der Startwert ist standardmäßig 0, aber manchmal ist zB auch 1 sinnvoll). Und in Verbindung mit einem passend eingesetzten Modulo weiß man dann, ob man in einem geraden oder ungeraden Durchlauf ist. Das ist die ganze Magie dahinter. ;)
Stift005
User
Beiträge: 17
Registriert: Montag 2. November 2020, 16:52

Ich nochmal.
Es ist einfach erstaunlich, was so ein paar Stunden Schlaf ausmachen können.. :-D
Mit der Lösung von gestern wollte ich mich nicht zufriedengeben, also habe ich es eben nochmal versucht.
Woran ich gestern über Stunden verzweifelte, gelang mir dann heute in 10 Minuten.

Es ist vielleicht nicht so elegant, aber ich bin dann doch grad einfach froh es auch selbst (mit Euren Tipps) hinbekommen zu haben:

Code: Alles auswählen

def meme_text(text):
    lowerText = text.lower()

    counter = 0
    result = ""

    for x in lowerText:
      counter = counter + 1
      for y in x:
        if counter % 2 == 0:
          result = result + (y.upper())
        else:
          result = result + y
    return result
Jankie
User
Beiträge: 592
Registriert: Mittwoch 26. September 2018, 14:06

Es gab mal einen ähnlichen Thread, da sind auch noch ein paar andere Wege beschrieben.

Hatte dort auch eine Lösung mit RegEx. Dabei wird aber für jedes Wort einzelnd betrachtet jeder zweite Buchstabe groß geschreiben, es ist also egal wie viele Leerzeichen zwischen den einzelnen Wörtern sind, das Ergebnis bleibt gleich.

Code: Alles auswählen

import re

def upper_every_second_letter_in_word(input_string):
    return " ".join("".join(char.upper() if index % 2 else char.lower() for index, char in enumerate(word)) for word in re.findall(r'\b\w+\b', input_string))
Antworten