![]() |
AW: Leerzeichen in String einfügen
Bahnbrechende Erkenntnis bei einem Codeschnippsel, das für einen Anfänger gedacht ist und dann in einem Buttonclick einmalig auf maximal 128 Zeichen losgelassen wird. Ich schlage vor, die beste Funktion mit der Parameterübergabeoption 'register' zu versehen, dann kann man nochmal einiges Rauskitzeln.
Eins ist sicher: Verwirrt ist der gute Mann (3 Beiträge, D7, Probleme in einer kleinen Schleife)bestimmt nicht. :wall: Leute, im Ernst: Ist hier nicht ausnahmsweise angebracht (ja, ich weiß, es gibt Programmierer, die lehnen das ab), die einfachste Variante vorzuschlagen? PS: Als Performancebooster war mein Vorschlag übrigens gar nicht gedacht, sondern als Versuch, die einfachste und verständlichste Version zu präsentieren. Falls das jemanden aufgefallen ist. |
AW: Leerzeichen in String einfügen
@Dejan Vu
Der TE ist in #5 bereits auf eine Lösung gekommen. Warum du dem TE in #7 wieder eine 2. Variante von #5 presentierst wird, erschließt sich mir nicht. Er hat gefragt, ihm ist das Insert() als eigene Lösung zuzugestehen. Völlig in Ordnung, er lernt es ja gerade. Mavarik hat ihm in #6 wohlwollend zeigen wollen, wie man das schneller machen... Warum? Nun ich würde davon ausgehen, das der TE eines schönen Tages Automatisierungen vornehmen müßte, wenn nicht mit dem Beispiel, dann an anderer Stelle und in anderer Variante. Nun neigt man dazu bereits vorhandenen Code wieder zu nutzen, da es ja schon wunderbar mit TForm1.leerzeichen(text:string) funktioniert hat. Ich bin nicht der Meinung, daß hier irgendwer den TE verwirren wollte, sondern auf sein konkretes Bsp. bezogen, mögliche Varianten der Herangehensweisen aufzeigen wollte. Mavarik's Version (gefixt) und die von Sir Rufo zeigen ihm nur, daß man eben die String-Copy Orgien, ab 'ne Iteration > 2 wirklich vermeiden sollte. Das lernt er aus den Benchmarks: Numbers are talking! Der TE kann sich diverse Erkenntnisse an allen Nachfolgenden Bsp. aneigenen. Wie: benutze Pointer-Inkrementierungen oder den Index des Strings, kalkuliere und reserviere den Speicher nur !einmal! und schieb rüber, was du braucht, schreibe kleine Funktionen und lagere diese aus ..... zum Bespiel. Durch Mavarik's "schneller" in #6 ist mir bei der Aufgabe sofort aufgefallen, das man seiner Version noch so einiges abringen könnte ->#7 Ein wiederholtes "da geht noch was" und in Fragestellung, ob mein größeres Code-Konstrukt etwas bessere und messbare Ergebnisse liefert -> Benchmarks. Irrglaube wer denkt, wenig Code bringt in jedem Falle viel Geschwindigkeit! Wenn der TE eines Tages sportliche/variable/hochgeschwindigkeits Resultate liefern sollte ... jeder hier wollte ihm auf seine Art und Weise zeigen, wie es geht. Uns hat man es ja auch mal gezeigt, oder nicht? Du hast ihm wiederholt, wie man es als Anfänger machen darf und man es bei mehr Erfahrung nicht machen sollte, warum auch immer. Er kann mit den zusätzlichen Code-Bsp. machen, was er möchte. Ist es zu kompliziert, sollte er weitere Erfahrungen sammeln und vielleicht später darauf zurückgreifen. Ich habe hierbei auch etwas gelernt:
Code:
Für hoch performante String functions war ich es gewohnt erst den String-Refcount und die Länge zu testen, bevor ich ein SetLength() calle. Die älteren IDE's haben in jedem Falle einen neuen Unique-String erzeugt. Nun scheint mir das dieser Test schon implizit dem SetLength drin ist. Oder baut das der Compiler neuerdings direkt ein? Das war nicht immer so, und gilt herauszufinden, seit welcher Version sich Delphi so verhält. Klar ich spare den Call aber der Code wird unleserlich und für jemanden, der davon keine Ahnung hat, völlig banane.
if (Pointer(Result) = nil) or //result unassigned?
(PLongInt(NativeUInt(Result) - SizeOf(LongInt))^ <> SrcLen) {PStrRec.Len} or //length different? (PLongInt(NativeUInt(Result) - (SizeOf(LongInt) shl 1))^ <> 1) {PStrRec.RefCnt} then //no unique string? SetLength(Result,SrcLen+((SrcLen-1) shr 2)); @Mavarik Ist der Test so in Ordnung für dich? Ergebnisse akzeptiert? |
AW: Leerzeichen in String einfügen
Die Lösung von #5 ist ja nicht korrekt, da sie die Länge des Strings ignoriert, daher die #7. Daraufhin hat Mavarik sein Pointerzeugs (nicht falsch verstehen, es ist 'good stuff') gepostet und dann hat sich der Thread in Richtung Performanceoptimierung degeneriert. So wird ein Schuh draus, gelle?
Ich fand es einfach amüsant, am Thema vorbei zu optimieren. Normalerweise macht man das dann in einem eigenen Thread, z.B. mit dem Titel 'Kata: Performanceoptimierung beim "Einfügen in Strings"'. Oder so ähnlich. Dann machen auch mehr Leute mit und es kann gefachsimpelt werden. PS: Nicht falsch verstehen: Ich liebe Performanceoptimierungen. |
AW: Leerzeichen in String einfügen
Zitat:
Das macht Delphi/Pascal standardmäßig von Haus aus. |
AW: Leerzeichen in String einfügen
Zitat:
Wir haben ja oft hier Neulinge die mit Delphi anfangen. Nach der einfachen Regel: ![]() Ich gebe Dir recht, dass für den TE die Insert Variante ausreichend ist. Aber auch er sollte im Hinterkopf behalten, dass es zwar funktioniert, aber nicht gerade performant ist. Daher finde ich meine Variante als gutes Beispiel. (Die RTL macht es auch so) Für so einen Fall "C-Like" mit PChars zu arbeiten. Dabei bleibt die Routine immer noch übersichtlich. Und ist immerhin 60x schneller... Wir regen hier nicht von 2x oder 3x! Sir Rufo's Variaten hat aber auch Ihren Platz - wenn auch nicht so schnell - kann man diese Routine, so übernehmen und als 1. Routine in seine eigene Procedurensammlung packen - weil allgemein gültig... Dabei immerhin auch 6,7x schneller als die Insert Variante. Not Bad für eine allgemeingültige Routine... Hugo hat dann gezeigt... Richtig Kopieren bringt nochmal eine mehr als doppelt so schnelle Version hervor. Auch wenn die Routine dann nicht mehr ganz so übersichtlich ist. Eigentlich wollte ich noch eine Thread-Variante bauen, die den String erst auf 4 Worker-Thread aufteilt, aber ich hatte leider gestern keine Zeit mehr... Vielleicht macht es ja noch einer... Im Großen und Ganzen... Finde ich den Thread sehr gelungen, auch wenn das Thema jetzt damit abgeschlossen ist. Wenn jeder Programmierer - selbst bei so wenigen Zeilen Source - die Performance Überlegungen nicht außer acht lassen würde, brächten wir heute nicht 3,5 GHz, um Dinge zu erledigen, die früher mit 25 MHz möglich waren... (bitte nicht darauf eingehen) 8-) Mavarik |
AW: Leerzeichen in String einfügen
@Mavarik
so da gibt jetzt der "Hugo" mal noch abschließend seinen Senf hinzu. Dem Link stimme ich vollends zu. Bitte schau es dir mal genauer an, was ich mache. Verstanden hast du es auch noch nicht. Der Trick ist nicht das kopieren. Das könnte man in den Quad folgen auch so lösen:
Code:
Und es würde nachwievor jede hier dargestellte Version schlagen.
.....
Dest^ := Src^; (Dest+1)^ := (Src+1)^; (Dest+2)^ := (Src+3)^; (Dest+3)^ := (Src+3)^; ..... Der Trick ist das Decrementieren des PEnd-Pointers um die Länge des concat-Strings. Somit kann ich in jedem Falle den gesamten concat innerhalb Lenght(Result) div Length(ConcatStr) Loop durchlaufen und ich brauch auch nicht mehr auf die Position zu achten, damit die N-te ein String-X wird. Somit läuft mein code mit 4x weniger loops als der deine. Die Loops waren das Performance Problem bei dir und bei Sir Rufo gleich doppelt + der Bremse, daß die IDE innerhalb jeder Loop die Offsets neu incrementieren muß, da er Str[x] offsets nutzt. Sir Rufos Bsp könnte man auch so darstellen (nicht hauen wenns jettzt nicht geht -> ungetestet):
Code:
und nun noch mal für den Laien verständlicher mit Anleitung und ohne Low-Level code:
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer; PAStr, Dest, PEnd: PChar; begin if AStr = '' then Result := '' else begin AStrLen := PLongInt(NativeUInt(AStr) - SizeOf(LongInt))^; //fast get AStr length if (AInsertStr = '') or (AStrLen <= APos) then Result := AStr else begin AInsertStrLen := PLongInt(NativeUInt(AInsertStr) - SizeOf(LongInt))^; //fast get AInsertStr length ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen); ConcatLen := APos+AInsertStrLen; SetLength(Result, ResLen); PAStr := Pointer(AStr); Dest := Pointer(Result); PEnd := (PAStr+AStrLen)-APos; <<<<<<<<<<<<<<----- Da hat der Frosch die Locken (: while (PAStr < PEnd) do begin System.Move(PAStr^, Dest^, APos{$IFDEF UNICODE} shl 1{$ENDIF}); System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen{$IFDEF UNICODE}shl 1{$ENDIF}); Inc(Dest, ConcatLen); Inc(PAStr,APos); end; System.Move(PAStr^, Dest^, ((PEnd+APos) - PAStr){$IFDEF UNICODE} shl 1{$ENDIF}); end; end; end;
Delphi-Quellcode:
Ich habs jetzt mal Quick & Dirty mit moves verpackt, da er ja Variable Längen und unterschiedliche Positionen zuläßt.
function EH_StrInsertEveryNthPos(const AStr, AInsertStr: string; APos: Integer): string;
var AStrLen, AInsertStrLen, ResLen, ConcatLen: Integer; PAStr, Dest, PEnd: PChar; begin if AStr = '' then Result := '' else begin { speichere die Länge das Strings, in welchen die Inserts gemacht werden sollen } AStrLen := Length(AStr); if (AInsertStr = '') or (AStrLen <= APos) then Result := AStr else begin { Vorbereitung } AInsertStrLen := Length(AInsertStr); //speichere die Länge des AInsertStrings ConcatLen := APos+AInsertStrLen; //speichere die Länge der Fragmente welche im Resultat zusammengesetzt werden ResLen := AStrLen+((( AStrLen div APos ) - Ord(AStrLen mod APos = 0))*AInsertStrLen); //kalkuliere die Länge des Resultats SetLength(Result, ResLen); //reserviere den benötigten Speicher für das Resultat PAStr := Pointer(AStr); //Speichere den Pointer von AStr Dest := Pointer(Result); //Speichere den Pointer von Result { Speichere den Pointer des letzten Chars von AStr abzüglich die Länge von APos } { um die Wiederholungen der nachfolgenden Schleife um Faktor APos zu reduzieren } PEnd := (PAStr+AStrLen)-APos; { fülle das Resultat mit den Daten } while (PAStr < PEnd) do begin { da wir PEnd um die Länge von APos reduziert haben, ist es garantiert, das wir NIE die Länge von AStr überschreiten } { kopiere eine Menge von APos Chars von der derzeitig gemerkten Stelle des AStr in das Resultat } System.Move(PAStr^, Dest^, APos*SizeOf(Char)); { kopiere AInsertString in das Resultat nach den zuletzt kopierten Daten } System.Move(Pointer(AInsertStr)^, (Dest+APos)^, AInsertStrLen *SizeOf(Char)); { passe die OffSets an } Inc(Dest, ConcatLen); //Erhöhe den Pointer des Resultats um die Menge der kopierten Chars Inc(PAStr, APos); //Erhöhe den Pointer des AStr um APos Stellen end; Inc(PEnd+APos); //nun inkrementiere PEnd wieder um APos Stellen, welche wir oben abgezogen haben { kopiere den Rest oder garnix (welches sich aus der Differenz von PEnd - PAStr ergibt) von AStr ins Resultat } System.Move(PAStr^, Dest^, (PEnd - PAStr)* SizeOf(Char)); end; end; end; Die moves arbeiten intern auch mit Loops, somit ist es möglich, das diese Variante nicht unbedingt viel schneller ist. Jedoch nehm ich der großen Loop(char by char) gleich die Luft raus, indem ich den offset auf die fertige String-Länge zurücksetzte. Und auch nicht mehr schauen muß, ob der Delimiter an der richtigen Position ist oder nicht, es ist garantiert! Nun ist es aber wieder so, daß das Original leicht verstädlich ist... Und meines wieder nicht. Dennoch behaupte ich, diese Version ist der Version von Sir Rufo weit überlegen .... e.g. Pointer-Offsets! |
AW: Leerzeichen in String einfügen
Liste der Anhänge anzeigen (Anzahl: 1)
@Sir Rufo,
der unangemeldeten Challange halber habe ich den vorherig geposteten Code mal gefixt. Habe mir die 8min genommen es zu testen. Resultate: Zitat:
JFYI, includes the test, cheers. Edit: Nur mal so, hier ergibt sich ein irrsinninger Spread: desto größer APos oder AInsertStr desto weiter läuft meine Interpertation der deinen davon. Habt Spaß beim Selber tüfteln. |
AW: Leerzeichen in String einfügen
Zitat:
Na ja. Ist eh wurscht. |
AW: Leerzeichen in String einfügen
Liste der Anhänge anzeigen (Anzahl: 1)
Guten Morgen,
wollte Dejan Vu's produktiven Worten eigentlich den Vortritt beim Abschluß dieses Threads lassen. Doch: Zitat:
Ich vergleiche hier die Result-Länge mit der Source-Länge. :roll: Na sooo wird dad doch auch nix! Also ... habe ich die Unit nochmals angehängt. Es kitzelt doch noch einige Ticks raus: Zitat:
PS. @Mavarik das div 4 braucht ca. 10 Zyklen, wärend ein shr 2 einen braucht.. Ist der Hauptunterschied zwischen "Leerzeichen" und "EH_QuadUnderScoreInjectorBy4".. |
AW: Leerzeichen in String einfügen
Ich habe mir jetzt nicht alle Lösungen angeguckt. Vielleicht ist meine Idee schon dabei: Ausgangszeichenkette in Vierer-Blöcke aufsplitten und beim Zusammensetzen die Leerzeichen einfügen.
Vielleicht auch nicht am schnellsten, aber sehr anschaulich, was passiert, denke ich. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:46 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz