Optimierbar?
-
Wolfsherz schrieb:
Ehrlich, das ist nett von dir. Aber ich verstehe von dem Quellcode die Hälfte nicht

Such dir Stück für Stück die Zeilen raus, die du nicht verstehst und versuch etwas darüber in Erfahrung zu bringen in Tutorials und Nachschlagewerken deiner Wahl. Dazu solltest du natürlich die Grundprinzipien der Sprache bereits durchgearbeitet haben und kennen, vorher machen Optimierungsversuche eh kaum Sinn.
-
Dann gehen wir das mal durch:
Zunächst mal ist das Programm in 3 Funktionen aufgeteilt und nicht mehr in eine einzige.
read_input liest die Eingabe von stdin, print_values gibt sie aus. der übersicht halber habe ich alles in einen namespaces namens "myapp" gepackt.
ein array aus integern wird nicht verwendet, statt dessen die klasse std::vector<T> aus <vector>.
Dieser wird bei read_input als call-by-reference übergeben. Was das heißt, kannst du ja in deinem C++ Buch nachlesen.die Fehlerbehandlung mit dem "Fehlerflag" habe ich durch Exceptions ersetzt. Zunächst habe ich eine eigene Klasse, abgeleitet von std::exception geschrieben. diese wird bei fehlerhafte eingabe geworfen (throw). somit kann das "programm", d.h. die beiden Funktion für die Ein- und Ausgabe zusammengefasst im try-Block stehen, während im catch-Block in main die eventuelle exception abgefangen wird und mit einer Ausgabe auf sie reagiert wird.
evt. macht die "typedef" auch noch Probleme, wenn du es noch nicht kennen solltest. schau dazu einfach in deinem Buch oder im netz (google: "c++ typedef") nach

-
typedef std::vector<int> int_vector_t;sowas sollte man verbieten, das bringt rein garnichts,
nicht weniger tipparbeit,
nur muss dann ein fremder code-leser erstmal suchen, ob denn nun
int_vector_t wirklich ein std::vector<int> istmich regt sowas echt auf, weil ich auf arbeit ständig so einen
mist lesen muss, da sucht man erstmal 3 header durch, bis man
zu 100% weiss womit man es zu tun hat
-
SonQuatsch schrieb:
typedef std::vector<int> int_vector_t;sowas sollte man verbieten, das bringt rein garnichts,
nicht weniger tipparbeit,
nur muss dann ein fremder code-leser erstmal suchen, ob denn nun
int_vector_t wirklich ein std::vector<int> istmich regt sowas echt auf, weil ich auf arbeit ständig so einen
mist lesen muss, da sucht man erstmal 3 header durch, bis man
zu 100% weiss womit man es zu tun hatDas einzige was man hier ankreiden kann ist, dass es int_vector_t und nicht int_container_t heisst.
solche typedefs sind gut. hier nicht notwendig, aber generell tut typedef programmen eher gut. wenn den genauen typ wissen willst, dann halte den mauszeiger über den typ und schon steht er da. in 99% der fälle willst du den typ aber nicht wissen, es ist ein container und gut ist. deshalb sollte die schleife auch nicht über op[] gehen sondern über iteratoren und schon wäre es besser.
was uU auch gehen würde wäre ein template typedef:
container<int>::type
oder so.
-
Shade Of Mine schrieb:
was uU auch gehen würde wäre ein template typedef:
container<int>::typeDas bringt aber auch nur etwas, wenn es wahrscheinlich ist, dass man den Containertypen später ändert (z.B. zu
std::list). Aber gerade bei solch kleinen Programmen ist das wohl eher nicht der Fall, und auch sonst kann man nicht einfach das Typedef ändern und alles ist gut.Shade Of Mine schrieb:
in 99% der fälle willst du den typ aber nicht wissen, es ist ein container und gut ist.
Solange man nur iteriert und keine spezifischen Funktionen wie
push_front(),operator[]etc. benutzt, mag das zwar sein - jedoch haben die einzelnen Containertypen ihre jeweiligen Stärken und Schwächen und es ist nicht immer egal, welchen Typen man auswählt. Beispielsweise nimmt man eher einen Vector, wenn man dessen Vorteile auch ausnutzen will (Random Access). Dann schreibt man lieber den Typen aus, damit man auch sieht, was man mit dem Container anstellen kann.Von daher finde ich das Argument, dass man möglichst flexibel ist (eine einzige Typedef-Änderung statt mehrere Ersetzungen), zwar grundsätzlich gut, aber in der Praxis kann man es selten 1:1 anwenden.
-
Nexus schrieb:
Shade Of Mine schrieb:
was uU auch gehen würde wäre ein template typedef:
container<int>::typeDas bringt aber auch nur etwas, wenn es wahrscheinlich ist, dass man den Containertypen später ändert (z.B. zu
std::list). Aber gerade bei solch kleinen Programmen ist das wohl eher nicht der Fall, und auch sonst kann man nicht einfach das Typedef ändern und alles ist gut.Man sollte sich aber sowas angewöhnen auch in den kleinen Programmen zu machen, sonst macht man es in den grossen erst recht nicht

