![]() |
Slicing for Delphi™
Liste der Anhänge anzeigen (Anzahl: 2)
Moin moin!
Der eine oder andere von euch kennt sicher aus anderen Programmiersprachen das Slicing-Konzept. Für die, die es nicht kennen, hier eine kurze Einführung: Dass man auf ein Listenelement mittels Liste[Index] zugreifen kann, ist sicher nichts neues. Das dumme daran: Man bekommt immer nur ein Element zurück. Und hier kommt das Slicing ins Spiel, welches derartige Zugriffe quasi exponentiell nützlicher macht. Die Grundlegende Syntax ist Liste[StartIndex:StopIndex]. Und eigentlich ist das schon ziemlich selbsterklärend, hier ein paar Beipsiele, was man damit so machen kann:
Code:
Nun, wems bis jetzt noch nicht gedämmert ist: Soetwas gibt es in Delphi leider nicht :(
Liste[0:4] 0tes - 5tes Element
Liste[3:4] 4tes - 5tes Element Liste[:5] 0tes - 6tes Element Liste[3:] 4tes - letztes Element Liste[1:-1] zweites - vorletztes Element u.v.m. Ich bin grade dabei mir zu überlgen, inwieweit man das in Delphi integrieren könnte. Herausgekommen ist dabei bis jetzt eine Beispielimplementation TSlicedStringList:
Delphi-Quellcode:
Das neue an ihr: sowas geht
unit SlicedStringList;
interface uses Classes; type TSlicedStringList = class(TStringList) private function GetStrings(Slice: string): TStrings; //procedure SetStrings(Slice: string; const Value: TStrings); public property Strings[Index: Integer]: string read Get write Put; default; property Strings[Slice: string]: TStrings read GetStrings; default; end; implementation uses SysUtils, Dialogs; { TExtStringList } type TIntArray = array of Integer; function GetIntArrayFromSlice(const Slice: string; const Max: Integer): TIntArray; var strStartPos, strStopPos: string; StartPos, StopPos, i: Integer; begin strStartPos := Copy(Slice, 1, Pos(':', Slice) - 1); strStopPos := Copy(Slice, Pos(':', Slice) + 1, Length(Slice)); case Length(strStartPos) of 0: StartPos := 0; else StartPos := StrToInt(strStartPos); end; case Length(strStopPos) of 0: StopPos := Max; else StopPos := StrToInt(strStopPos); end; if StartPos < 0 then StartPos := Max + StartPos; if StopPos < 0 then StopPos := Max + StopPos; SetLength(Result, StopPos - StartPos + 1); for i := StartPos to StopPos do begin Result[i - StartPos] := i; end; end; function TSlicedStringList.GetStrings(Slice: string): TStrings; var i: Integer; begin Result := TStringList.Create; case Pos(':', Slice) of 0: Result.Add(Strings[StrToInt(Slice)]); else begin for i in GetIntArrayFromSlice(Slice, Count - 1) do Result.Add(Strings[i]); end; end; end; //procedure TSlicedStringList.SetStrings(Slice: string; const Value: TStrings); //begin // //end; end.
Delphi-Quellcode:
Im Anhang findet ihr eine kleine Demo-Anwendung dazu, nix großes, soll nur das Prinzip verdeutlichen und enthält KEINE Fehlerabfragen, derartige 'Bugreports' sind also überflüssig ;)
lst := StringList['1:4'];
Die spannende Frage ist nun: Was haltet ihr davon? Nützlich? Fürn Popo? Unbedingt weiterentwicklen? Und wenn weiterentwickeln, was wären die Konventionen, die ihr bevorzugegen würdet? Soll [-1] Das letzte oder das vorletzte Element liefern? (Beides ergibt Sinn). Soll der EndIndex im Ergebnis enthalten sein oder nicht (beide Versionen gibt es in anderen Sprachen). Fällt euch eine bessere Möglichkeit ein, als das ganze per string zu übergeben (was nicht sehr nützlich ist, aber es ist der einizge Weg, der mir spontan eingefallen ist)? Nuja, her mit euren Ideen und Anregungen :) |
Re: Slicing for Delphi™
Zitat:
Delphi-Quellcode:
TSlicedStringList = class
private function GetSlice(From, To: integer): TStrings; ... public property Slices[From,To: integer]: TStrings read GetSlice; ... end; ...
Delphi-Quellcode:
lst := SlicedStringList.Slices[1,4];
lst.free; |
Re: Slicing for Delphi™
Zitat:
[-1] Wäre das letzte Element, [0, -1] würde was anderes bedeuten, man müsste dafür also [-1, -1] übergeben. Auch irgendwie blöd :| |
Re: Slicing for Delphi™
Zitat:
EDIT: ![]() Siehe mein Beitrag MfG |
Re: Slicing for Delphi™
Interessant!
Ich würde das mit dem ![]() Dazu leitet man von TStrings ab und muss dann natürlich alle virtuellen (und abstrakten) Methoden überschreiben. Dabei leitet man die Methoden nur auf das TStrings-Objekt weiter, dass im Konstruktor übergeben wurde.
Delphi-Quellcode:
TSlicedStrings = class(TStrings)
private FStrings : TStrings; function GetSlice(const Range: string): TStrings; //procedure SetStrings(Slice: string; const Value: TStrings); public constructor Create(AStrings:TStrings); function Add(const S: string): Integer; override; property Slice[const Range: string]: TStrings read GetStrings; property Slice[fromIdx,toIdx:Integer]:TStrings; // 2. Variante end; constructor TSlicedStrings.Create(AStrings:TStrings); begin inherited Create; FStrings := AStrings; end; function TSlicedStrings.Add(const S: string): Integer; override; begin Result := FStrings.Add(S); // einfach auf internes Objekt umleiten end; function TSlicedStrings.GetSlice(const Range: string): TStrings; var i,j : Integer; begin Result := TStringList.Create; case Pos(':', Range) of 0: begin j := StrToInt(Range); Result.AddObject(Strings[j], Objects[j]); end else begin for i in GetIntArrayFromSlice(Range, Count - 1) do Result.AddObject(Strings[i], Objects[i]); end; end; end; |
Re: Slicing for Delphi™
Zitat:
|
Re: Slicing for Delphi™
Lustig. Der Nachteil des String-Parametersi ist die etwas sehr umständliche Verwurstung von Variablen. I.A. benötige ich nicht das 2. bis 5. Element, sondern das i.te bis j.te (bzw. N Elemente ab Position I). Ich müsste mir also den Deskriptorstring mittels IntToStr oder Format zusammenbasteln. Das ist irgendwie blöd.
Delphi-Quellcode:
Übersichlich geht anders.
MySlicedStringList := StringList[Format('%d:%d',[I,N])];
|
Re: Slicing for Delphi™
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: Slicing for Delphi™
xD
Um etwas klarzustellen -> meinst du mit [0:-1] ~ [0:Anzahl der Rechten Elemente - 1] ? Falls nein, dann war mal mein Beitrag wieder voll daneben :roll: MfG |
Re: Slicing for Delphi™
bei Arrays kann man auch Copy nutzen ... ok, ohne das "von rechts"
Delphi-Quellcode:
ansonsten gibt's 2 Möglichkeiten für die Parameter:
var a: Array of Double;
a := Copy(a, 5, 9);
Delphi-Quellcode:
public
property Slices[From: Integer; To: Integer = MinInt]: TStrings read GetSlice; // if To = MinInt then {to=from} // else {from,to}
Delphi-Quellcode:
public
property Slices[From, To: Integer]: TStrings read GetSlice2; Default; property Slices[To: Integer]: TStrings read GetSlice1; Default; ... |
Re: Slicing for Delphi™
MinInt? Das kennt mein Delphi gar nicht. Was ist denn das? 0? -1? $80000000?
|
Re: Slicing for Delphi™
Delphi-Quellcode:
im Grunde muß da einfach nur irgendein Wert hin, welcher (vermutlich) nie als Parameter übergeben wird.
const MinInt = Low(Integer)
|
Re: Slicing for Delphi™
Ich finde das Slicing ja ganz nett, aber imho wird versucht, Delphi eine anderes Paradigma aufzudrängen. Die beschriebene Funktionalität lässt sich doch sehr schön mit unterschiedlichen Methoden (und aussagekräftigen Namen) bewerkstelligen. Die Syntax ist kryptisch und widerspricht daher dem Konzept der Lesbarkeit.
Delphi-Quellcode:
Das wäre dann eine sinnvolle Erweiterung für Listen. Mir fällt nur grad (bis auf Copy) kein sinnvoller Einsatz ein. Hast Du Beispiele, für die Verwendung von 'nur jedes 3.Element'?
Const
FromStart = -MaxInt; ToEnd = MaxInt; Type TSliceableList = Class Function Copy(From,To : Integer) : TSliceableList; Function Probe(StartIndex, Step : Integer) : TSliceableList; ... End; |
Re: Slicing for Delphi™
Zitat:
Zitat:
Zitat:
|
Re: Slicing for Delphi™
Moin Zusammen,
ich würde jedenfalls in keinem Falle TStrings als Rückgabewert verwenden, sondern als Parameter übergeben, da man dann immer vor dem Problem steht, wann diese Objekt wieder zerstört werden muss, und von wem. |
Re: Slicing for Delphi™
Ich wäre für ein Interface als Rückgabewert.
|
Re: Slicing for Delphi™
Am besten noch ein Enumerator-Interface ;)
|
Re: Slicing for Delphi™
Zitat:
Zitat:
|
Re: Slicing for Delphi™
Praktisch ein TStrings, aber als Interface. Durch die Referenzzählung umgehst du die manuelle Freigabe. Ein Enumerator-Interface ist eines, dass eine Methode GetEnumerator hat, welche einen Enumerator zurückgibt (mehr dazu in der Delphi-Hilfe). Dadurch wird die Verwendung der For-In-Schleife ermöglicht.
|
Re: Slicing for Delphi™
Soderle,
ich habe das ganze nun noch etwas weiterentwickelt, und jetzt funktioniert das ganze schon so:
Delphi-Quellcode:
was ich schonmal ziemlich brauchbar finde :firejump: (funktioniert allerdings jetzt nur noch ab D2005).
uses Slicing;
... var lst1, lst2: TStringList; ... lst1 := lst2[-3]; lst1 := lst2[1,5]; lst1 := lst2['1:5'] // Aus Nostalgiegründen drinne gelassen =) Allerdings hat es immernoch unschönerweise TStringList als Rückgabetyp. Ich versteh einfach nicht, wie das mit interface praktischerweise gehen soll. Wenn ich irgendein interface selbst definiere, dann muss ja der Anwender immer erst eine passende Klasse dazu implementieren, das wäre ja auch bescheuert. Oder könnte ich via class helpern der TStringList bspweise ein interface "beibringen", sodass das dann verwendet werden könnte? Nunja, ich versteh einfach nicht, wie ihr das meint, bzw. wo da der Vorteil sein soll... |
Re: Slicing for Delphi™
einfach eine StringList mit 'nem Interface versehen
und dann nur noch dem Interface die nötigen Funktionen mitgeben... hier fehlen jetzt nur noch einige Funktionen im Interface, also welche man noch unbedingt aus den Public-Abschnitten benötigt.
Delphi-Quellcode:
Type
IInterfacedStringList = Interface ['{E7FD1CCD-B023-491F-BEBA-3CEC190370D6}'] //Private Function Get(Index: Integer): String; Function GetCount: Integer; //Public Function Add (Const S: String): Integer; Function IndexOf(Const S: String): Integer; Property Count: Integer Read GetCount; Property Strings[Index: Integer]: String Read Get; Default; Function Find (Const S: String; Var Index: Integer): Boolean; //... End; TInterfacedStringList = Class (TStringList, IInterfacedStringList) Protected FRefCount: Integer; Function QueryInterface(Const IID: TGUID; Out Obj): HResult; StdCall; Function _AddRef: Integer; StdCall; Function _Release: Integer; StdCall; Public Procedure AfterConstruction; Override; Procedure BeforeDestruction; Override; Class Function NewInstance: TObject; Override; Property RefCount: Integer Read FRefCount; End; Function TInterfacedStringList.QueryInterface(Const IID: TGUID; Out Obj): HResult; Begin If GetInterface(IID, Obj) Then Result := 0 Else Result := E_NOINTERFACE; End; Function TInterfacedStringList._AddRef: Integer; Begin Result := InterlockedIncrement(FRefCount); End; Function TInterfacedStringList._Release: Integer; Begin Result := InterlockedDecrement(FRefCount); If Result = 0 Then Destroy; End; Procedure TInterfacedStringList.AfterConstruction; Begin Inherited; InterlockedDecrement(FRefCount); End; Procedure TInterfacedStringList.BeforeDestruction; Begin If RefCount <> 0 Then System.Error(reInvalidPtr); Inherited; End; Class Function TInterfacedStringList.NewInstance: TObject; Begin Result := Inherited NewInstance; TInterfacedStringList(Result).FRefCount := 1; End; |
Re: Slicing for Delphi™
OK, aber das bedeutet doch dann, dass der Benutzer wenn er Slicing verwenden will immer TInterFacedStringList verwenden muss (wenn er eigentlich mit TStringList arbeiten will) - das würde ich eigentlich gerne vermeiden :(
|
Re: Slicing for Delphi™
Du brauchst es doch nur da einzusetzen, wo du 'ne StringList als Result zurückgibst?
also
Delphi-Quellcode:
Das Ganze würde ich auch nicht für die Basisklasse umsetzen.
Function GetStrings(Index: Integer): IInterfacedStringList;
Witzig wäre es, wenn du deine Slicing-Erweiterung als Class-Helper für TStrings erstellst, dann hätte man diese Funktion gleich automatisch in allen TStrings-Nachfahren (wie TStringList) drin, und das sogar auch noch bei allen Instanzen, die man nichtmal selber erstellt hat (wie z.B. .Lines von TMemo). :angel: |
Re: Slicing for Delphi™
Zitat:
|
Re: Slicing for Delphi™
Liste der Anhänge anzeigen (Anzahl: 1)
:angel:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:02 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 by Thomas Breitkreuz