end-Iterator decrement definiert?
-
Ich denke aber es wäre einfacher, nachzugeben
Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen, dann müssen schon viele Leerzeichen dabei sein...
-
Nathan schrieb:
Sorry, Nexus aber:
c++11-coder schrieb:
Nathan schrieb:
Stimmt, falsche Reihenfolge
begin != end && (do_sth(*begin), iterate(++begin, end), true);
Mein Fehler.
Immer noch UB, allerdings sehr subtil: do_sth könnte einen Typ zurückgeben, der den operator, überladen hat. Dann ist do_sth(*begin) nicht mehr vor iterate(++begin, end) gesequenced, was bedeutet, dass *begin und ++begin nicht gesequenced sind, was dazu führen kann, dass do_sth(*(begin+1)) aufgerufen wird, was einerseits falsch ist und andereseits UB für begin+1==end.
</OT>Die Tricks vom IOCCC funktionieren wohl wirklich nur mit C.
cast nach void hilft.
-
c++11-coder, kannst du etwas mehr über
range()
verraten? Basiert das auf Boost.Range oder hast du das selbst gestrickt?Ich frage mich vor allem deswegen, weil
for (auto e : r)
davon ausgeht, dass für den Typ von
r
einbegin()
undend()
(global oder als Member) vorhanden ist, und diese beiden Funktionen jeweils Iteratoren zurückgeben.Damit aber so etwas möglich wäre wie in der verschachtelten Schleife, wo
e
selbst ein Iterator (und kein Element) ist, müsstest du eine zusätzliche Indirektion einbauen. Machst du das tatsächlich oder war es mehr eine Idee?
-
Ich habe mir mein range() zwar selber geschrieben, aber neu ist die Idee nicht. Du könntest auch boost::irange verwenden:
#include <iostream> #include <string> #include <boost/range/irange.hpp> int main() { for (int i : boost::irange(0,5)) std::cout << i << '\n'; std::string s = "abcdef"; using std::begin; using std::end; for (auto i : boost::irange(begin(s), end(s))) for (auto j : boost::irange(std::next(i), end(s))) std::cout << *i << *j << '\n'; }
Ausgabe:
0 1 2 3 4 ab ac ad ae af bc bd be bf cd ce cf de df ef
Dieser range() ist eine schöne Ergänzung zum normalen range based for, weil das for eine Indirektion wegnimmt, man aber manchmal die Iteratoren eben doch braucht. Und es funktioniert super mit int-Ranges.
-
Arcoth schrieb:
Ich denke aber es wäre einfacher, nachzugeben
Jup, ich gebe nach. War alles Blödsinn. Auch wenn ich while-Schleifen einfach deutlich mehr mag, als for-Schleifen. Ich mag es halt nicht, wenn mehrere Dinge auf eine Zeile fallen,
Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!
-
volkard schrieb:
Du magst for nicht so gern, NUR WEIL Du for mißbrauchst, Alda!
Jetzt halt schon die Schnauze mit deinem Assi-Dialekt! Bist du erst 25 oder was?
Und ich missbrauche
for
-Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:for( unsigned i = 2; i != primes.size(); ++i )
Für nichts anderes. Außer vielleicht
for( unsigned i; std::cin >> i; ) //...
Wobe ich da schon eher zu
while
tendiere.
-
Arcoth schrieb:
Und ich missbrauche
for
-Schleifen nie! Ich nutze sie tatsächlich lediglich für das Standardschema. Beispiel:for( unsigned i = 2; i != primes.size(); ++i )
Für nichts anderes.
Aha...
Arcoth schrieb:
for (auto first = begin(v); first != end(v); ++first) // Hier sollte die Abbruchbedingung wahrscheinlich eher first != --end(v) lauten for( auto second = first; ++second != end(v); ) doSomething( *first, *second );
Das ist mMn. deutlich eleganter.
-
Arcoth schrieb:
Allerdings kann man dieses temporäre Objekt gleich selbst in die Hand nehmen
… for( auto second = first; ++second != end(v); ) doSomething( *first, *second );
Das ist mMn. deutlich eleganter.
-
Aha...
Ups! Ich sollte es natürlich nicht so formulieren. Ich nutze es auch zum Iterieren mit Iteratoren. Standardschema hieß oben
for( Initialisieren; Vergleich; Inkrement )
wie immer.Außerdem habe ich nicht gesagt, dass
for( auto second = first; ++second != end(v); )
die eleganteste Methode ist. Es ist nur so: Ich finde es schöner als
for( auto second = std::next( first ); second != end(v); ++second )
,
aber wahrscheinlich würde ich in meinem Code eherwhile
benutzen. Und zwar soauto second = first; while( ++second != end(v) )
Weil ich for, sobald es von obigem Schema abweicht, einfach nicht mehr attraktiv finde. Bei while kann die Bedingung auch gut und gerne ein wenig komplizierter werden.
Nun fand ich bei dem konkreten Beispiel doof, dass man die Klammern ergänzen muss, wenn man
while
verwendet (weil man noch eine Zeile zum Definieren vonsecond
braucht)! Ich versuche immer möglichst ohne diese Klammern auszukommen - das ist ein Tick von mir
-
Arcoth schrieb:
for( auto second = std::next( first ); second != end(v); ++second )
Ein Bißchen Syntax-Rauschen weg
for( auto second=next(first); second!=end(v); ++second )
und schon isses hübsch.
Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.
-
Arcoth schrieb:
...
for( auto second = first; ++second != end(v); )
Man sollte nur unter zwingenden Gründen von der Standard-Funktionalität der drei
for
-Argumente abweichen. Das "clevere"++second
ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben: Man muss erst einmal die volle Tragweite des ++ an dieser Stelle erfassen! Der Großteil der Leser wird auf den ersten Blick wohl nicht korrekt erfassen, das die Schleife tatsächlich tut.Geht in Richtung "Principle of least surprise". Da muss das persönliche Schönheitsempfinden manchmal weichen, vor allem, wenn man nicht allein entwickelt.
Arcoth schrieb:
auto second = first; while( ++second != end(v) )
... einfach nicht mehr attraktiv finde.
Dito. Jedes Handbuch über guten Code sagt, dass
for
nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplenwhile
. Du ziehst eine Mutter auch nicht mit der Kombizange an, wenn du den passenden Maulschlüssel hast, oder?Dazu kommt noch was anderes:
second
steht außerhalb des Scopes der Schleife, beifor
innerhalb.
-
Arcoth schrieb:
auto second = first; while( ++second != end(v) )
... einfach nicht mehr attraktiv finde.
Dito.
Lern lesen und zitieren, Junge.
Edit: Oder nur zitieren. Ich weiß nicht genau, zu was du zustimmst, aber das Zitat ist falsch.Das "clevere" ++second ist eine versteckte Bombe für alle anderen (und dich selbst in 3 Jahren), die mit dem Code zu tun haben:
Ich werde auch in dreißig Jahren sofort sehen, was
++second
da verloren hat, da bin ich mir verdammt sicher
-
Arcoth schrieb:
Lern lesen und zitieren, Junge.
Lern vielleicht ein bisschen den Ball flachzuhalten. Wir sind hier nämlich nicht mehr 25.
Es geht um die Verbindung von Schönheitsempfinden und "attraktiv finden". Zitat vielleicht missglückt, aber das war der Hintergrund. Dass es darum geht, den Code auch für andere so klar wie möglich zu schreiben.
Arcoth schrieb:
Ich werde auch in dreißig Jahren sofort sehen, was
++second
da verloren hat, da bin ich mir verdammt sicherDu vielleicht. Und deine Kollegen?
-
minastaros schrieb:
Jedes Handbuch über guten Code sagt, dass
for
nicht nur eine Art von Schleife ist, sondern bereits deren Aufgabe beschreibt, nämlich über eine bestimmte Menge der Reihe nach zu iterieren. Allein durch dieses Schlüsselwort hat der Leser bereits mehr Information als bei einem simplenwhile
.Jo. Sehe ich in sonst feinem ein for, weiß ich, daß über die ganze Sequenz iteriert wird. Welche Sequenz ist meist eh aus dem Kontext klar. Da bereite ich mich schon auf den Kern der Schleife vor, während ich der Vollständigkeit halber noch Initialisierungsausdruck lese, und spätestens dann weiß ich, daß es die Schleife war, die ich meinte und steige in den Körper ein, während ich noch halbherzig überfliege, daß die Laufbedingung stimmt und den Weiterschaltungsausdruck, kann ja nur ++i oder p=p->next oder so sein, falls nicht, fällt es mir nachträglich auf, obwohl die Augen schon unten sind: "Da hat doch gerade was nicht gestimmt?!", dann schalte ich vom 5. Gang in den 1. Gang zurück und habe einen Getriebeschaden. Und der ganzen Funktion oder der ganzen Datei stehe ich ein Weilchen mit Mißtrauen gegenüber.
-
Du vielleicht. Und deine Kollegen?
Welche Kollegen? Ich arbeite mit niemandem, und das habe ich auch noch nie (das erklärt einiges, was?).
Ich würde nicht in einem Projekt Code schreiben, der nicht gängigen Idiomen folgt. Vor allem gibt es in (so gut wie) allen Firmen und größeren Projekten ganz feste Regeln, nach denen Code geschrieben wird - von der Formatierung bis eben zu Dingen wie for-Schleifen. Da dürfte ich solche Schleifen möglicherweise nicht schreiben.
Auch wenn ich nach wie vor nicht verstehe, wie man diese beiden Zeilen
unsigned i = 0; while( ++i < 10 )
Nicht sofort verstehen kann. Ist doch nicht so schwer. Strengt ihr euch beim Code lesen nicht an? Auch wenn ich persönlich natürlich für Skalare
for( unsigned i = 0; i != 10; ++i )
schreibe, und zwar immer. Bei Skalaren nutze ich praktisch nur
for
-Schleifen. Für so etwas schreibe ich nicht einmal eine while-Schleife, wenn ich die Schleifenvariable nachher brauche, dann schreibeunsigned i = 0; for(; i != 10; ++i )
Das ist alles eindeutig.
Lern vielleicht ein bisschen den Ball flachzuhalten. Wir sind hier nämlich nicht mehr 25.
Tut mir Leid.
(Die Meisten hier sind übrigens recht jung, höchstens 30)Da muss das persönliche Schönheitsempfinden manchmal weichen
Wirklich? Das glaube ich gar nicht. Nämlich dass mein Schönheitsempfinden von dem der Menge abweicht. Ich versuche möglichst lesbaren Code zu schreiben, gerade bei Schleifen. Und ich erwarte nichts, wenn ich Code lese, jede Zeile kann einem völlig neuem Muster folgen.
Vor allem hatte ich nicht erwartet, dass man (bspw. Volkard) einen Getriebeschaden erleidet, wenn die Schleife im Code nicht aussieht wie 99% der anderen. Das muss ich mir auf jeden Fall merken.
kann ja nur ++i oder p=p->next oder so sein,
Moment mal! Für so etwas verwendet man doch keine for-Schleife! Bei Listenknoten sollte das doch definitiv ans Ende des Körpers ausgelagert werden. So habe ich es aufgefasst, nachdem du auf Schleifen so penibel achtest.
Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.
Was genau daran assoziierst du mit C?
-
Arcoth schrieb:
Du vielleicht. Und deine Kollegen?
Welche Kollegen? Ich arbeite mit niemandem, und das habe ich auch noch nie (das erklärt einiges, was?).
Naja, das mit den Kollegen ist ein verdammt gutes Modell, um selber Code zu schreiben, den man in einem halben Jahr noch versteht. Schreibe so, daß Dein Kollege, der nicht eingearbeitet ist, es lesen kann, dann kannst Du es auch in einem halben Jahr lesen. Schreibe ihn so, daß der Azubi ihn lesen kann, dann kannste ihn auch lesen, wenn Du mal einen schlechten Tag hast. Machst ja auch bei Schnittstellen diese Vorgehen, machst alle private und so, vermeidest Redundanz, tust Liskov-Prinzip, Regel der großen 3-5, und endlos mehr. Obwohl nur Du selber der Benutzer der Klassen bist, programmierst Du gegen einen hypothetischen Idiotenbenutzer, gegen Dich. Und Viola, Du läßt unglaublich viele spätere logische Fehlerchen vom Compiler rausfischen dadurch. Unit-Tests? Ist was für Java-Leute, damit sie mit fünffacher Arbeit nur noch doppelt so viele Fehler rauslassen wie wir, wenn wir der Nase nach programmieren (bevor jetzt Aufschreie kommen, wir können Unit-Tests auch machen bei Bedarf je nach Anforderungen).
Arcoth schrieb:
Auch wenn ich nach wie vor nicht verstehe, wie man diese beiden Zeilen
unsigned i = 0; while( ++i < 10 )
Nicht sofort verstehen kann. Ist doch nicht so schwer. Strengt ihr euch beim Code lesen nicht an?
Naja, mich hat's vielleicht viel stärker erwischt.
Mit dem 64-er aufgewachsen. Keine lokalen Variablen, kein new/delete. Pascal habe ich übersprungen, war auch gut so. Dann C auf dem PC. Von BASIC nach C war schon nicht leicht, aber auf einmal konnte ich "alles" machen in der Hochsprache! Geil! (Naja, gerade mal Maustreiber-Callbacks bauen und absetzen.) Voller Begeisterung schrieb ich auch mal einen Datenkomprimierer. Warum PK-Zip, wenn ich es vielleicht besser kann?
Habe mir ein schon nettes Verfahren überlegt. Und das hatte ich so auch Papier an kleinen Beispielen auch gemacht. Und reingeknüppelt in die Tastatur habe ich es auch. Es hat nicht so stark komrpimiert, wie ich es mir vorstellte…
Vielleicht einen Monat lang dran gerätselt, dies und das probiert, nöö, es wurde nicht besser. Verflixt!Studienbeginn!! Echt keine Zeit gerade für sowas!
Halbes Jahr später sah es so aus: Ich wusste noch haargenau wie mein Algo funktionierte. Nebenbei bemerkt, das weiß ich heute noch ebenso genau. Nur vom Code konnte ich bis aig triviale Sachen wie "void main()" oder includes keine eine Zeile mehr entzuffern, was ich vor einem nur halben Jahr damit beabsichtigt hatte. Auch nicht durch eine Woche Anstarren des Codes. Er war komplett für die Tonne.
Und sowas mag ich nicht mehr. Code von mir seit 1995 kann ich flüssig lesen, seitdem schreibe ich kompromisslos gegen mich selber.
Tja, das ist mein persönlicher Spleen. Sozusagen ein Engramm, wenn ich Dianetiker wäre. Darum folge ich Dir auch nicht in die Tiefen der TMP, sondern warte lieber eine Sprache ab, wo man mit der Sprache selber Compilezeitsachen machen kann, ohne, daß sie Lispelt.
Arcoth schrieb:
volkard schrieb:
Nee, mal ehrlich, eine komplexte Laufbedingung nervt Dich weniger als eine komplexe Initialisierung, das finde ich irgendwie C.
Was genau daran assoziierst du mit C?
Sterne-Programmierer. Im frühen C war die Begeisterung enorm, mit wenigen Zeichen komplexe Sachen auszudrücken. Wo man mit ASM 20 Befehle brauchte oder in BASIC 5 Zeilen, war man mit einer "einfachen" Zeile in C fertig. Krass! Geniale Sprache!
-
Arcoth schrieb:
Welche Kollegen? Ich arbeite mit niemandem, und das habe ich auch noch nie (das erklärt einiges, was?).
Ja, das merkt man.
Der Vergleich von Volkard gefällt mir: Mein zukünftiges Ich ist selbst mein erster Kollege.
Arcoth schrieb:
Auch wenn ich nach wie vor nicht verstehe, wie man diese beiden Zeilen
unsigned i = 0; while( ++i < 10 )
Nicht sofort verstehen kann. Ist doch nicht so schwer. Strengt ihr euch beim Code lesen nicht an?
Nochmal: Es geht nicht um "verstehen". Code arbeitet auf mehreren Ebenen, die reine Funktionalität ist nur die unterste. Technisch gesehen funktionieren alle Lösungen, keine Frage.
Aber darüber kommt die Semantik. Nicht: was tut der Code, sondern was will er im gröberen Kontext sein? Und da ist, wenn ich es noch härter ausdrücke,
while
an dieser Stelle einfach das falsche Tool.While: Ein bestimmter Block wird mehrfach wiederholt, bis oder solange eine bestimmte allgemeine Bedingung eintritt.
For: Es gibt eine Menge (pure Zahlen, ein Container, eine Liste usw.) und auf jeden Fal eine Laufvariable, im Allgemeinen deren Index oder Iterator. Und Aufgabe dieses Code-Blockes ist es, über die Menge zu iterieren (das kann auch rückwärts oder in 2er-Schritten sein, aber im Prinzip bleibt es beim Iterieren).
Genauso, wie man
for
nicht für andere Dinge missbrauchen darf (z.B. in der Laufbedingung etwas ganz anderes prüfen, oder im Fortsetzungsblock Dinge tun, die in den Schleifenkörper gehören usw.), "lügt" der Code, wenn einwhile
da steht, es aber in Wirklichkeit um eine Iteration geht.Der Punkt ist, dass man beim Lesen (Faustregel: Code wird 10% geschrieben, aber 90% gelesen; von mir, von anderen, von dem, der unter Zeitdruck einen Fehler finden muss usw.) oft nur durch schnelles Überfliegen etwas sucht oder verstehen muss, weil die Zeit fehlt, ins Detail zu gehen. Ist wie beim Fachbuch die Schlagwörter zu überfliegen oder die Überschriften. Wenn ich dann im Code sehe, aha, ein Container, und dann ein
for
, weiß ich schon im Prinzip, was passieren wird und brauche die Details womöglich erstmal nicht. Beiwhile
geht das nicht, zumindest nicht so schnell, weil ich auf jeden Fall die Laufbedingung durchlesen muss. Das ist dass, was mit "Getriebeschaden" gemeint war: aus der schnellen Geschwindigkeit runterbremsen.Arcoth schrieb:
Tut mir Leid.
(Die Meisten hier sind übrigens recht jung, höchstens 30)Akzeptiert. Ist eine Frage der Höflichkeit, weniger des Alters (die 25 war von dir - hab ja nur, äh, zitiert.)
Es macht Spaß, auch über kleinste Details heftig zu debattieren. Aber es sollte sachlich bleiben.
-
Arcoth schrieb:
Nicht sofort verstehen kann. Ist doch nicht so schwer. Strengt ihr euch beim Code lesen nicht an?
Nein. Wenn ich Probleme hab, den Code zu lesen, dann wird in der Logik sicher ein Fehler sein, also lohnt sich das Code verstehen nicht. Dann schreib ich den Block gleich neu.
-
Gut, ich habe ein Beispiel: Ich implementiere gerade Tonelli-Shanks. Dabei kommt für den zweiten Schritt, also einen quadratischen Nichtrest modulo p zu finden, bei mir folgende Schleife dran:
unsigned z = 1; while( bin_mod_exp( ++z, (p-1)/2, p ) == 1 ); // per square-and-multiply, weil es größere p sind
Hierbei ist der Startwert von z im Prinzip 2, das verstecke ++ in der Schleife.
Es ginge auchunsigned z = 2; for(; bin_mod_exp(z, (p-1)/2, p) == 1; ++z );
Also welches davon soll ich bevorzugen?
-
Arcoth schrieb:
Gut, ich habe ein Beispiel: Ich implementiere gerade Tonelli-Shanks. Dabei kommt für den zweiten Schritt, also einen quadratischen Nichtrest modulo p zu finden, bei mir folgende Schleife dran:
unsigned z = 1; while( bin_mod_exp( ++z, (p-1)/2, p ) == 1 ); // per square-and-multiply, weil es größere p sind
Hierbei ist der Startwert von z im Prinzip 2, das verstecke ++ in der Schleife.
Es ginge auchunsigned z = 2; for(; bin_mod_exp(z, (p-1)/2, p) == 1; ++z );
Also welches davon soll ich bevorzugen?
Weder noch!
Die for-Schleife hat ne komplexe Suchbedingung,
die for-Schleife hat ihre triviale Initialisierung ausgelagert,
die while-Schleife (do vergessen?) macht ++ in einem komplexeren Ausdruck,