Sind Generatoren schwer verständlich?

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
problembär

Edit (Leonidas): Von "String ersetzen" abgetrennt.
C4S3 hat geschrieben:Obwohl's immer heißt, Python sei einfach, finde ich es manchmal schon sehr kryptisch.
Python IST leicht, benutzerfreundlich und gut verständlich:

Code: Alles auswählen

#!/usr/bin/env python
#-*- coding: iso-8859-1 -*-

s = """#76#111#118#101#32#44#108#111#118#101#32#105
#115#32#97#32#118#101#114#98#76#111#118#101#32#105#115
#32#97#32#100#111#105#110#103#32#119#111#114#100
#70#101#97#114#108#101#115#115#32#111#110#32#109
#121#32#98#114#101#97#116#104#71#101#110#116#108
#101#32#105#109#112#117#108#115#105#111#110#83#104
#97#107#101#115#32#109"""

a = s.split("#")
b = ""

for i in a:
    try:
        i = int(i)
    except:
        continue
    b += chr(i)

print b
Nur meinen einige Egoisten, alles mit map(), lamdbda() usw. zukleistern zu müssen und nehmen damit Python wieder viele seiner wunderbaren Eigenschaften.

Viele Grüße
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

problembär hat geschrieben:Nur meinen einige Egoisten, alles mit map(), lamdbda() usw. zukleistern zu müssen und nehmen damit Python wieder viele seiner wunderbaren Eigenschaften.
Also gerade das Weglassen von ``map()`` etc. führt dazu, dass wunderbare Eigenschaften verloren gehen. Dein Code ist sowieso komisch, denn er fängt alle Exceptions auf und das ist so oder so eine schlechte Idee.

Und Variablennamen wie ``a``, ``s``, ``b`` und ``i`` (insbesondere wenn ``i`` üblicherweise für Integer steht und nicht für Strings) machen deinen Code auch nicht klarer. Eher länger und mit sehr geringem Informationsgehalt, dafür schlechter Laufzeit (man sollte Strings in Python nicht mit + zusammensetzen).
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Panke
User
Beiträge: 185
Registriert: Sonntag 18. März 2007, 19:26

Nur meinen einige Egoisten, alles mit map(), lamdbda() usw. zukleistern zu müssen und nehmen damit Python wieder viele seiner wunderbaren Eigenschaften.
Dieser funktionale Stil gilt vielen als eleganter (sprich lesbarer) als etwas imperatives. Man muss ihn nur gewohnt sein. Ich finde die Lösung von EyDu super lesbar, gerade die zweite finde ich sprechend.

Gib mir das Zeichen (chr) zu jeder Zahl (int) zwischen den # (split) und füge das Ergebnis zusammen (join).

Bei solchen Minimalbeispielen aber auf Variablen mit einem Zeichen rumzureiten, finde ich allerdings vorgeschoben.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@problembär: Ich vermute mal, dass doch dich wenig bis gar nicht mit funktionaler Programmierung beschäftigt hast. Dort ist dieses vorgehen ganz normal, man muss sich nur ein wenig daran gewöhnen und wissen, wie man den Code zu lesen hat.

Ich empfehle dir einfach mal, dich ein Wochenende oder eine Woche mit Haskell zu beschäftigen. Am Anfang wirst du es sicher nicht mögen, aber wenn du das Prinzip verstanden hast, dann wird es eine wirkliche Bereicherung darstellen.
Das Leben ist wie ein Tennisball.
problembär

EyDu hat geschrieben:@problembär: Ich vermute mal, dass doch dich wenig bis gar nicht mit funktionaler Programmierung beschäftigt hast.
Das kann schon sein, aber es geht nicht um mich, sondern darum, ob der Fragesteller als Python-Anfänger die Antworten verstehen kann oder nicht. Und das konnte er nach eigenen Angaben nicht.
Ich würde mir eben wünschen, daß manchmal ein paar Code-Beiträge etwas einfacher geschrieben wären. Das ist alles.

Gruß
BlackJack

@problembär: Aber die ersten beiden Antworten *sind* einfach. Das sind IMHO die einfachsten, direktesten, und unkompliziertesten Arten das zu lösen.

