Probleme bei Stringbearbeitung
-
Ziel ist es die Blockreihenfolge umzudrehen, nicht die Blöcke selbst.
Und wenn du nun 'Block' durch 'Woerter' ersetzt, erkennst du, dass das Internet viele bekannte Loesungen in vielen bekannten und unbekannten Sprachen bereithaelt. Denken muss man wirklich nicht. Oder der MAtrixstil: Nicht denken, wissen!
Ach, ich bin da eher faul: http://www.geeksforgeeks.org/reverse-words-in-a-given-string/
Einfach ' ' durch ':' ersetzen, http://ideone.com/Xc1Sz5 . Mann muss zwei Mal drueberiterieren, ist aber inplace. Trivial parallelisierbar. Um Sonderfaelle zu beachten, brauche ich eine bessere Problembeschreibung.
-
Genau das wollte ich machen. Mein Algorithmus ist dem doch dann gar nicht so fern....
Danke!
LG, freakC++
-
verallgemeinerten Problem
Soll ich mal anfangen zu verallgemeinern?
1.) Variabler Separator
2.) Separator variabler Laenge
3.) Separator escapen um als Teilwort betrachtet zu werden.
4.) Escapezeichen escapen
5.) Keine Strings sondern Streams mit verschiedenen Fehlerzustaenden
6.) Keine Strings sonder Bitstreams deren Wortgroesse kein Vielfaches von 8 Bit sind
7.) Muahahahaha
8.) Werner darf nicht mitmachen
9.) Mehr Muahahahaha ...
-
Dank knivils Link habe ich meine Methode nun wie folgt umgebaut. So funktioniert der Minialgorithmus und ich meiner Ansicht auch recht performant.
Hat jemand was dagegen?

void myclass::format_output(std::string& raw_result) { std::string::iterator num_begin = raw_result.begin(); for(std::string::iterator it = raw_result.begin(); it != raw_result.end(); ++it) { if(*it == delimiter){ std::reverse(num_begin, it); num_begin = it+1; } } std::reverse(raw_result.begin(), raw_result.end()); raw_result.erase(0,1); }Viele Grüße
freakC++
-
Hat jemand was dagegen?
Warum sollte es eine Methode und nicht eher eine freie Funktion sein?
-
weil ich die Funktion nur in meiner Klasse brauche. Das würde dem Prinzip der Kapselung wiedersprechen.
Später brauche ich die Klasse nur als Hilfsmittel. Diese Formatierungsfunktion ist privat und muss nicht nach außen sichtbar sein.
-
Es gibt viel bessere Alternativen. Datenkapselung ist es auch nicht, da diese Methode keine Daten der Klasse benutzt. Es ist eine freie Funktion, guter C++ Stil respektiert das, indem sie als freie Funktion implementiert wird.
-
SeppJ schrieb:
??? Aufmerksam lesen: Meine Streamlösung ist für den Fall, dass 12:34:56 zu 65, 43, 21 werden soll. So wie vom TE ursprünglich verlangt
Ja, ursprünglich.
freakC++ schrieb:
Sorry Leute! Ich habe Blödsinn geredet. Aus
23:24:28:10:29:18:12:24:11:27:10:22:10:20:28
soll
28:20:10:22:...
werden. Daher kann nicht einfach ein reverse machen. Deswegen ist mein Code etwas komplizierter. Sieht jemand das Problem?
Sollte doch nur eine kleine Änderung in deinem Code sein sein, nicht?
und als Demonstration für Skym0sh0, wie es ohne drei Umwege und trotzdem universell gehen könnte. Damit ist das eine ganz wunderbare Lösung, die kaum einen Prozessortakt zu viel verschwendet, dabei aber universell auf viele Probleme angewandt werden kann.

