Froschs Blog

Computer und was das Leben sonst noch so zu bieten hat

Zur Website | Impressum

Einfach und kompliziert

28. März 2021 um 10:04 Uhr von Atari-Frosch

Schon während meiner Schulzeit fiel meinen Eltern wie auch den Lehrpersonen immer wieder – und für sie offenbar sehr irritierend – auf, daß ich eigentlich einfache Aufgaben oft sehr umständlich löste. Nicht falsch, aber eben umständlich und mit viel zu hohem und gar nicht vorgesehenem Aufwand. Diese Eigenschaft blieb mir leider treu und verschaffte mir und anderen während meines Lebens eine Menge Facepalms.

Als ich 14 war, wurde zum ersten Mal ganz offiziell mein IQ gemessen, und die zuständige Psychologin war halbwegs geschockt, als sie feststellte, daß die Kurve fast nicht mehr auf den vorgesehenen Papier-Bogen paßte. Das Ergebnis: 140. Es hieß damals, das könnte der Grund für das unnötig komplizierte Denken sein. Ob das heute noch so eingeschätzt würde, weiß ich nicht.

Da mir das Thema gerade unvorbereitet, aber durchaus willkommen, einen kleinen Hyperfokus angestoßen hat, wollte ich das mal ausführlicher zeigen, was in so einem Fall – Aufgabe einfach, aber Lösung erstmal viel zu kompliziert – bei mir abläuft.

Nehmen wir eine kleine Programmier-Aufgabe. In einem meiner Python-Projekte (zu denen ich viel zu selten komme, *seufz*) habe ich es viel mit Zeitstempeln aus Logdateien zu tun. An einer Stelle benötige ich die Information, ob der gerade bearbeitete Zeitstempel auf einen Monats-Letzten fällt.

Python kennt für Zeitstempel einen eigenen Variablen- bzw. Objekt-Typ namens datetime, in dem alle Teile eines Zeitstempels – vom Jahr bis zur Sekunde und ggf. Mikro-Sekunde – zusammen abgespeichert sind. Gegeben sei also ein Zeitstempel z als datetime-Objekt, für den ermittelt werden soll, ob der Tag der letzte Tag eines Monats ist.

Mein Gehirn präsentierte mir nacheinander und mit einigem Zeitabstand drei verschiedene Lösungswege:

1. Lösung: quick & dirty

  • Ziehe Monat und Tag aus dem Zeitstempel.
  • Wenn der Monat 1, 3, 5, 7, 8, 10 oder 12 und der Tag 31 ist, haben wir einen Monats-Letzten.
  • Wenn der Monat 2 ist und der Tag 28 oder 29, haben wir auch einen Monats-Letzten.
  • Wenn es ein anderer Monat ist (4, 6, 9 oder 11; was anderes kommt ja jetzt nicht mehr in Frage) und der Tag ist 30, haben wir ebenfalls einen Monats-Letzten.

In Python:

 import datetime
 […]
 for line in loglines:
   z = … 
   lastofmonth = False
   mon = int(z.strftime("%m"))
   day = int(z.strftime("%d"))
   if mon == 1 or mon == 3 or mon == 5 or mon == 7 or mon == 8 or mon == 10 or mon == 12:
     if day == 31:
       lastofmonth = True
   elseif mon == 2 and (day == 28 or day == 29):
     lastofmonth = True
   elseif day == 30:
     lastofmonth = True

Klar, das funktioniert so, ist aber halt wegen der vielen Abfragen umständlich und viel unnötige Tipperei – und verbrät vermutlich auch unnötige CPU-Zyklen. Anstatt jetzt aber eine einfachere Lösung zu präsentieren, bekam ich einige Zeit später von meinem Gehirn erstmal die …

2. Lösung: Es geht noch komplizierter!

  • Baue eine Liste aus 12 Elementen und setze den jeweils letzten Tag des entsprechenden Monats ein.
  • Ziehe Monat und Tag aus dem gerade bearbeiteten Zeitstempel.
  • Überprüfe, ob der Monat ein Februar ist, und wenn ja, ermittle auch noch das Jahr und prüfe, ob das Jahr ein Schaltjahr ist; wenn ja, ist der Monats-Letzte ausnahmsweise 29 statt, wie in der Liste, 28.
  • Prüfe, ob Tag und Monat aus dem Zeitstempel mit dem Element der Monatsnummer und dem entsprechenden Monats-Letzten übereinstimmen.

In Python:

 import datetime
 […]
 for line in loglines:
   z = … 
   lastdayofmonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
   mon = int(z.strftime("%m"))
   day = int(z.strftime("%d"))
   leapyear = False
   year = int(z.strftime("%Y"))
   if year % 4 == 0:
     leapyear = True
     if year % 100 == 0:
         leapyear = False
   if mon == 2 and leapyear == True:
     lastday = 29
   else:
     lastday = lastdayofmonth(mon - 1) # - 1, weil eine Liste immer mit Element 0 anfängt
   if day == lastday:
     lastofmonth = True