Bei Deiner Lösung würde ich nämlich sagen sie ist umständlicher als sie sein müsste und würde sie in cofi's Lösung umschreiben. Das ist ja im Grunde fast nur eine Transformation von sonst gleichem Code. Er umgeht nur durch das `strip()` die Notwendigkeit der Ausnahmebehandlung und verwendet nicht ``+``/``+=`` auf Zeichenketten in einer Schleife, was man sowieso nicht machen sollte. Wenn man diesen "Fehler" bei Dir beseitigt, hat man auch bei der langen Schreibweise der Schleife ein `join()`.

Was `join()`, `strip()`, und `split()` auf Zeichenketten machen, kann man nachlesen, das sind keine komplizierten Operationen, und "list comprehensions" bzw. Generatorausdrücke sind nicht schwerer zu verstehen als "traditionelle" Schleifen. Ich denke der Knackpunkt liegt da eher bei Programmierern, die schon seit Ewigkeiten prozedurale Schleifen kennen, und für die so etwas ungewohnt aussieht.
yantur_v_b
User
Beiträge: 17
Registriert: Montag 25. Mai 2009, 10:32

BlackJack hat geschrieben:@problembär: Aber die ersten beiden Antworten *sind* einfach. Das sind IMHO die einfachsten, direktesten, und unkompliziertesten Arten das zu lösen.
Aber sie sind eins nicht: Für Nutzer, die noch keine nahezu perfekten Programmierer sind und noch nicht in Programmiersprache denken, einfach zu verstehen. Aber auch das ist ein möglicher Bedeutungsinhalt für "einfach".
BlackJack

@yantur_v_b: Für Nutzer die noch nicht in Programmiersprachen "denken" ist auch die andere Lösung nicht einfacher zu verstehen. Es werden letztendlich die gleichen Methoden verwendet und man muss eine ``for``-Schleife verstanden haben.

Der funtkionale Ansatz ist für Anfänger letztlich nicht schwerer oder leichter als der prozedurale. Die grossen Probleme haben in der Regel bloss die Leute die es schon kennen, und zwar etwas anders, *umzudenken*.

Bei der prozeduralen Schreibweise braucht man sogar noch eine Liste und eine Methode mehr:

Code: Alles auswählen

    result = list()
    for c in s.strip('#').split('#'):
        result.append(chr(int(c)))
    print ''.join(result)

    # vs.
    
    print ''.join(chr(int(c)) for c in s.strip('#').split('#'))
Bevor das als Einwand kommt: Die Lösung mit ``+=`` ist für mich keine. Man sollte Anfängern IMHO gleich idiomatischen Code zeigen und nichts was potentiell quadratische Laufzeit hat.
yantur_v_b
User
Beiträge: 17
Registriert: Montag 25. Mai 2009, 10:32

BlackJack hat geschrieben:Die grossen Probleme haben in der Regel bloss die Leute die es schon kennen, und zwar etwas anders, *umzudenken*.
Ich bezweifele, daß das sachlich stimmt. Aber das ist im Grunde auch gar nicht wichtig: In jedem Fall ist das Problem bei einigen Leuten offenkundig vorhanden, auch wenn es aus dogmatischen Erwägungen unzulässig erscheint. Und es ergibt sehr wenig Sinn, jemand zu sagen, er dürfe ein Problem ja gar nicht haben und es sei deswegen obsolet, darauf einzugehen oder gar eine Problemlösung so anzugehen, daß das Problem dabei berücksichtigt wird.
BlackJack hat geschrieben:Bei der prozeduralen Schreibweise braucht man sogar noch eine Liste und eine Methode mehr:...
Das kann dem Verständnis durchaus zuträglich sein. Und für die meisten Programme ist das in Punkto Speicher- und Zeitbedarf echt egal.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

yantur_v_b hat geschrieben:Das kann dem Verständnis durchaus zuträglich sein. Und für die meisten Programme ist das in Punkto Speicher- und Zeitbedarf echt egal.
Wäre dass praktisch würden alle Programmierer so denken, dann müsste man sich gar nicht mehr bemühen auf mehrere Programme gleichzeitig zu achten.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

