Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - String verarbeiten problem
MagicRain - Do 25.06.09 19:19
Titel: String verarbeiten problem
Hallo ich möchte folgendes mit einem langen String machen....
Quelltext
1: 2: 3: 4: 5:
| C92917D2E4D98789EE51928EECA075E8K3NDI45JKS90OMLJD56JLSDJ7LSK6DK .........
wird zu:
C929-17D2-E4D9-8789-EE51 ...................... |
Habe es mal so versucht aber das kalppt nicht so ganz wie ich es möchte :(
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| procedure TForm1.Button1Click(Sender: TObject); var a,b:String; begin a := Edit1.Text; a[5] :='-'; Edit2.Text := a; end; |
Wäre nett wenn mir jemand da irgendwie helfen kann?!?!
MfG
Moderiert von
Narses: Topic aus Sonstiges (Delphi) verschoben am Do 25.06.2009 um 20:00
jfheins - Do 25.06.09 19:29
Etwa so in der Art:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| var orginal, neu: String; begin i := 1; j := 1; while i <= length(orginal) do begin neu[j] := orginal[i];
inc(j);
if (i mod 5 = 0) and (i <> length(orginal)) then begin neu[j] := '-'; inc(j); end;
inc(i); end; |
elundril - Do 25.06.09 19:33
is zwar jetzt n bissal code mit pseudocode vermischt aber ich denke es wird dir helfen:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| procedure TForm1.Button1Click(Sender: TObject); var orginal, neu: String; begin original := Edit1.Text; i := 1; j := 1; während i+4 <= länge(orginal) do begin kopiere von der stelle i bis i+4 den text aus original in neu; füge am ende von neu ein '-' an; inkrementiere i um 4; end; end; |
MagicRain - Do 25.06.09 19:49
Oh man danke euch :)
MfG
Delete - Do 25.06.09 19:53
Oder gleich in eine Funktion gepackt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| function InsertGivenChar(const Src: string; InsChr: char; InsertAfter: integer): string; var i,j: integer; begin SetLength(Result,Length(Src) + Length(Src) div InsertAfter); i := 1; j := 1; while i <= Length(Src) do begin Result[j] := Src[i]; inc(j); if ((i mod InsertAfter) = 0) and (i < Length(Src)) then begin Result[j] := InsChr; inc(j); end; inc(i); end; end; |
[edit] Denkfehler korrigiert, das kommt davon, wenn man direkt im Editor tippt. [/edit]
elundril - Do 25.06.09 22:07
darf ich fragen warum ihr beide die Copyfunktion von Delphi so verschmäht?
lg elundril
(Die Befehlssyntax ist folgende: anyString := Copy(anotherString, Index, Count);)
Lannes - Do 25.06.09 22:57
Hallo,
nicht die Copyfunktion wird verschmäht, sondern die durch Copy erforderlich werdenden Stringverkettungen.
jaenicke - Fr 26.06.09 08:19
Wenn es um Geschwindigkeit geht:
Da sollte man dann erstens keine while-Schleife nehmen, bei der in jedem Durchlauf Length aufgerufen wird und zweitens nicht auf Strings indiziert zugreifen, da bei diesem zeichenweisen Zugriff jedes Mal der Referenzzähler überprüft wird. ;-)
@
DeddyH: Deine Methode verschluckt das letzte Zeichen, nämlich das mit dem Index Length(Src), denn da brichst du schon ab, stattdessen steht da ein zufälliges Zeichen drin. ;-)
Wenn die Stringlänge ein Vielfaches der Länge der Einzelteile, hier also 4, ist, dann geht es auch so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| function InsertHyphens4A(const AText: AnsiString): AnsiString; var i, TextLen: Integer; Src, Dst: PAnsiChar; begin TextLen := Length(AText); SetString(Result, nil, TextLen + Pred(TextLen div 4)); Src := PChar(AText); Dst := PChar(Result); for i := 1 to Pred(TextLen div 4) do begin PLongWord(Dst)^ := PLongWord(Src)^; Inc(Src, 4); Inc(Dst, 4); Dst^ := '-'; Inc(Dst); end; PLongWord(Dst)^ := PLongWord(Src)^; end; |
Oder allgemeiner und auch für Unicode in D2009, dafür langsamer:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| function InsertHyphens(const AText: string; AChars: Integer): string; var i, TextLen: Integer; Src, Dst: PChar; begin TextLen := Length(AText); SetString(Result, nil, TextLen + Pred(TextLen div AChars)); Src := PChar(AText); Dst := PChar(Result); for i := 1 to Pred(TextLen div AChars) do begin CopyMemory(Dst, Src, AChars * SizeOf(Char)); Inc(Src, AChars); Inc(Dst, AChars); Dst^ := '-'; Inc(Dst); end; CopyMemory(Dst, Src, AChars * SizeOf(Char)); end; |
MagicRain - Fr 26.06.09 12:25
So danke euch für die ganzen Beiträge richtig klasse aber ich habe die Lösung dann doch gefunden viel einfacher!
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| Insert('-',STRING1,5); Insert('-',STRING1,10); Insert('-',STRING1,15); Insert('-',STRING1,20);
SetLength(STRING1, 24); |
MfG
Delete - Fr 26.06.09 12:28
Klar, das passt für diesen Fall, aber ist nicht variabel, falls man das nochmal braucht ;)
jaenicke - Fr 26.06.09 12:36
Und ist natürlich sehr viel langsamer, also nur geeignet, wenns nur einmal oder so ausgeführt wird. In einer Schleife wäre sowas kaum sinnvoll. :D
MagicRain - Fr 26.06.09 13:01
Ja da muss ich euch rechtgeben :) nun habe ich ja viele lösungen danke :)
MfG
BenBE - Fr 26.06.09 13:10
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| Function InsertDashes(S: String): String; var X: Integer; SP,DP: PChar; Begin SetLength(Result, Length(S) + Length(S) div 4); SP := @S[1]; DP := @Result[1]; For X := 1 to Length(S) do Begin DP^ := SP^; Inc(SP); Inc(DP); If (X and 3) = 0 then Begin DP^ := '-'; Inc(DP); end; end; end; |
Die Übersetzung nach ASM überlasse ich dem geneigten Leser ...
delfiphan - Fr 26.06.09 19:10
Wartbarkeit, Lesbarkeit, Robustheit, Portierbarkeit gegen unnütze Performance macht hier keinen Sinn. For-Schleife mit Insert o.ä. ist dagegen einfach zu verstehen, praktisch selbsterklärend und macht am wenigsten Annahmen über den Aufbau von Strings.
Wartbarkeit:
Wenn mit Assembler oder Pointern gearbeitet wird: Ist schwieriger zu debuggen, verstehen und erweitern (vor allem von anderen)
Lesbarkeit:
Assembler praktisch unlesbar. Mit Pointern ist es etwas besser aber nicht besonders elegant.
Robustheit:
Schnell mal einen Bug programmiert durch undurchdachter Zugriff über Pointer:
InsertDashes: Macht einen ungültigen Zugriff auf [1], wenn der Eingabestring leer ist.
InsertHyphens4A/InsertHyphens: Schreibt auf PChar(''), wenn der Eingabestring leer ist.
Portierbarkeit:
Assembler ist überhaupt nicht portabel. Falls mit Pointern und/oder CopyMemory gearbeitet wurde: Vorsicht geboten bei Transition von String zu UnicodeString -> Code macht möglicherweise Annahmen über die Struktur von String.
Fazit:
Die Prozedur, die oben keine Pointerarithmetik verwendet ist InsertGivenChar. Die einzige Prozedur, die hier sicher gegen Zugriffsverletzung ist. Allerdings durch die Verwendung von mehreren Indizes etwas unübersichtlich, und enthält einen Denkfehler: Die Prozedur funktioniert nicht wie gewünscht, wenn Length(Src) mod InsertAfter = 0 bei Length(Src)>0
Delete - Fr 26.06.09 20:03
Stimmt, hab es oben verbessert.
alzaimar - Fr 26.06.09 21:32
Oh..Oh..Oderso (Kurz und Knapp):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| Function UnterteileString (Const aString : string; aTeilerZeichen : Char; aAbstand : Integer) : String; Var i : Integer;
Begin i := aAbstand+1; Result := aString; While i <= Length (Result) do begin Insert (aTeilerZeichen,Result,i); inc (i, aAbstand+1); End End; |
BenBE - Fr 26.06.09 21:37
delfiphan hat folgendes geschrieben : |
| Wartbarkeit, Lesbarkeit, Robustheit, Portierbarkeit gegen unnütze Performance macht hier keinen Sinn. For-Schleife mit Insert o.ä. ist dagegen einfach zu verstehen, praktisch selbsterklärend und macht am wenigsten Annahmen über den Aufbau von Strings. |
For-Schleife mit Insert ist dagegen: ultra lahmarschig, strapaziert den Memory Manager unnötig, ist einfach unelegant!
delfiphan hat folgendes geschrieben : |
Wartbarkeit:
Wenn mit Assembler oder Pointern gearbeitet wird: Ist schwieriger zu debuggen, verstehen und erweitern (vor allem von anderen) |
Dafür gibt es Kommentare ... sofern diese nötig sein sollte :mrgreen:
delfiphan hat folgendes geschrieben : |
Lesbarkeit:
Assembler praktisch unlesbar. Mit Pointern ist es etwas besser aber nicht besonders elegant. |
ASM ja, Pointer sind dagegen ein warer Genuss. Je kürzer die Anweisung, desto klarer wird sie.
delfiphan hat folgendes geschrieben : |
Robustheit:
Schnell mal einen Bug programmiert durch undurchdachter Zugriff über Pointer:
InsertDashes: Macht einen ungültigen Zugriff auf [1], wenn der Eingabestring leer ist. |
Falsch: Wenn Input-String leer ist, wird zwar [1] als Adresse aufgelöst, jedoch nie zugegriffen.
delfiphan hat folgendes geschrieben : |
InsertHyphens4A/InsertHyphens: Schreibt auf PChar(''), wenn der Eingabestring leer ist.
Portierbarkeit:
Assembler ist überhaupt nicht portabel. Falls mit Pointern und/oder CopyMemory gearbeitet wurde: Vorsicht geboten bei Transition von String zu UnicodeString -> Code macht möglicherweise Annahmen über die Struktur von String. |
Meine Pointer-Version IST Portabel ;-)
delfiphan hat folgendes geschrieben : |
Fazit:
Die Prozedur, die oben keine Pointerarithmetik verwendet ist InsertGivenChar. Die einzige Prozedur, die hier sicher gegen Zugriffsverletzung ist. |
Jain ... InsertDashes ist auch sicher gegen Zugriffsverletzungen, trotz dem Pointer-Arithmetik verwendet wird. Was jedoch bei meiner Routine wichtig ist, ist die Tatsache, dass man genau wissen sollte, was jeder Befehl tut. Ist also nichts, was man unkonzentriert schreiben sollte ...
delfiphan hat folgendes geschrieben : |
| Allerdings durch die Verwendung von mehreren Indizes etwas unübersichtlich, und enthält einen Denkfehler: Die Prozedur funktioniert nicht wie gewünscht, wenn Length(Src) mod InsertAfter = 0 bei Length(Src)>0 |
Und genau diese doppelten Index-Berechnungen machen InsertGivenChar nicht optimal, da ständig Sachen doppelt und dreifach berechnet werden.
Kommt halt immer drauf an, was man speziell will.
Geht Kürzer:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| Function UnterteileStringShorterSource (Const S : string; C : Char; A : Integer) : String; Var i : Integer; Begin Result := S; i := (Length(S) div A) * A + 1; While i > 1 do begin Insert (C, Result, i); Dec(i, A); End; End; |
Oreaden - Fr 26.06.09 23:04
juten Morgen,
sehe ich das richtig, 'n Wettbewerb? Da will ich doch auch zwei Zeilen in den Ring werfen
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29:
|
procedure InsStr(Var aStr: string; At: integer; Chr: char); var LengthIdx,RunIdx : integer; TmpStr : string; SourceChar, DestinationChar: PChar; begin LengthIdx := length(aStr); if (LengthIdx = 0) or (LengthIdx < at) then exit; TmpStr := aStr; setlength(aStr, LengthIdx + LengthIdx div at); SourceChar := @TmpStr[at]; DestinationChar := @aStr[at]; RunIdx := at; while RunIdx < LengthIdx do begin if RunIdx mod at = 0 then begin inc(DestinationChar); DestinationChar^ := chr; end; inc(RunIdx); inc(SourceChar); inc(DestinationChar); DestinationChar^:= SourceChar^; end; end; |
vielleicht gefällts
Oreaden
delfiphan - Fr 26.06.09 23:16
@BenBE: Ich werde nicht versuchen, dich zu ändern ;) Du darfst gerne deine eigene Meinung behalten. Ich bleibe bei meiner Meinung.
"@S[1]": Okay, gibt vielleicht keine Zugriffsverletzung, aber führt dazu, dass du keine Range-Checks mehr verwenden darfst. Kann man ja ausschalten, würdest du jetzt sagen. Klar, es gibt für alles eine Lösung. Aber in meiner täglichen Arbeit merke ich einfach, dass solche Kleinigkeiten mit der Zeit mühsam sind. Wenn die Hälfte nicht da steht oder eine Menge Annahmen im Code stecken, die nur der Originalautor kennt, dann knallt's halt öfters mal und man muss rumwerkeln, bis es endlich läuft. Im schlimmsten Fall muss ich eine Änderung in Fremd-Code machen, und beim nächsten Update darf ich wieder nachprüfen, ob der Fehler in zwischen schon gefixt ist, oder ob ich die Änderung wieder machen muss.
Ich will in erster Linie ein Programm, eine Prozedur, eine Komponente, was auch immer, das nicht ständig rumzickt sondern einfach funktioniert (Vorausgesetzt ich habe sie richtig verwendet. Bei falscher Verwendung bzw. Gefahr darf sie mich gerne darauf aufmerksam machen).
Kommentare: Guter Code kommt mit sehr wenig Kommentaren aus. Ausserdem machen die wenigsten Leute gute Kommentare. Besser ist es, wenn der Code selbsterklärend ist.
Portabilität: Da gibt es einen fliessenden Übergang zwischen portabel und nicht portabel. Von Delphi 2007 zu Delphi 2009 mag dein Code ohne Änderung laufen. Aber Pointers gibt es nicht überall.
Wenn ich bei einem Code-Review eine Assemblerlösung für dieses Problem sehe würde ich es zurückschicken.
Wie auch immer. Ich fand es nur ironisch, dass hier jeder versuchte, einen schnelleren Code als der vorherige zu schreiben. Dabei war keine Lösung überhaupt wirklich funktionsfähig. Deine übrigens auch nicht, wenn der String zum Beispiel 8-stellig ist.
Aber lassen wir's gut sein. Ich wollte nur darauf Aufmerksam machen, dass hier evtl. die falschen Prioritäten gesetzt werden. Es gibt viele verschiedene Programmier-Philosophien in der Literatur. Unix Philsophie, Extreme Programming Philsophie (YAGNI, OAOO, SPOT), etc.. Meistens heisst es, dass Code-Optimierung an letzter Stelle kommt. Und ich bin damit zu einem grossen Teil einverstanden.
MagicRain - Fr 26.06.09 23:29
Was ist denn jetzt hier passiert krieg ? :D aber danke euch allen!!!
MfG
bole - Sa 27.06.09 15:34
Ich arbeite im 3. Level Support eines grossen Finanzdienstleisters... Genau solche Programmierer machen mir das Leben schwehr weil sie für ein einfaches Problem die komplizierteste Lösung programmieren um noch einen Bruchteil einer Mikrosekunde an Performance zu gewinnen!
Solche Programmierung mag zwar sehr interessant sein, jedoch in komerziell genutzten Programmen hat sie meist nichts verloren. KISS heisst das Stichwort (
http://de.wikipedia.org/wiki/KISS-Prinzip)
jaenicke - Sa 27.06.09 15:48
Es kommt eben immer drauf an wo man sowas einsetzt. In einer Schleife oder einfach als Befehl. Wie ich ja auch schon geschrieben hatte.
Wenn ich bei meinem aktuellen Problem z.B. einfach mit Strings arbeiten würde ohne mit Pointern zu spielen, dann würde das Auslesen einer 350 MiB Datei mehrere Minuten dauern. Mit Pointern stoße ich jetzt eher ans Limit der Festplattengeschwindigkeit und es dauert ca. 10 Sekunden.
Und bei Dateien "normaler" Größe dauert es nur noch wenige Millisekunden statt mehrerer Sekunden.
Dafür muss man sich eben ganz genau überlegen was man wo tut und kann es nicht wie bei einfacheren Prozeduren einfach herunterschreiben.
MagicRain - Sa 27.06.09 16:57
Also da hat jaenicke nunmal recht hehe! Aber genau das ist es ja am Programmieren für das Problem was man hat genau die richtige lösung zu finden... Nicht immer ist eine bestimmte lösung gut für ein bestimmtest problem :)
MfG
BenBE - Sa 27.06.09 17:15
Seh ich ähnlich. Generische Algorithmen für spezielle Probleme einsetzen ist mehr als ineffizient und führt oftmals dazu, dass Programme nicht oder nur schlecht skalieren. Das beste Beispiel für solche Fehlentwicklungen sind riesige Webserver wie der Apache, die zwar alles mögliche können, aufgebläht sind, wie Sau, aber nichts optimal können. Vergleicht man dann dem gegenüber den Apache mit dem Gatling, so kann dieser zwar weniger, das was er kann dafür aber so effizient, dass man teilweise nicht merkt, dass er überhaupt etwas tut.
Genauso ist es mit den Funktionen der RTL von Delphi: Diese mögen zwar für alles Mögliche etwas beitragen, um eine Lösung zu erhalten, wenn man dafür jedoch haufenweise Arbeit doppelt ausführt, verschenkt man unter Umständen mehr als "ein paar Millisekunden". Es gibt für mich 3 Dinge, die ich versuche zu vermeiden: 1. Redundanz, 2. Arbeit 3. Aufrufe an den Speicher-Manager.
Die Nutzung der Funktion Insert mag für einzelne Aufgaben durchaus berechtigt sein, sobald sie aber in Schleifen ausgeführt wird, verursacht sie unnötige Aufrufe an den Speichermanager und macht sich durch das Umkopieren der Strings unnötige Arbeit. Wenn man sich also vor der Ausführung einer Routine überlegt, was diese tun soll und was der minimale Weg ist, dies zu erreichen, kann man oftmals zu recht eleganten Lösungen kommen: Man berechne vorher, wie lang das Ergebnis wird und sorge dann dafür, dass man dieses auf dem kürzesten Wege erreicht. Nichts anderes macht meine Routine InsertDashes...
Ich durfte mich auch bereits in historisch gewachsenen (Qualitätsmerkmal 1) und universell eingesetzten (Qualitätsmerkmal 2) Quelltext einarbeiten. Bei einer Datei habe ich nur kurz drüber geschaut, ein Fest für jedes Komprimierungsprogramm und 3 Fehler (die wurden mir bestätigt, dass das wirklich fehler sind) gefunden und nach kurzem Überlegen eine Möglichkeit gefunden, aus ~9kLOC <500 LOC zu machen, ohne dabei an Funktionalität zu verlieren. Nachteilig daran war einzig, dass der Quelltext dadurch ein wenig langsamer geworden ist - das stört aber nicht, wenn dadurch der Wartbarkeitsgewinn dennoch überwiegt.
Mir geht es also weniger um Performance und wie man es am schnellsten anstellt, sondern auch im Wesentlichen um Eleganz. Oftmals spielt beides aber Hand in Hand.
Was mich von daher also am meisten stört, sind Leute, die anhand von Regeln festmachen, ob Quelltext gut oder böse ist: Sicherlich gibt es hier ein paar Regeln, die einem helfen, diesen möglichst gut zu gestalten; aber Regeln sind nun einmal da, um gebrochen zu werden ;-)
alzaimar - Sa 27.06.09 18:13
BenBE hat folgendes geschrieben : |
| Was mich von daher also am meisten stört, sind Leute, die anhand von Regeln festmachen, ob Quelltext gut oder böse ist: Sicherlich gibt es hier ein paar Regeln, die einem helfen, diesen möglichst gut zu gestalten; aber Regeln sind nun einmal da, um gebrochen zu werden ;-) |
Verbindliche Regeln sind die einzige Möglichkeit, um einen Haufen von besserwisserischen Egozentrikern (a.k.s. 'Softwareabteilung') auf Linie zu halten. Mit deinen Grundregeln ("Vermeiden von 1. Redundanz, 2. Arbeit 3. Aufrufe an den Speicher-Manager"), wärst Du bei mir gleich wieder vor der Tür(zumindest mit 2 und 3). Aber ich denke, Du bist erfahren genug, selbst deine eigenen Grundregeln ggf. zu brechen. Dazu sind sie ja deiner Meeinung nach gerade da ;-). Zudem scheint dein Spezialgebiet genau dort zu liegen, wo diese Regeln Gold wert sind.
Für mich z.B. sind die goldenen Regeln der Softwareentwicklung:
1. Robustheit
2. Einfachheit.
3. Wartbarkeit.
Für so ein kleines Problemchen, wie hier, das zudem von einem 'Anfänger' gestellt wurde, sollte der Code unter Ignorierung sämtlicher Performanceregeln in erster Linie leicht verständlich sein, also z.B. eine direkte Umsetzung einer ersten Problemlösung (Nach 4 Zeichen ein '-' einfügen. Dann -weil ein Zeichen dazugekommen ist- an 9.Stelle, usw). Ob da nun 3x Insert aufgerufen wird, oder nicht, ist irrelevant. Zudem sind die theoretischen Überlegungen bezüglich der Performance seit dem optimierten Speichermanager eh zu überdenken.
Leutchen, hier wollte jemand wissen, wie man einen String überhaupt in Vierergruppen unterteilt, und nicht, wie man das in optimaler Zeit unter Einbeziehung sämtlicher Hacks und Kniffe hinbekommt. Code dieser Art wären auch bein BenBE schnell da, wo sie hingehören (nehme ich an).
BenBE - Sa 27.06.09 18:40
@alzaimar: Dein letzter Satz ist unverständlich ;-)
elundril - Sa 27.06.09 19:03
1) Ich denke gerade hier im Forum, wo es ja nicht direkt um den Einsatz von Code geht, is es schon gut wenn man mehrere Möglichkeiten präsentiert bekommt und ich seh auch nix falsches daran, Codes performancemäßig optimal zu gestalten, denn aus diesen vielen Lösungen kann man sich die, für sich selbst, optimalste Lösung raussuchen bzw durch ein Mash up von zwei lösungen zu einer Optimalen Lösung kommen.
2) was wäre wenn die Insert-Methode mit Pointern usw arbeiten würde, wie BenBe und Jaenicke? Würdet ihr euch dann auch aufregen und ne extra neue Methode machen, weil die von Delphi zur verfügung gestellte "fehleranfällig" ist?
lg elundril
BenBE - Sa 27.06.09 19:39
@alzaimar: Unter Zuhilfenahme eines Thesaurus und der Berücksichtigung des Fuzzy Spellings in deinem Post nehm ich mal an, du meintest, ob bis ans Letzte ausgereizter Code bei mir in der Tonne landen würde:
Ja, wenn er gepflegt werden müsste (verstößt gegen Regel 2: Keine Arbeit machen)
Nein, wenn es darum geht, zu schauen, was möglich ist.
Fall 1 und 2 schließen sich bei mir zumindest aus, weshalb es den Fall "Jain" nicht gibt.
Und zum Thema, dass Code zum Verständnis eines Themas daherrieseln muss, geb ich mal ein Gegenbeispiel, was einige hier sicherlich bereits kennen sollten. Ansonsten Happy Reading ;-):
http://slexy.org/view/s2qBPPzw6Q
Dieser Code ist dabei entstanden, als ein Kumpel mich gefragt hat, wie man mit Pointern arbeitet und wie das ganze funktioniert. Also hab ich mich mit ihm hingesetzt und diesen Clone des Unix-Utilities env geschrieben. Das Teil ist weder effizient, noch übersichtlich, erfüllte abervollkommen seinen Zweck: Wenn man diesen Code verstanden hat, weiß man, wo überall Zeiger einzusetzen sind und weshalb diese zustande kommen. Ist übrigens ein schönes Beispiel, wie wenig viele heute über strikte Typ-Prüfung wissen, bzw. sich drum scheeren. Bei diesem Beispiel kommt es nämlich auf jeden Zeiger an UND dass er an der richtigen Stelle auftaucht. Wenn ich also jemandem erklären möchte, warum man etwas auf eine gewisse Art tun sollte, dann reicht es nicht, eine Trivial-Lösung zu zeigen, sondern man muss denjenigen soweit einbeziehen, dass er auch die Gründe dahinter versteht.
Und für dich zur Beruhigung: Diesen Source würd ich nie in dieser Form schreiben, wenn ich ihn warten müsste. Das Teil ist Unwartbar - auch wenn's beim IOCCC nur den Trostpreis "Nice Try" gewinnen würde. Darf ich jetzt bei Dir anfangen zu arbeiten?
bole - Sa 27.06.09 21:39
| Zitat: |
| Ja, wenn er gepflegt werden müsste (verstößt gegen Regel 2: Keine Arbeit machen) |
Wenn ich das richtig verstehe behauptest Du Dein Code müsse nicht gepflegt werden. Wahrscheinlich weil er perfekt ist... :shock:
Code pflegen muss man nicht nur wenn Fehler auftreten, es kann auch sein das man das Programm ändern muss sei es durch äussere Einflüsse oder durch eine funktionelle Erweiterung! Solche entwicklungen kannst auch Du nicht vorhersehen...
In hoffe das war ironisch gemeint :wink:
Kha - Sa 27.06.09 22:45
BenBE hat folgendes geschrieben : |
| For-Schleife mit Insert ist dagegen [...] einfach unelegant! |
Ich glaube, wir besitzen etwas verschiedene Auffassungen von Eleganz ;) ...
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| import Data.List
blocks :: Int -> [a] -> [[a]] blocks _ [] = [] blocks width xs = block : blocks width tail where (block, tail) = splitAt width xs insertSeperator :: a -> Int -> [a] -> [a] insertSeperator sep width = intercalate [sep] . blocks width |
Quelltext
1: 2: 3: 4:
| ghci> insertSeperator '-' 4 "C92917D2E4D98789EE51928EECA075E8K3NDI45JKS90OMLJD56JLSDJ7LSK6DK" "C929-17D2-E4D9-8789-EE51-928E-ECA0-75E8-K3ND-I45J-KS90-OMLJ-D56J-LSDJ-7LSK-6DK" ghci> take 10 $ insertAt 0 1 [1..] [1,0,2,0,3,0,4,0,5,0] |
Wenigstens scheine ich mit delfiphan, alzaimar und bole drei Seelenverwandte zu haben, da kann ich ja heute Nacht beruhigt schlafen :mrgreen: .
BenBE hat folgendes geschrieben : |
| ASM ja, Pointer sind dagegen ein warer Genuss. Je kürzer die Anweisung, desto klarer wird sie. |
Eben,
das ist doch ein Merkmal von "Eleganz", auf das man sich einigen könnte! Bei einer imperativen Sprache wie Delphi werde ich eine deklarative Programmierweise nicht als Merkmal von Eleganz durchsetzen können, aber dass in der Kürze nunmal die Würze liegt, ist quasi
allgemein anerkannt: [
http://wilshipley.com/blog/2007/05/pimp-my-code-part-14-be-inflexible.html]
| Zitat: |
You've probably seen some variant of this, but I'll show you my version. In coding, you have many dimensions in which you can rate code:
- Brevity of code
- Featurefulness
- Speed of execution
- Time spent coding
- Robustness
- Flexibility
Now, remember, these dimensions are all in opposition to one another. You can spend a three days writing a routine which is really beautiful AND fast, so you've gotten two of your dimensions up, but you've spent THREE DAYS, so the "time spent coding" dimension is WAY down.
So, when is this worth it? How do we make these decisions?
The answer turns out to be very sane, very simple, and also the one nobody, ever, listens to:
"START WITH BREVITY. Increase the other dimensions AS REQUIRED BY TESTING." |
Und die kürzeste Delphi-Version stammt ja auch von dir ;) ...
Bei
(X and 3) = 0 hoffe ich allerdings, um eine Zusammenarbeit mit dir herumzukommen :mrgreen: .
jaenicke hat folgendes geschrieben : |
| Und ist natürlich sehr viel langsamer, also nur geeignet, wenns nur einmal oder so ausgeführt wird. In einer Schleife wäre sowas kaum sinnvoll. :D |
Stimmt, ab zwei Durchläufen wird das schon extrem kritisch...
An der ursprünglichen Frage war schon abzulesen, dass es sich hier wohl nicht um zehntausende Strings handelt. Und wenn I/O noch dazukommt, würde ich keine Aussage über mögliche Bottlenecks wagen, bevor nicht ein Profiler drübergeschickt wurde.
Noch schlimmer: Wenn es beispielsweise ums Transformieren einer Textdatei ginge, ist die Pointer-Version genauso unbenutzbar wie die kürzere Insert-Version - wir wollen schließlich nicht den gesamten String in den Speicher laden müssen, sondern vielmehr Byte für Byte (oder Block für Block) vom Quell- in den Zielstream übersetzen. Ironischerweise schafft das meine Haskell-Version ohne Zutun :zwinker: . Ich glaube ehrlich gesagt einfach nicht, dass es
irgendeinen Use-Case dieser doch relativ speziellen Funktion gibt, der eine Pointer-Version rechtfertigt.
Ich habe natürlich nichts dagegen, wenn
auch hyperoptimierte Versionen vorgeschlagen werden, insbesondere wenn der Use-Case nicht klar ist. Aber als in der DP zum Thema "Anzahl Dezimalstellen?" eine Schleifen-Lösung vor der doch wohl "maximal-eleganten" Log-Lösung gepostet wurde, da die FPU eben doch nicht ganz so schnell sei, ist mir nur noch "/facepalm" in den Sinn gekommen :D .
Also einfach zusätzlich zum traditionellen Optimisierungswettbewerb noch einen Eleganzwettbewerb in jedem Thread einführen, dann bin ich zufrieden :mrgreen: .
Missionar - Sa 27.06.09 22:52
Kha hat folgendes geschrieben : |
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9:
| import Data.List
blocks :: Int -> [a] -> [[a]] blocks _ [] = [] blocks width xs = block : blocks width tail where (block, tail) = splitAt width xs insertSeperator :: a -> Int -> [a] -> [a] insertSeperator sep width = intercalate [sep] . blocks width | |
:eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy: :eyecrazy:
Kha - So 28.06.09 00:39
Missionar hat folgendes geschrieben : |
| :eyecrazy: and friends |
Wenigstens kannst du dich nicht mehr beschweren, dass es hier im Forum nur noch um .NET ginge ;) ...
BenBE - So 28.06.09 02:13
bole hat folgendes geschrieben : |
| Zitat: | | Ja, wenn er gepflegt werden müsste (verstößt gegen Regel 2: Keine Arbeit machen) |
Wenn ich das richtig verstehe behauptest Du Dein Code müsse nicht gepflegt werden. Wahrscheinlich weil er perfekt ist... :shock: |
Nein. Damit meine ich, dass ich Code, der in Real-Programmen auftaucht, die auch Weiterentwickelt werden sollen, nicht bis ins letzte Detail optimieren würde, sondern solche experimentellen Optimierungen auf Testprogramme beschränken, die ich für mich behalte, bzw. die als Demonstration dienen und nicht für den Produktiveinsatz gedacht sind.
bole hat folgendes geschrieben : |
Code pflegen muss man nicht nur wenn Fehler auftreten, es kann auch sein das man das Programm ändern muss sei es durch äussere Einflüsse oder durch eine funktionelle Erweiterung! Solche entwicklungen kannst auch Du nicht vorhersehen...
In hoffe das war ironisch gemeint :wink: |
Nein, spätestens deshalb nicht, da meine Kristallkugel unter chronischer Belastung durch DAU-Fragen leidet und daher ständig krank feiert ... Aber wie berits angedeutet beschränke ich Optimierungen im Programm auf ein sinnvolles, nebenbei optimierbares Maß: D.h. ich programmier von Haus aus unter Beachtung einiger Regeln zum Schreiben von optimalem Code eine Fassung, an der nicht mehr viel herauszuholen ist (meist) und verschwende keine Zeit darauf, diese noch weiter zu optimieren, außer ich seh: etwas auf anhieb ODER es ist dringend notwendig.
Insofern komme ich selten dahin, dass ich eine Hochoptimierte Routine vorliegen habe, die im Nachhinein optimiert werden muss, da solche Routinen nur in Ausnahmefällen in Produktiv-Software auftauchen.
Kha hat folgendes geschrieben : |
BenBE hat folgendes geschrieben : | | For-Schleife mit Insert ist dagegen [...] einfach unelegant! | Ich glaube, wir besitzen etwas verschiedene Auffassungen von Eleganz ;) ... |
Es ist ein Unterschied, ob das Insert intern die For-Schleife bearbeitet und daher die String-Allocation an einem Stück vorhersehen kann, oder ob ich hunderfach Insert aufrufe und er jedes Mal den String auf's Neue umkopiert.
Kha hat folgendes geschrieben : |
| Wenigstens scheine ich mit delfiphan, alzaimar und bole drei Seelenverwandte zu haben, da kann ich ja heute Nacht beruhigt schlafen :mrgreen: . |
Wieso? Ich habe nur etwas dagegen, wenn Code offensichtlich sinnlose Arbeit tut ... und das in Schleife ...
Kha hat folgendes geschrieben : |
BenBE hat folgendes geschrieben : | | ASM ja, Pointer sind dagegen ein warer Genuss. Je kürzer die Anweisung, desto klarer wird sie. | Eben, das ist doch ein Merkmal von "Eleganz", auf das man sich einigen könnte! Bei einer imperativen Sprache wie Delphi werde ich eine deklarative Programmierweise nicht als Merkmal von Eleganz durchsetzen können, |
Jap. Daher erübrigt sich dein Vergleich mit Haskell (Wer benutzt sowas esotherisches eigentlich ernsthaft :???: ) ...
Kha hat folgendes geschrieben : |
| Und die kürzeste Delphi-Version stammt ja auch von dir ;) ... |
halt alles eine Frage, wieviele offensichtliche Optimierungen man von Haus aus sieht. Wobei ein wenig geschummelt hab ich da auch, indem ich die Variablen etwas umbenannt hab ;-)
Kha hat folgendes geschrieben : |
| Bei (X and 3) = 0 hoffe ich allerdings, um eine Zusammenarbeit mit dir herumzukommen :mrgreen: . |
Man könnte theoretisch, unter ganz widrigen Umständen, in einer entfernten, grauen Vergangenheit auch mal
X mod 4 = 0 geschrieben haben ... Aber wer macht sowas Hirnrissiges :mrgreen:
Kha hat folgendes geschrieben : |
jaenicke hat folgendes geschrieben : | | Und ist natürlich sehr viel langsamer, also nur geeignet, wenns nur einmal oder so ausgeführt wird. In einer Schleife wäre sowas kaum sinnvoll. :D | Stimmt, ab zwei Durchläufen wird das schon extrem kritisch...
An der ursprünglichen Frage war schon abzulesen, dass es sich hier wohl nicht um zehntausende Strings handelt. Und wenn I/O noch dazukommt, würde ich keine Aussage über mögliche Bottlenecks wagen, bevor nicht ein Profiler drübergeschickt wurde. |
Wenn I/O dazu kommt, ist es aber oftmals von Vorteil, wenn die I/O-Funktion zum Bottleneck wird ;-) Der Einsatz eines Profilers ist aber bei jeglichen Aufgaben, wo variable oder undeterministische Verzögerungen auftauchen immer von Vorteil ...
Kha hat folgendes geschrieben : |
| Noch schlimmer: Wenn es beispielsweise ums Transformieren einer Textdatei ginge, ist die Pointer-Version genauso unbenutzbar wie die kürzere Insert-Version - wir wollen schließlich nicht den gesamten String in den Speicher laden müssen, sondern vielmehr Byte für Byte (oder Block für Block) vom Quell- in den Zielstream übersetzen. Ironischerweise schafft das meine Haskell-Version ohne Zutun :zwinker: . Ich glaube ehrlich gesagt einfach nicht, dass es irgendeinen Use-Case dieser doch relativ speziellen Funktion gibt, der eine Pointer-Version rechtfertigt. |
In der Regel implementiert man aber solche Geschichten, wenn sie für die Verarbeitung von Dateien gedacht sind eh als Stream bzw. als Stream-Filter, weshalb sowas nicht weiter tragisch wäre ...
Kha hat folgendes geschrieben : |
| Ich habe natürlich nichts dagegen, wenn auch hyperoptimierte Versionen vorgeschlagen werden, insbesondere wenn der Use-Case nicht klar ist. Aber als in der DP zum Thema "Anzahl Dezimalstellen?" eine Schleifen-Lösung vor der doch wohl "maximal-eleganten" Log-Lösung gepostet wurde, da die FPU eben doch nicht ganz so schnell sei, ist mir nur noch "/facepalm" in den Sinn gekommen :D . |
Wenn ich den ld einer Gleitkomma-Zahl brauch, dann kann es durchaus gerechtfertigt sein, sich den Exponenten direkt aus dem Speicher auszulesen. Sowas wäre aber nur als "Sonder-Lösung für spezielle Fälle" überhaupt im näheren Ideenkreis anzusiedeln. Fällt in Punkt 2 rein: noch hab ich IEEE754 nicht im Kopf; zudem Wartbarkeit ...Wenn verwendet, dann garantiert nicht inline, sondern in einer Funktion ausgelagert ...
Kha hat folgendes geschrieben : |
| Also einfach zusätzlich zum traditionellen Optimisierungswettbewerb noch einen Eleganzwettbewerb in jedem Thread einführen, dann bin ich zufrieden :mrgreen: . |
k, hätte da glaube ich mal eine Schöne Frage, für die man mal wieder einen wettbewerb starten könnte ... Grob: Jegliche Wörter, die in der deutschen Wikipedia vorkommen alphabethisch sortiert ausgeben ;-) Aber gut; können wir gern auch für jegliche Anfängerfragen machen :P
Kha hat folgendes geschrieben : |
Missionar hat folgendes geschrieben : | | :eyecrazy: and friends | Wenigstens kannst du dich nicht mehr beschweren, dass es hier im Forum nur noch um .NET ginge ;) ... |
Für Haskell gibt es inzwischen auch einen .NET-Compiler AFAIK ...
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!