Und man sollte eigentlich fast immer damit rechnen, dass sich der Container verändern könnte. Und da bist du besser abgesichert, mit solch einer Lösung.Nexus schrieb:
Shade Of Mine schrieb:
in 99% der fälle willst du den typ aber nicht wissen, es ist ein container und gut ist.
Solange man nur iteriert und keine spezifischen Funktionen wie
push_front(),operator[]etc. benutzt, mag das zwar sein - jedoch haben die einzelnen Containertypen ihre jeweiligen Stärken und Schwächen und es ist nicht immer egal, welchen Typen man auswählt. Beispielsweise nimmt man eher einen Vector, wenn man dessen Vorteile auch ausnutzen will (Random Access). Dann schreibt man lieber den Typen aus, damit man auch sieht, was man mit dem Container anstellen kann.Da hast du Shade Of Mine, glaub ich, falsch verstanden. Ein Leser des Codes, also z.B. ein anderer Entwickler, ist nicht daran interessiert, was für ein Container hier verwendet wird. Dies Information ist überflüssig zum Verständnis des Programmcodes.
Grüssli
-
Dravere schrieb:
Da hast du Shade Of Mine, glaub ich, falsch verstanden. Ein Leser des Codes, also z.B. ein anderer Entwickler, ist nicht daran interessiert, was für ein Container hier verwendet wird. Dies Information ist überflüssig zum Verständnis des Programmcodes.
Ja, zum reinen Lesen geht das ja vielleicht noch. Aber wenn man selber dran weiterarbeitet, sollte man auch wissen, was der Container so kann.
Und:
Dravere schrieb:
Und man sollte eigentlich fast immer damit rechnen, dass sich der Container verändern könnte. Und da bist du besser abgesichert, mit solch einer Lösung.
Das heisst, bei allen Containern sollte man nicht mehr als durchiterieren und Random Access mit
std::advance()durchführen, damit es auch bei einer Liste geht?Meiner Ansicht nach sollte man sich irgendwann für einen Containertypen entscheiden, damit man auch dessen spezifische Vorteile nutzen kann.