Durch die Liste wird zusätzlicher Speicherplatz in Anspruch genommen (wenn auch natürlich nicht viel). Wir haben auch einiges an Abfragen, nur andere, vor allem für den Sonderfall des Schaltjahres mit dem 29. Februar. Funktioniert auch. Ist aber ebenfalls völlig unnötig.

Denn – und das wußte ich eigentlich bereits zur Zeit der ersten Lösung – mit einem datetime-Objekt kann man direkt rechnen. Und damit kommen wir zur …

3. Lösung: Ganz einfach!

  • Wenn der aktuelle Zeitstempel plus ein Tag beim Tag des Monats eine 1 zurückliefert, haben wir einen Monats-Letzten.

In Python:

 import datetime
 […]
 oneday = datetime.timedelta(days=1)
 for line in loglines:
   z = …
   lastdayofmonth = False
   plusoneday = z + oneday
   if int(plusoneday.strftime("%d")) == 1:
     lastdayofmonth = True

Also ganz einfach! 😁

Diese dritte Lösung fiel mir übrigens heute früh völlig kontextlos ein, als ich im Bett lag. 🤦‍♀️


History

10 Kommentare zu “Einfach und kompliziert”

  1. Michael Richter quakte:

    Du solltest mal was cooleres machen, also sagen wir mal C++ oder Rust. Du hast ja oben das Effizienz-Argument angedeutet, und da ist Python ja sowiso jenseits von Gut und Böse und deswegen nutzt man das nicht für Dinge die Performance-relevant sind. Ob Du da nun eine Tabelle nutzt oder eine Reihe von If-Abfragen, das macht das auch nichts mehr aus. In C++ oder Rusrt kannst Du aber mit wenig Code hochgradig effizient programmieren. Das macht einfach nur Mega-Spaß.


  2. Joachim quakte:

    Nette Idee. Wäre ich auch nicht ganz sofort darauf gekommen. Was aber das „coolere“ angeht: in meiner „Lieblingssprache“ einfach dies dem Date hinzufügen:

    isLastDayOfMonth = ( (self + one day) day = 1)

    Ist schöner als da mit strftime zu hantieren. Muss das in Python wirklich?
    Aber Frosch, lass dich nicht von mir (oder jemanden) verrückt machen. Die Idee ist cool.


  3. Atari-Frosch quakte:

    @Michael: Ich programmiere eigentlich nicht der „Coolness“ wegen 😉 Python habe ich mir wegen der leichten Erlernbarkeit bei gleichzeitig mächtigem Funktionsumfang rausgesucht, und ich finde es für mich eine schöne Sprache. Ich kann damit problemlos in Datenbanken herumfuhrwerken, Web-Zeuchs machen, aber auch Scripte für die Automatisierung regelmäßiger Admin-Aufgaben.

    Von Rust weiß ich nicht mehr als den Namen, aber C++ ist ja eher für system-nähere Projekte, oder? So im Bereich Kernel-Treiber bis Dienste (Webserver etc.), wäre also für meine Anwendungsgebiete eher nicht geeignet.

    Klassisches Kerninghan-Ritchie-C hab ich mal gelernt, in einem Kurs, 1989, danach nie wieder gebraucht. Dabei hatte ich sogar das mit den Pointern verstanden 😉

    (Wobei es mir hier eigentlich gar nicht um das Programmieren an sich ging, sondern um das umständliche Denken …)


  4. Atari-Frosch quakte:

    @Joachim: Man könnte natürlich eine Lib schreiben, um den Umgang mit Zeitstempeln noch weiter zu vereinfachen und den Code am Ende so zu schreiben, wie Du das auch machst. Aber ansonsten, ja, muß es wohl strftime sein.

    BTW, schön, mal wieder was von Dir zu lesen, ist ja ’ne Weile her 🙂


  5. Joachim quakte:

    Ja, Arbeit und Veränderungen. Aber ich schau hier immer mal rein. Du meinst doch nicht, ich würde dich vergessen? Schon weil ich meine:

    Danke für „die immer wieder kehrenden Dinge hier“, die mich aus meiner Blase schubsen und meinen kleinen Horizont etwas aufweiten.

    Was C++ angeht (mach ich täglich), eigentlich ist das weniger für wirklich systemnahe Dinge geeignet. Z.B weil new (malloc) unter Umständen auf kleinen Systemen kritisch sein kann. Oder die Lib (STL) manchmal doch ganz schön Overhead generiert. Nein C++ ist schon auch gerade für größere Dinge, etwa auch GUI-Programmierung (Qt ist so eine Art C++ mit „moc“…) gedacht. Die neueren C++ Versionen sind IMHO überkompliziert, aber schon nachvollziehbar gut durchdacht. Ob ich dir dazu raten würde?

    Wenn du mit Programmieren Geld verdienen willst nimm lieber Cobol. Die deutsche Wikipedia vergisst übrigens die Autorin Grace Hopper zu erwähnen.

    Gut, Cobol ist alt, aus heutiger Sicht keine so tolle Sprache mehr und verdirbt den Stil. Aber gerade hat IBM einen neuen Compiler gebracht sogar mit OO, wenn ich nicht irre. Banken verwenden das und suchen händeringend Leute. Wenn Du also Geld brauchst und was kannst, wovon Andere keine (Möchtegern-) Ahnung haben … 😉 IMHO machst du das mit Links.

    Beim gcc gibt es auch Cobol (als Übersetzer nach C?).

    … ich muss doch noch einen Nachtrag machen: Ich stelle mir das in meinem kleinen Kopf so einfach vor. Ich weiß, musst du nun nicht sagen. Aber vielleicht… ich meine es kommt doch immer anders, als man denkt. Wenn du nun damit reich wirst, dann gibst du einen aus. Klar soweit?


  6. Michael Richter quakte:

    Naja, wo Du schon die Effizienz unterschiedlicher Python-Lösungen angeführt hast habe ich halt alle Möglichkeiten durchespielt. Einerseits, dass Python von der Performance eh jenseits von Gut und Böse ist (https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/python3-gcc.html), d.h. wenn man schon Dinge tut die damit nicht effizient realisierbar sind, diese Unterschiede eh keine Rolle spielen. Andererseits, dass wenn man doch Wert auf Performance legt man in C++ und Rust sehr effizient programmieren kann, ohne wie in C sich für triviale Probleme nen Wolf zu programmieren.
    Und, Joachim, ich meine auf einem Arduino kannst Du sicher kein new und malloc() machen, aber das sind dann so Primitiv-Systeme, dass man auch auf C zurückgreifen kann, ohne einen nennenswerten Produktiviäts- und Wartbarkeits-Unterschied zu haben.


  7. Joachim quakte:

    @Michael Richter: ich habe da als alter Besserwisser ™ vielleicht eine etwas andere Sicht. Nimm immer die Sprache, die dem Problemfeld entspricht (Kunststück, wenn man die Wahl hat).

    Persönlich würde ich in der Regel Lua, Ruby und auch perl usw. einem Python vorziehen. Doch es gibt Anwendungsfälle, wo python absolut Sinn macht. Die vorhandenen Libs kommen inzwischen sogar an perl ran oder gehen im mathematischem Bereich darüber hinaus.

    Soweit ich das verstehe macht hier also Atari-Frosch (und viele Andere auch) alles richtig.

    Was C++ angeht, man kann schon („malloc“) machen. Wie gesagt, ich programmiere jeden Tag C++ (auch auf kleinen STM32). Ich würde nicht freiwillig auf die Vorteile von C++ gegenüber C verzichten. Ich meide new auf dem STM32. Doch gehen geht das schon. Bei dynamische Datenstrukturen geht das kaum sinnvoll anders.

    Wenn das alles nicht hilft, dann baust du dir einfach deine eigene „Sprache“ (was tatsächlich in C und anderen Sprachen ein Entwurfsmuster sein kann. Beispiel regexp).


  8. Joachim quakte:

    @Michael Richter: Nachtrag, natürlich hast du wenigstens in vielen Situationen Recht mit der Aussage zu Arduino und C. Und, geniale Bilder auf deiner Webseite. Sehr genial.

    @Atari-Frosch: könntest du dich mal kurz hier melden? Oder habe ich zuviel Blödsinn von mir gegeben? Das täte mir wirklich sehr leid.


  9. Atari-Frosch quakte:

    @Joachim: Nönö, alles gut, hab bloß grad schon wieder andere Probleme, also alles wie üblich bei mir 😉

    Was die Performance angeht: Klar ist Python da nicht die ideale Sprache. Darauf kommt’s mir aber gar nicht an. Das darzustellen war auch nicht meine Intention bei dem Artikel. Es ging mir vielmehr um meine eigene „Performance“ und um über-kompliziertes Denken. Das kleine Python-Problem sollte dabei nur als Beispiel dienen. Aber wenn Ihr Euch darüber über Programmiersprachen unterhalten wollt, bitte, gerne 😉


  10. Joachim quakte:

    Wenn ich helfen kann, gib mir Bescheid. Das gilt auch, wenn ich hier mal etwas länger wegbleibe. Mir ist klar, dass die Diskussion ein wenig OT ist. Immerhin bringst du auch die interessanten Programmierthemen 😉

    Darüber hinaus: die ideale Sprache gibt es nicht. Du hast mit deinem Beispiel gut dargestellt, dass es weniger auf die Sprache als auf die eigene Performance ankommt. Diese um die Ecke denken kenne ich auch und löse das oft auch so, wie du. Eine Nacht darüber schlafen.

    Anders gesagt, … ach lassen wir das. Wie sagte mal jemand auf einer Netzpolitik-Tagung zu mir. „Komm, das ist jetzt egal. Hauptsache wir verstehen uns“. High Five 🙂


Kommentieren

Bitte beachte die Kommentarregeln!

XHTML: Du kannst diese Tags verwenden: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>