Ich habe mal deine Lösung mitstd::string foo = "!nebeirhcseg streawkceur remmi nedrew netfahcstoB ehcsinataS"; for (int i=0; i<20; i++) foo = foo + foo;getestet und ich erhalte:
reverse string + streambuf: 3.30s user 0.16s system 99% cpu 3.467 total seppjs reversed streambuf : 2.93s user 0.10s system 99% cpu 3.034 total iterator + for-loo p : 0.78s user 0.10s system 99% cpu 0.883 totalOooh, wie unerwartet. istream hält einen eigenen buffer, ostream hält einen buffer und formatierter Input ist teuer.
std::ostreambuf_iterator<char> out(std::cout); auto it=foo.rbegin(), end=foo.rend(); while (it != end) { while (std::isspace(*it) && it != end) ++it; while (!std::isspace(*it) && it != end) *out++ = *it++; *out++ = ' '; }Meine Aussage: IOstream-Parsen ist sehr langsam und für Nicht-Whitespace-getrennten-Standarddatentypen-Input enorm umständlich. (Bei langsamen Festplatten fällt das Parsen vielleicht weniger ins Gewicht, aber wenn der Input schon im RAM ist, ist stringstream der Tod)
Für Speed soll man auf Iteratoren wechseln (istreambuf_iterator im Fall von externen Streams, sonst halt was gegeben ist). Ist oft auch kürzer und einleuchtender. Wenn einem Whitespaces egal sind, ist der Stream nur hinderlich.
Zu deiner Lösung meines exotischen Problems: Ähh, du hast es nicht gelöst. Es geht nicht darum, die Zeichen auszugeben. Ich will die Zahlen haben! Wir wollen damit rechnen.
Das ist deine Interpretation fürs Problem. Klar ist eine Streamlösung einfacher, wenn wir Parsen wollen. Das war aber nie gefragt.
Wenn du nun beginnst, num_get nachzuprogrammieren: Holzweg. Dann fang ich an, Fließkommazahlen hexadezimal in Scientificschreibweise mit Vorzeichen in die Liste zu packen und es soll immer noch funktionieren. Bitte eine universelle Lösung, die meiner ebenbürtig ist.
Nimmstu std::stoi. Das Design der Iostreams ist so kaputt, dass ich num_get nur mit istreambuf_iterator<char|wchar_t> verwenden kann (d.h. ich brauche deinen Iter2Streambuf aufgrund von Iostream-Limitations).
Ich wiederhole: Wie würdest du die eigentliche (im Gegensatz zur ursprünglichen) Aufgabe von freakC++ lösen?
-
knivil schrieb:
Datenkapselung ist es auch nicht, da diese Methode keine Daten der Klasse benutzt.
Stimmt nicht. "delimiter" ist ein Klassenattribut. In jeder Software Engineering Vorlesung lernst Du, dass frei in der Welt herumfliegende Funktionen nicht gut sind. Zwar wird nur auf "delimiter zugegriffen, was ehrlich gesagt ein char ist, aber die Funktion gehört logisch zur Aufgabe der Klasse. Sie ist Teil einer Verantwortlichkeit und gehört für mich daher in die Klasse. Außerdem habe ich dann den Namen der Funktion nicht vergeben.
eSsThehelL schrieb:
Für Speed soll man auf Iteratoren wechseln (istreambuf_iterator im Fall von externen Streams, sonst halt was gegeben ist). Ist oft auch kürzer und einleuchtender. Wenn einem Whitespaces egal sind, ist der Stream nur hinderlich.
Ich hoffe, dass duch dich nur auf das Parsen beziehst. Ich baue gerade einer BigInteger Klasse, die ihre Ergebniss zum Schluss in einem String zurückgibt. Alle Zwischenergebnisse werden nach und nach in einen stringstream geschoben. So geht es am einfachsten.
Oder ist das zu langsam und es gibt noch bessere Methoden, die für mich bislang unbekannt waren?
Danke
-
freakC++ schrieb:
knivil schrieb:
Datenkapselung ist es auch nicht, da diese Methode keine Daten der Klasse benutzt.
Stimmt nicht. "delimiter" ist ein Klassenattribut. In jeder Software Engineering Vorlesung lernst Du, dass frei in der Welt herumfliegende Funktionen nicht gut sind. Zwar wird nur auf "delimiter zugegriffen, was ehrlich gesagt ein char ist, aber die Funktion gehört logisch zur Aufgabe der Klasse. Sie ist Teil einer Verantwortlichkeit und gehört für mich daher in die Klasse. Außerdem habe ich dann den Namen der Funktion nicht vergeben.
Was spricht gegen eine freie Funktion:
void format_output(std::string &raw_result, char delimiter);?
Und schon hast du wesentlich mehr Flexibilität.
-
Nichts, aber ich brauche sie nie wieder. Ich brauche sie in einer Klasse einmal. Diese Klasse soll dann später in andere mit eingebunden werden. Da ist es mir irgendwie lieber, wenn die Methoden zu irgendjemandem gehören.
Vielleicht habe ich aber auch nur zu viel Java programmiert und zu gut in Software Engineering aufgepasst^^
lg, freakC++
-
freakC++ schrieb:
In jeder Software Engineering Vorlesung lernst Du, dass frei in der Welt herumfliegende Funktionen nicht gut sind.
Ne. Nur in denen, wo Propaganda für Java gemacht wird. Aber aus Interesse, was war die Begründung? Gabs überhaupt eine oder sind Behauptungen dort grundsätzlich richtig?
freakC++ schrieb:
Zwar wird nur auf "delimiter zugegriffen, was ehrlich gesagt ein char ist, aber die Funktion gehört logisch zur Aufgabe der Klasse.
Impliziert noch keine Member-Funktion.
freakC++ schrieb:
Außerdem habe ich dann den Namen der Funktion nicht vergeben.
Der Name ist nicht vergeben. Überladung, Namensräume, cpp-lokale Deklarationen...
Lies vielleicht bei Gelegenheit diesen Artikel. Freie Funktionen sind ein wichtiges C++-Sprachmittel.
-
freakC++ schrieb:
Ich hoffe, dass duch dich nur auf das Parsen beziehst. Ich baue gerade einer BigInteger Klasse, die ihre Ergebniss zum Schluss in einem String zurückgibt. Alle Zwischenergebnisse werden nach und nach in einen stringstream geschoben. So geht es am einfachsten.
Ja, das ist langsam. Einen std::string nehmen und immer push_back(dezimalziffer) aufrufen ist schneller (vor allem, wenn du die Endgrösse abschätzen kannst und reserve aufrufst). Fällt aber vermutlich kaum ins Gewicht, weil du eine aufwändige Umrechnung durchführen musst.
-
eSsThehelL schrieb:
23:24:28:10:29:18:12:24:11:27:10:22:10:20:28
soll
28:20:10:22:...
werden. Daher kann nicht einfach ein reverse machen. Deswegen ist mein Code etwas komplizierter. Sieht jemand das Problem?
Sollte doch nur eine kleine Änderung in deinem Code sein sein, nicht?
Nein, das macht ein vernünftiger Mensch so wie knivil gesagt hat. Die Streamlösung zielt voll auf formatierte Interpretation der Zahlen ab.
und als Demonstration für Skym0sh0, wie es ohne drei Umwege und trotzdem universell gehen könnte. Damit ist das eine ganz wunderbare Lösung, die kaum einen Prozessortakt zu viel verschwendet, dabei aber universell auf viele Probleme angewandt werden kann.

Ich habe mal deine Lösung mitstd::string foo = "!nebeirhcseg streawkceur remmi nedrew netfahcstoB ehcsinataS"; for (int i=0; i<20; i++) foo = foo + foo;getestet und ich erhalte:
reverse string + streambuf: 3.30s user 0.16s system 99% cpu 3.467 total seppjs reversed streambuf : 2.93s user 0.10s system 99% cpu 3.034 total iterator + for-loo p : 0.78s user 0.10s system 99% cpu 0.883 totalOooh, wie unerwartet. istream hält einen eigenen buffer, ostream hält einen buffer und formatierter Input ist teuer.
Äpfel und Birnen? Aber selbst da ist es trivial anpassbar, da du unformatierte Lesefunktionen nutzen kannst. Das ist ja gerade die schöne Flexibilität. Oh Wunder, schon ist es gleich schnell.
Meine Aussage: IOstream-Parsen ist sehr langsam und für Nicht-Whitespace-getrennten-Standarddatentypen-Input enorm umständlich. (Bei langsamen Festplatten fällt das Parsen vielleicht weniger ins Gewicht, aber wenn der Input schon im RAM ist, ist stringstream der Tod)
Langsam im Gegensatz zu was? Wie kriegst du formatierte Ausdrücke sonst geparsed? Du vergleichst hier dauernd plumpe, unformatierte Wiedergabe der Zeichen mit formatierten Lesefunktionen. Also volle Bestätigung, dass du überhaupt nicht kapiert hast, was die Streamlösung überhaupt macht.
Zu deiner Lösung meines exotischen Problems: Ähh, du hast es nicht gelöst. Es geht nicht darum, die Zeichen auszugeben. Ich will die Zahlen haben! Wir wollen damit rechnen.
Das ist deine Interpretation fürs Problem.
Nein. Da gibt es nichts zu interpretieren. Die Aufgabe ist es, Zahlen zu parsen
aus beliebiger Iteratorquelleeinem forward_list<char>::const_iterator.Klar ist eine Streamlösung einfacher, wenn wir Parsen wollen. Das war aber nie gefragt.
Wenn du nun beginnst, num_get nachzuprogrammieren: Holzweg. Dann fang ich an, Fließkommazahlen hexadezimal in Scientificschreibweise mit Vorzeichen in die Liste zu packen und es soll immer noch funktionieren. Bitte eine universelle Lösung, die meiner ebenbürtig ist.
Nimmstu std::stoi.
Und die Zeichen bekommst du wie in strtoi?
Ich wiederhole: Wie würdest du die eigentliche (im Gegensatz zur ursprünglichen) Aufgabe von freakC++ lösen?
Siehe oben.
-
SeppJ schrieb:
Äpfel und Birnen? Aber selbst da ist es trivial anpassbar, da du unformatierte Lesefunktionen nutzen kannst. Das ist ja gerade die schöne Flexibilität. Oh Wunder, schon ist es gleich schnell.
Zeig sie doch, deine triviale Anpassung. Ich wüsste nicht, wie das trivial möglich wäre.
-
eSsThehelL schrieb:
SeppJ schrieb:
Äpfel und Birnen? Aber selbst da ist es trivial anpassbar, da du unformatierte Lesefunktionen nutzen kannst. Das ist ja gerade die schöne Flexibilität. Oh Wunder, schon ist es gleich schnell.
Zeig sie doch, deine triviale Anpassung. Ich wüsste nicht, wie das trivial möglich wäre.
Die Änderung wäre im Anwendungscode! Keine Anpassung meines Codes. Dein Schrottvergleich soll keine Äpfel mit Birnen vergleichen. Aber das weißt du auch selber. Du möchtest dich bloß aufspielen.
-
SeppJ schrieb:
eSsThehelL schrieb:
SeppJ schrieb:
Äpfel und Birnen? Aber selbst da ist es trivial anpassbar, da du unformatierte Lesefunktionen nutzen kannst. Das ist ja gerade die schöne Flexibilität. Oh Wunder, schon ist es gleich schnell.
Zeig sie doch, deine triviale Anpassung. Ich wüsste nicht, wie das trivial möglich wäre.
Die Änderung wäre im Anwendungscode! Keine Anpassung meines Codes. Dein Schrottvergleich soll keine Äpfel mit Birnen vergleichen. Aber das weißt du auch selber. Du möchtest dich bloß aufspielen.
Naja, ich bezweifle
1. Dass es annähernd so schnell ist wie der direkte Weg
2. Dass es ein trivialer Anwendungscode wird (der schnellste Weg wäre, wieder über istreambuf_iterator zu gehen)Ich weiss nicht, weshalb du dich so sträubst, deine Behauptungen zu unterlegen. Zumal du es ja offensichtlich schon gemacht hast, um die Geschwindigkeit zu vergleichen.
-
"delimiter" ist ein Klassenattribut
Wie gross ist der Aufwand, es als Funktionsparameter zu uebergeben.
In jeder Software Engineering Vorlesung lernst Du, dass frei in der Welt herumfliegende Funktionen nicht gut sind
Lol, das ist abhaengig von Paradigma und Sprache. Sicher schreiben deine Profs. auch permanent Programme. Fuer C++ gilt das eben nicht. Kannst gerne Artikel von Herb Sutter, Scott Meyers oder Stroustrup konsultieren. Die wissen es besser.
Nichts, aber ich brauche sie nie wieder.
Das gleiche gilt fuer die Methode deiner Klasse.
-
eSsThehelL schrieb:
1. Dass es annähernd so schnell ist wie der direkte Weg
Dann bezweifel eben. Meinungen zählen nicht bei objektiven Fakten.
2. Dass es ein trivialer Anwendungscode wird (der schnellste Weg wäre, wieder über istreambuf_iterator zu gehen)
Wenn du derart schlecht mit Streams umgehen kannst, dass dir dazu nur ein Streamiterator einfällt, dann erklärt das einiges.
Ich weiss nicht, weshalb du dich so sträubst, deine Behauptungen zu unterlegen.
Weil ich dazu deinen Anwendungscode bräuchte. Ich weiß nicht einmal, was du überhaupt genau gemacht hast, außer, dass du formatiertes Lesen mit Direktzugriff verglichen hast. Wie jeder große Experte, der alle Feinheiten verstanden hat.
-
SeppJ schrieb:
Dann bezweifel eben. Meinungen zählen nicht bei objektiven Fakten.
Der "objektive Fakt" ist hier deine Meinung, die völlig frei in der Landschaft steht.
SeppJ schrieb:
Wenn du derart schlecht mit Streams umgehen kannst, dass dir dazu nur ein Streamiterator einfällt, dann erklärt das einiges.
Dann klär mich auf.
SeppJ schrieb:
Ich weiß nicht einmal, was du überhaupt genau gemacht hast, außer, dass du formatiertes Lesen mit Direktzugriff verglichen hast. Wie jeder große Experte, der alle Feinheiten verstanden hat.
Du siehst meinen Code und du hast deinen Code (der auf der 1. Seite). Mein Code macht genau das gleiche, was dein Code macht. Ob sich das jetzt formatiert nennt oder nicht, ist irrelevant. Mein Code nutzt Iteratoren (=schneller) und ist mindestens genauso klar.
// gegeben: SeppJs ausgedachtes Problem std::string foo = "!nebeirhcseg streawkceur remmi nedrew netfahcstoB ehcsinataS"; for (int i=0; i<20; i++) foo = foo + foo; // dein Code std::ostreambuf_iterator<char> out(std::cout); auto& ct = std::use_facet<std::ctype<char>>(std::locale()); auto nospace = [&ct](char c){return !ct.is(std::ctype_base::space, c);}; for (auto it=foo.rbegin(), end=foo.rend(); it != end; *out++ = ' ') it=copy_until(std::find_if(it,end,nospace),end,out,nospace); // Sehr allgemeine Hilfsfunktionen wie dein Streambuf (zählen nicht zum Code) template <typename In, typename Out, typename Pred> In copy_until(In from, In to, Out out, Pred pred) { while (from != to && pred(*from)) *out++ = *from++; return from; }Code mit Streams hat also keinen Vorteil gegenüber Iteratoren. Vor allem, wenn man sich Hilfsfunktionen wie ignore_spaces schreibt. Dann kann Iteratorencode auch Sachen, die Streams nicht wirklich können. Schreib du mal Streamcode für das zweite Problem:
// gegeben: freakC++s Problem std::string foo = "eins:zwei:drei:vier:fuenf:sechs:"; for (int i=0; i<20; i++) foo = foo + foo; foo.pop_back(); // dein Code std::ostreambuf_iterator<char> out(std::cout); for (auto it=foo.rbegin(), end=foo.rend(); it != end;) { auto a = it.base(), b = (it = std::find(it,end, ':')).base(); copy(b, a, out); if (it != end) { ++it; *out++ = ':'; } // Kein ':' am Schluss }