DasIch hat geschrieben:Wäre dass praktisch würden alle Programmierer so denken, dann müsste man sich gar nicht mehr bemühen auf mehrere Programme gleichzeitig zu achten.
Weil ein einzelnes Programm die Maschine so stark auslastet, dass da kein zweites Programm sinnvoll läuft?! 8)

yantur_v_b, jeder etwas bessere Python-Programmierer, selbst wenn er von der imperativen Schiene kommt wird dir das ``+=`` in der Schleife anmäkeln, das hat mit Einfachkeit oder Schwierigkeit nichts zu tun - sowas macht man in idiomatischen Python-Code einfach nicht. Genauso wie man in C auch nicht Strings mit realloc() immer um einzelne Bytes größer macht, obwohl es geht. Generell sollte man sich beim Lernen einer Sprache durchaus bemühen Code idiomatisch zu lesen und auch idiomatisch zu schreiben. Wenn es mächtige Konzepte wie Generator Expressions gibt, deren Funktionsweise den Programmierern weithin bekannt ist, dann sollte man sie auch nutzen. Denn bei einer for-Schleife kann alles mögliche passieren, bei einer Generator Expression weiß man sofort, dass alle Elemente einzeln verarbeitet werden und dann in einem Generator landen. Da muss man nicht schauen was wo passiert, es ist auf einen Blick klar. Es ist so eine Art "Mini-Design Pattern" das erfahrene Programmierer sofort erkennen und sogar schneller verstehen als eine ``for`` Schleife, weil eben eine ``for`` Schleife erst analysiert werden muss.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
yantur_v_b
User
Beiträge: 17
Registriert: Montag 25. Mai 2009, 10:32

Leonidas hat geschrieben: bei einer Generator Expression weiß man sofort, dass alle Elemente einzeln verarbeitet werden und dann in einem Generator landen.
Wenn man schon in Programmiersprachen denkt, mag es sein, daß man so etwas "sofort weiß", das kann ich nicht beurteilen. Als jemand, der umlernt oder frisch anfängt, weiß man es jedenfalls nicht.

OT:
(Meine eigene Erfahrung ist, daß ich alles, was ein "generator" enthält, hasse, weil es keine Möglichkeit gibt, zu sehen, was da überhaupt ist. Alle Versuche, an das, was für mich "Inhalt" von so etwas wäre, heranzukommen, scheitern. Das heißt, die Dinger sind irgendwelche "black boxes", die ich zwar per Copy&Paste oder Abtippen in meine Programme einbauen kann, aber nur um den Preis, daß ich nicht weiß, wie und warum die dann funktionieren. Lernen tue ich so nichts. Da sind mir Listen, Dictionaries oder vergleichbares weit lieber; dann kann ich wenigstens herumprobieren und weiterlernen, auch wenn es langsam geht.
/OT

Aber vielleicht sollten wir das Gespräch irgendwohin verlagern, wo es nicht ganz so OT ist?
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Dann mach aus einem Generator eine Liste und schaue nach was drin ist?! Also die Argumentation verstehe ich überhaupt nicht. Ein Generator ist keine Blackbox, da ist drin was man reingetan hat, wie bei einer Liste, einem Dictionary oder einem String auch.

Jemand der Umlernt schreibt ja auch öfters mal ``for i in range(len(iterable))``, das hier ja auch nicht als positives Beispiel aufgezeigt wird. Generell ist Python für die lesbar, die Python mehr oder weniger gut können (das können auch Anfänger sein, aber auch die sollten ein Tutorial durcharbeiten und nicht versuchen die Syntax zu erraten). Eine Sprache für jemanden verständlich zu machen der nicht programmieren kann ist nicht Sache der Syntax, sondern des Tutorials. Und wenn man das mal durch hat, sind auch GEs oder LCs nicht komplizierter als ``for``-Schleifen, durch ihre Beschränktheit eher noch simpler.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
yantur_v_b
User
Beiträge: 17
Registriert: Montag 25. Mai 2009, 10:32

Leonidas hat geschrieben:Dann mach ...
Klar - ich weiß und mache einfach das, was jeder Profi mit beliebiger Programmiererfahrung selbstredend tun würde, weil es ohnehin das Einfachste von der Welt ist. Das macht schließlich jeder Anfänger so!? Oder kann es sein, daß "Anfänger" eine Bedeutung hat, die irgendwie mit diesem Problem in Zusammenhang steht und vor allem auch mit dem Inhalt des Begriffs "einfach"? [/sarkasmus]

Danke für das Abspalten des Fadens übrigens :-).
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Die Diskussion ist muessig. Zum Lernen einer Sprache gehoert eben nicht nur das Lernen der Syntax, sondern auch das der Idiome und das Anwenden letzterer. In dem speziellen Fall ist es sogar noch Syntax.

Darueber hinaus finde ich die Abschnitte im Tutorial zu den Comprehensions sehr gelungen (zumindest ab 3.1/2.7, mit den davor habe ich mich nicht genau auseinandergesetzt).

Um mal auf die Grundthese einzugehn, dass Python manchmal kryptisch ist, so glaube ich, dass C4S3 die funktionale Loesung von EyDu meinte, allein schon wg den Zeitpunkten. IMHO ist die weit schwieriger verdaulich als meine GE.

P.S. Ich finde es richtig drollig, dass bei dem Titel keine Generatoren im Thread auftauchen :twisted:
yantur_v_b
User
Beiträge: 17
Registriert: Montag 25. Mai 2009, 10:32

cofi hat geschrieben:Zum Lernen einer Sprache gehoert eben nicht nur das Lernen der Syntax, sondern auch das der Idiome und das Anwenden letzterer.
Durchaus richtig - nur kann wenigstens ein Teil der Menschheit nicht alles auf einmal und sofort ganz am Anfang schon lernen. Und dann macht es einen Unterschied in bezug auf die Einordnung als "einfach", wie ein Stück Software formuliert ist, vor allen wenn es um die Antwort auf eine Frage geht.

Komischerweise finden sich im englischsprachigen Pythonforum im Bereich für die Anfänger auch fast keine einzeiligen Ausdrücke. Ich kann natürlich nicht beurteilen, ob die Leute, die dort die Fragen anderer beantworten, vielleicht einfach keine guten Programmierer sind. Aber obwohl meine Muttersprache Deutsch ist, sind die dortigen Erklärungen für mich oft erhellender als die mehr oder weniger kommentarlos als Antwort verfassten Beiträge in diesem Forum - was ich nicht selten als ziemlich frustrierend empfinde, es wär halt doch schön, wenn ich auch in meiner eigenen Sprache mal einfache Fragen zu Python stellen könnte und auf schlicht hilfreiche Antworten hoffen. Aber einen Ort dafür habe ich bislang nicht auftreiben können :-( .
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Den Code danach zu schreiben, dass jeder Anfänger ihn sofort versteht halte ich nicht für besonders sinnvoll. Die Fragesteller sind eben nicht immer totale Anfänger und da ist eine unnötig lange Lösung einfach sinnlos. Man kann doch nachschauen oder hier nachfragen wenn etwas nicht verständlich ist. Dann bekommt man auch eine Lösung mit mehreren Schritten, und lernt vielleicht noch mehr als eigentlich geplant.
Das Leben ist wie ein Tennisball.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Jeder, der mehr machen will, als Programmtext solange zusammen zu klöppeln, bis das ganze zufällig funktioniert, muss eher früher als später die zu Grunde liegenden (mathematischen) Konzepte verstehen, wie z.B. Abstraktion, Applikation oder Rekursion. Eine funktionale Sichtweise macht es hier viel einfacher als eine imperative.

Hinzu kommt, dass wir gelernt haben, dass Abstraktionen bei der Kommunikation helfen, in dem sie Abkürzungen für immer wiederkehrende Sachverhalte erfinden. Vielleicht ist es nicht jedem klar, aber ein Microprozessor kann gar nicht wirklich multiplizieren. Schreiben wir "3*4" hin, läuft ein recht komplexes Programm (heutzutage in Microcode, einer Ebene noch unterhalb des Maschinencodes) ab, welches eine komplizierte Folge von Additionen und anderen Bit-Operationen durchführt. Wir müssen das (glücklicherweise) nicht alles hinschreiben. Das war nicht immer so. Der Z80-Prozessor oder der 6502-Prozessor, die in vielen gerade mal 20 Jahre alten Home-Computern verbaut wurden (wie dem Apple II oder dem C64 (da war es ein 6510 aber egal)) konnten nicht multiplizieren. Dort musste man das immer und immer wieder hinschreiben. Meist gab es eine generische Multiplikationsroutine, aber die war viel zu langsam, wenn man etwa nur mit 10 multiplizieren wollte. Da war es besser, das Äquivalent von `a1 = a + a; a1 = a1 + a1; a1 = a1 + a; a = a1 + a1` zu programmieren.

Was ich mit diesem langen Ausflug in die Frühgeschichte der Computer sagen will, ist dies: Gewisse Abstraktionen wie die Multiplikation (und das Computer auch gar nicht wirklich addieren können, sondern nur Bitmuster verknüpfen, will ich jetzt gar nicht ausweiten) sind inzwischen selbstverständlich.

Gerade die funktionale Programmiersprache hat weitere Abstraktionen als Teil einer Programmier-Sprache entwickelt, die immer wieder kehren und auf diese Weise eine kürzere aber vor allen Dingen präzisere Ausdrucksform ermöglichen. So kennen wir z.B. `map` oder `filter` auch in Python. Beides kann man wiederum in einer List-Comprehension zusammenfassen. Die Sprache Scala kennt Sequence-Comprehension, die noch eine dritte `flatMap` genannte Funktion kennen, um noch allgemeiner zu sein. Bräuchte man in Python auch, wenn man LCs sauber auf OO-Konzepte abbilden wollte.

Apropos OO: Natürlich sind auch hier Konzepte entstanden, die inzwischen relativ normal sind. Und schon vor fast 30 Jahren hat man bei Smalltalk erkannt, dass innere Iteratoren (wie sie Ruby nutzt) mächtiger und gleichzeitig verständlicher sind als äußere Iteratoren (das sind die einzigen, die Java kennt). LCs zählen zu den inneren Iteratoren.

Und Generatoren gibt es als Streams auch häufig als Konzept in funktionalen Sprachen und in der Variante - die bei Python erst später hinzudefiniert wurde - als Coroutinen tauchten diese schon in Simula-67 auf (wenn nicht noch früher).

Bevor ich komplett den Faden verliere: Ich bin davon überzeugt, dass der funktionale Stil der klarere, der ausdrucksstärkere und damit letztlich auch der einfachere ist, das gerade LCs sehr gut lesbar sind und Generatorausdrücke als Optimierung davon als Konzept wichtig und sehr lesbar sind. Genau wie wir "+" und "-" für Zahlen als Operationen kennen, müssen wir die üblichen Operationen auf (möglicherweise unendlichen) Listen kennen.

Ich meine, Sprache in der Tradition von APL (z.B. "J" oder "K", die ich mir schon länger mal anschauen wollte, arbeiten immer auf Listen oder Feldern und nicht nur auf skalaren Werten.

Stefan
Nocta
User
Beiträge: 290
Registriert: Freitag 22. Juni 2007, 14:13

Wenn ein Ausdruck map und List Comprehensions beinhaltet, kann der Anfänger, der dies nicht versteht, einfach in der Dokumentation nachschauen, was dies denn ist.
Wenn er es trotzdem nicht verstehen sollte, kann er ja immer noch konkrete Fragen dazu stellen, dann werden die auch sicher beantwortet.
Also lernt der Anfänger somit viel mehr als mit einer anderen Lösung, die nur die "anfängerfreundliche Elemente" von Python benutzt.

Das alles setzt noch Eigeninitiative vorraus, aber wenn man programmieren will, braucht man das auch.
Früher wär ich auch nie auf die Idee gekommen, LCs, GEs, map, lambda, etc zu benutzen, doch ich hab im Forum gesehen, wie praktisch diese sein können. Ich habe erstmal nichts davon verstanden, aber als ich ein wenig dazu nachgelesen habe und damit rumgespielt habe, habe ich gemerkt, wie nützlich das Zeug ist und wie viel besser man damit klarkommt, als alles in umständliche for-Schleifen zu schreiben.
Antworten