-
Nexus schrieb:
Ja, zum reinen Lesen geht das ja vielleicht noch. Aber wenn man selber dran weiterarbeitet, sollte man auch wissen, was der Container so kann.
Wenn man dran arbeitet, hat man das normalerweise im Kopf. Wenn man es nicht mehr im Kopf hat, kennt man wahrscheinlich auch den Code nicht mehr und muss ihn lesen, was auch problemlos geht.
Und um den Typ dann noch schnell rauszufinden, gibt es entsprechende Suchfunktionen. Viele IDEs bieten da auch Dinge an, wie: "Goto Definition/Declaration" oder ähnliches.Nexus schrieb:
Das heisst, bei allen Containern sollte man nicht mehr als durchiterieren und Random Access mit
std::advance()durchführen, damit es auch bei einer Liste geht?Vielfach ist das sogar tatsächlich am besten. Klar sollte man sich einmal auf einen Container definieren, aber man bleibt halt flexibler, wenn man es so löst. Die berühmte Wartbarkeit wird meiner Meinung nach dadurch erhöht.
Grüssli
-
Warum ist es wichtig ob der Container ein vector, eine list oder deque ist wenn er meine Anforderungen erfüllt? Dafür haben wir ja das iteratoren Konzept.
natürlich ist es an einer gewissen stelle wichtig welche implementierung der container verwendet - aber die meiste zeit ist das egal. ich sage einfach find(c.begin(), c.end(), v) und bekomme das gesuchte Elemente. selbes bei for_each oder sonstigen schleifen -> relevant wird es nur wenn ich den container bearbeite. und auch dann sind nur die groben constraints interessant (habe wie komplex sind die insert/remove operationen).
-
Dravere schrieb:
Wenn man dran arbeitet, hat man das normalerweise im Kopf. Wenn man es nicht mehr im Kopf hat, kennt man wahrscheinlich auch den Code nicht mehr und muss ihn lesen, was auch problemlos geht.
Und um den Typ dann noch schnell rauszufinden, gibt es entsprechende Suchfunktionen. Viele IDEs bieten da auch Dinge an, wie: "Goto Definition/Declaration" oder ähnliches.Den Typen herauszufinden ist auch nicht das Problem. Normalerweise reicht ein Darüberfahren mit der Maus, wie schon Shade Of Mine sagte. Aber ich kann mir vorstellen, das es sehr viele Fälle gibt, wo man so ein
container_t-Typedef macht, und dann den Typen trotzdem nicht ändert. Da wäre es vielleicht ein wenig effizienter, wenn man gleich den Typen sähe (auch wenn das nicht viel ausmacht).Dravere schrieb:
Vielfach ist das sogar tatsächlich am besten. Klar sollte man sich einmal auf einen Container definieren, aber man bleibt halt flexibler, wenn man es so löst. Die berühmte Wartbarkeit wird meiner Meinung nach dadurch erhöht.
Schon wieder so ein Standardargument :p
Was aber im Gegensatz zur guten Planung im Voraus und zum sauberen Design steht
Sorry, aber wenn man auf einer
std::listmitstd::advance()den Random-Access hinbiegen will, kann man ja gleich zustd::vectorgreifen. Oder zustd::deque, das ist noch flexibler. Kommt es so häufig vor, dass man im Voraus nicht sagen kann, wie der Container gebraucht wird und welche Funktionalität er bieten soll?Shade Of Mine schrieb:
Warum ist es wichtig ob der Container ein vector, eine list oder deque ist wenn er meine Anforderungen erfüllt? Dafür haben wir ja das iteratoren Konzept.
Ja eben, bei einfachem Durchiterieren und allgemein gehaltenen Operationen ist das ja kein Problem. Aber wieso haben die einzelnen STL-Container (reden wir von den 3 sequenziellen) Unterschiede? Wohl eher nicht, damit man alles allgemein hinbiegt (
std::advance()undinsert(begin())stattpush_front()). Klar kann es Fälle geben, in denen es nicht drauf an kommt, welchen Container man nimmt. Aber zumindest in den anderen Fällen finde ich ein Typedef unnötig.
-
Nexus schrieb:
Aber wieso haben die einzelnen STL-Container (reden wir von den 3 sequenziellen) Unterschiede? Wohl eher nicht, damit man alles allgemein hinbiegt (
std::advance()undinsert(begin())stattpush_front()). Klar kann es Fälle geben, in denen es nicht drauf an kommt, welchen Container man nimmt. Aber zumindest in den anderen Fällen finde ich ein Typedef unnötig.Es ist immer wichtig welchen Container man nimmt, es ist nur nicht immer wichtig

Was ich damit sagen will ist, dass die Entscheidung wichtig ist, aber der Client Code, dem ist das herzlichst egal. Die komplette c++ standard library ist so aufgebaut: gib mir 2 iteratoren und ich mache meinen job - egal welcher container zugrunde liegt.
und von wenigen code stücken abgesehen, ist es egal welche container man verwendet. man operiert die meiste zeit auf iteratoren - interessant ist das ganze nur in den kurzen momenten wo ich den container befülle oder rauslösche (oder ähnliche operationen mache). aber die meiste zeit suche ich etwas, transformiere etwas, manipuliere etwas,... und dafür ist der container egal. das ist die ganze idee dahinter.
uU will man auch einfach eine list verwenden und dann später mal einen benchmark mit einer single linked list testen und uU noch etwas später mal sehen wie es mit einer deque performt...
-
Aber wieso haben die einzelnen STL-Container (reden wir von den 3 sequenziellen) Unterschiede? Wohl eher nicht, damit man alles allgemein hinbiegt (std::advance() und insert(begin()) statt push_front()).
Ich kann es atm leider nicht überprüfen, aber ich würde mal behaupten, dass die STL das eh mit Spezialisierung löst, dass die passenden Funktionen aufgerufen werden.
-
Shade Of Mine schrieb:
man operiert die meiste zeit auf iteratoren - interessant ist das ganze nur in den kurzen momenten wo ich den container befülle oder rauslösche (oder ähnliche operationen mache). aber die meiste zeit suche ich etwas, transformiere etwas, manipuliere etwas,... und dafür ist der container egal. das ist die ganze idee dahinter.
Hm, das leuchtet ein. Aber zumindest für den kurzen Moment spielt das schon eine Rolle
- naja, bei meinen Anwendungen von Containern war es eben eher so, dass z.B. erase()-Operationen oder Random Access nicht selten vorkommen. Ich versuche auch von Anfang an, den am besten geeigneten Container einzusetzen und gebe dafür halt einen Teil der Flexibilität auf. Im Übrigen ist es bei meinen Projekten noch im Bereich des Möglichen, Containertypen auszuwechseln (auch wenn es kaum vorkommt). Deshalb der Einwand. Ausserdem bin ich kritisch gegenüber Argumenten wie besserer Wartbarkeit/mehr Flexibilität, die teilweise auf Kosten der Übersicht gehen und oft gar nie in der Praxis zur Geltung kommen.drakon schrieb:
Ich kann es atm leider nicht überprüfen, aber ich würde mal behaupten, dass die STL das eh mit Spezialisierung löst, dass die passenden Funktionen aufgerufen werden.
Ja, also wegen der Performance wirds kaum einen wesentlichen Unterschied machen... Beispielsweise wird ja bei
std::advance()nach Möglichkeit deroperator+verwendet, und sonst eben n Maloperator++. Ich meinte nur, man könne ja direkt die dafür vorgesehenen Funktionen verwenden, wenn der Container feststeht. Und meiner Ansicht nach sollte es auch nicht sehr häufig vorkommen, das man am Anfang den Container noch nicht festlegen kann.
-
Nexus schrieb:
Aber ich kann mir vorstellen, das es sehr viele Fälle gibt, wo man so ein
container_t-Typedef macht, und dann den Typen trotzdem nicht ändert. Da wäre es vielleicht ein wenig effizienter, wenn man gleich den Typen sähe (auch wenn das nicht viel ausmacht).Das habe ich sehr oft, dass ich am Ende nichts daran ändere. Aber man macht es trotzdem so, damit im Falle der Fälle ...

Ich mache bei Logikfehlern und anderem auchasserts in den Code rein. Die sind ja eigentlich unnötig, wenn man die Bibliothek oder das Programm richtig benutzt, dann funktioniert es doch. Trotzdem leistet man sich diese zusätzliche Schreibarbeit
Nexus schrieb:
Was aber im Gegensatz zur guten Planung im Voraus und zum sauberen Design steht

Man kann NIE alles planen! Das gilt im übrigen nicht nur in der Programmierung

Nexus schrieb:
Kommt es so häufig vor, dass man im Voraus nicht sagen kann, wie der Container gebraucht wird und welche Funktionalität er bieten soll?
Es geht schliesslich nur um das "falls". Zudem erhöht es an sich die Übersicht sogar. Ich finde so Templateklassen extrem mühsam zu lesen.
std::vector<unsigned int> <-> IndexContainer_tUnd es ist dann auch viel einfacher zu schreiben:
std::vector<unsigned int>::iterator iter = // ... std::vector<unsigned int>::iterator end = // ... // <-> IndexContainer_t::iterator iter = // ... IndexContainer_t::iterator end = // ...Und dieses Argument gibt es auch noch:
Shade Of Mine schrieb:
uU will man auch einfach eine list verwenden und dann später mal einen benchmark mit einer single linked list testen und uU noch etwas später mal sehen wie es mit einer deque performt...
Und noch eine Sache gibt es. Wenn du das ganze als Teil einer Bibliothek auslieferst und du plötzlich den Container wechselst, dann zerschiesst du die Anwendungen aller jener, welche die Lib benutzen. Und gerade bei sowas, kann es durchaus vorkommen, dass man mal einen anderen Container nimmt. Vielleicht von den Standard-Container weg will, um eine bessere Version einzubauen, eine eigene oder ähnliches.
Aber naja, wie du es selbst sagst. Bei deinen "kleinen" Anwendungen ist es vielleicht noch kein Problem, wenn du den Container wechselst und du nur an 5 Stellen etwas anders schreiben gehen musst. Wenn du aber plötzlich an hunderten Stellen etwas umschreiben musst, dann wirst du dir wünschen, dass du es damals anders gelöst hättest.
Und man soll sich eben auch schon bei kleinen Projekten an sowas gewöhnen. Was Fritzchen nicht lernt, lernt er nimmer mehr ... oder so ähnlich ging das doch?
Grüssli
-
*lol* Thread-hijacking vom Feinsten

cheers, Swordfish
BTW: Ich bin (auch) für die
typedef-Variante.
-
Auf jeden Fall die typedef-Variante. Das steigert die Abstraktion.
-
Was soll denn immer das gerede von "kleinen" Anwendungen?
Bis zu einer gewissen größe kann ich auch alles in die main schreiben oder rein funktional Programmieren.
Das Ziel in dem Forum ist doch nicht die minimale Lösung, sondern die optimale Lösung.
-
templäd schrieb:
Bis zu einer gewissen größe kann ich auch alles in die main schreiben oder rein funktional Programmieren.
Leute, lernt endlich mal, dass es einen wichtigen Unterschied zwischen funktional und prozedural gibt.