Jeg ville straks kaste mig over en liste som TObjectList eller TStack. Det betyder dog at du skal have et objekt i stedet for en byte (hvis det skal være pænt). Den kommer her:
I ContNrs-unitten ligger der også en TQueue (og en TObjectQueue).
Det mtj111 efterlyser er dog, i mine øjne, en TStack. Han nævner selv pop og push.
... men igen. Det kommer an på om han har lyst til at prøve kræfter med klasserne eller vil holde det i en effektiv array. Han kunne pakke den simple array ind i en klasse og opnå det samme:
const MaxStack = 100;
TMyStack = class private fStack: array[1..MaxStack] of byte; findex: integer; public property Count: integer read fIndex; procedure Push(aValue: byte); function Pop: byte; end;
procedure TMyStack.Push(aValue: byte); begin if fIndex = MaxStack then raise Exception.Create('Stakken er fuld'); inc(fIndex); fStack[fIndex] := aValue; end;
function TMyStack.Pop: byte; begin if fIndex = 0 then raise Exception.Create('Stakken er tom'); result := fStack[fIndex]; dec(fIndex); end;
Det nemmeste (og det jeg har prøvet) er indtil videre move-funktionen, men den vil ikke helt som jeg vil.
Det jeg eftersøger er en pænere måde at lave følgende på: for i := Low(MinArray) to High(MinArray)-1 do MinArray[i] := MinArray[i+1];
og eftersom hvad jeg kan læse og forstå om move-funktionen, burde den gøre det samme. Problemet er bare, at Move(MinArray[1],MinArray[0],Length(MinArray)-1); kun flytter de ca. 25 første elementer i MinArray bagud - og det undrer mig en smule.
Lille update: Hvis jeg skriver 500 i stedet for Length(MinArray)-1, så virker det (bortset fra en fejl i programmet når det afsluttes, hvilket jeg går ud fra skyldes at 500 er for stort)
Hvilket tal skal jeg bruge, når MinArray er defineret som Array[0..100] of Integer;?
Og nu fandt jeg ud af det :) Length() fungerer åbenbart ikke som I de ældre versioner (jeg bruger Delphi 2005), så man skal bruge SizeOf(MinArray) i stedet
Vil der være nogen mærkbar hastighedsforskel mellem en array og move-funktionen i forhold til jeres andre løsningsforslag? Jeg har kun brug for ca. 100 værdier (i Array'et), så det er vel begrænset hvor meget andre løsningsforslag er hurtigere?
arne: Det er som man tolker det. Jeg mener det kan være begge, men når mtj111 nævner "pop" og "push" tænker jeg altså på en stak.
mtj111: Hvis du kigger på det eksempel jeg lavede, så behøver der slet ikke flyttes noget. Hvis ellers eksemplet er korrekt synes jeg det er en elegant og simpel løsning - hvis altså det er en stak du vil have.
En ordentlig stak kode som illusterer mulighederne:
program queue;
{$APPTYPE CONSOLE}
uses ShareMem, Contnrs, SysUtils, Windows;
const SIZE = 100;
type Data = array[0..SIZE] of Byte;
type ITest = interface(IInterface) procedure Add(v : Byte); function Get() : Data; function GetName() : String; property Name : String read GetName; end; Mover = class(TInterfacedObject, ITest) private buf : Data; ix : Integer; public constructor Create; procedure Add(v : Byte); function Get() : Data; function GetName() : String; end; Wrapper = class(TObject) private val : Byte; public constructor Create(v : Byte); property Value : Byte read val; end; Container1 = class(TInterfacedObject, ITest) private q : TObjectQueue; public constructor Create; procedure Add(v : Byte); function Get() : Data; destructor Destroy; override; function GetName() : String; end; Container2 = class(TInterfacedObject, ITest) private q : TQueue; public constructor Create; procedure Add(v : Byte); function Get() : Data; destructor Destroy; override; function GetName() : String; end; Circular = class(TInterfacedObject, ITest) private buf : Data; ix : Integer; public constructor Create; procedure Add(v : Byte); function Get() : Data; function GetName() : String; end;
constructor Mover.Create();
begin ix := 0; end;
procedure Mover.Add(v: Byte);
begin if ix < SIZE then begin buf[ix] := v; Inc(ix); end else begin Move(buf[1], buf[0], SIZE); buf[SIZE] := v; end; end;
function Mover.Get() : Data;
begin Get := buf; end;
function Mover.GetName() : String;
begin GetName := 'Moving for each Add'; end;
constructor Wrapper.Create(v : Byte);
begin val := v; end;
constructor Container1.Create();
begin q := TObjectQueue.Create; end;
procedure Container1.Add(v: Byte);
var tmp : Wrapper;
begin if q.Count > SIZE then begin tmp := Wrapper(q.Pop); tmp.Destroy; end; q.Push(Wrapper.Create(v)); end;
function Container1.Get() : Data;
var res : Data; i : Integer; tmp : Wrapper;
begin for i := 0 to SIZE do begin tmp := Wrapper(q.Pop); res[i] := tmp.Value; tmp.Destroy; end; Get := res; end;
destructor Container1.Destroy;
begin q.Destroy; inherited; end;
function Container1.GetName() : String;
begin GetName := 'Using builtin TObjectQueue'; end;
constructor Container2.Create();
begin q := TQueue.Create; end;
procedure Container2.Add(v: Byte);
begin if q.Count > SIZE then begin q.Pop; end; q.Push(Pointer(v)); end;
function Container2.Get() : Data;
var res : Data; i : Integer;
begin for i := 0 to SIZE do begin res[i] := Byte(q.Pop); end; Get := res; end;
destructor Container2.Destroy;
begin q.Destroy; inherited; end;
function Container2.GetName() : String;
begin GetName := 'Using builtin TQueue'; end;
constructor Circular.Create();
begin ix := 0; end;
procedure Circular.Add(v: Byte);
begin buf[ix] := v; ix := (ix + 1) mod (SIZE + 1); end;
function Circular.Get() : Data;
var res : Data; i : Integer;
begin for i := 0 to SIZE + 1 do begin res[i] := buf[(ix + i) mod (SIZE + 1)]; end; Get := res; end;
function Circular.GetName() : String;
begin GetName := 'Just circling around'; end;
procedure Test(impl : ITest);
var i : Integer; tmp : Data; t1, t2 : Integer;
begin t1 := GetTickCount; for i := 0 to 1234567 do begin impl.Add(i); end; t2 := GetTickCount; tmp := impl.Get; writeln(impl.Name); write('result: '); for i := 0 to SIZE do begin write(' ', tmp[i]); end; writeln; writeln('time: ', t2 - t1); end;
arne: Mon ikke gråspurvene er ved at være rædde? Kanonkuglerne flyver i alt fald gennem luften nu. Du giver i alt fald ikke op. MTJ111 skal have en cirkulær liste.
Det kan da godt være at gråspurvene er ved at være lidt nevøse.
Men det var en god lejlighed for mig til at prøve lidt OOP i Delphi.
Og så fandt jeg iøvrigt ud af at TQueue og TObjectQueue tilsyneladende er array backed og ikke double linked list backed. Hvilket overrasker mig en del.
Du tænker på TPointerList? Du er ikke den eneste der er overrasket (er vist kommet til at kalde den en dobbeltkædet liste et eller andet sted); men når man tænker over løsningen er den ret effektiv - men afgjort med et strøg af noget ur-paskalsk'.
TList er en array[0..maxInt div $f] of pointer (hvorfor har de ikke brugt shr 1 i stedet for div?). Hvordan kan den være langsom? Uanset om du har en dobbeltkædet liste eller en array, skal tingene oprettes. En TList opretter efter behov i klumper (a la 16 linjer) og der er ikke fiflen med pointere der peger den ene eller anden vej.
type ITest = interface(IInterface) procedure Add(v : Byte); function Get() : Data; function GetName() : String; property Name : String read GetName; end; Mover = class(TInterfacedObject, ITest) private buf : Data; ix : Integer; public constructor Create; procedure Add(v : Byte); function Get() : Data; function GetName() : String; end; Wrapper = class(TObject) private val : Byte; public constructor Create(v : Byte); property Value : Byte read val; end; Container1 = class(TInterfacedObject, ITest) private q : TObjectQueue; public constructor Create; procedure Add(v : Byte); function Get() : Data; destructor Destroy; override; function GetName() : String; end; Container2 = class(TInterfacedObject, ITest) private q : TQueue; public constructor Create; procedure Add(v : Byte); function Get() : Data; destructor Destroy; override; function GetName() : String; end; Circular = class(TInterfacedObject, ITest) private buf : Data; ix : Integer; public constructor Create; procedure Add(v : Byte); function Get() : Data; function GetName() : String; end; PElement = ^Element; Element = record val : Byte; next : PElement; prev : PElement; end; DoubleLinkedList = class(TInterfacedObject, ITest) private n : Integer; head : PElement; tail : PElement; public constructor Create; procedure Add(v : Byte); function Get() : Data; function GetName() : String; end;
constructor Mover.Create();
begin ix := 0; end;
procedure Mover.Add(v: Byte);
begin if ix < SIZE then begin buf[ix] := v; Inc(ix); end else begin Move(buf[1], buf[0], SIZE); buf[SIZE] := v; end; end;
function Mover.Get() : Data;
begin Get := buf; end;
function Mover.GetName() : String;
begin GetName := 'Moving for each Add'; end;
constructor Wrapper.Create(v : Byte);
begin val := v; end;
constructor Container1.Create();
begin q := TObjectQueue.Create; end;
procedure Container1.Add(v: Byte);
var tmp : Wrapper;
begin if q.Count > SIZE then begin tmp := Wrapper(q.Pop); tmp.Destroy; end; q.Push(Wrapper.Create(v)); end;
function Container1.Get() : Data;
var res : Data; i : Integer; tmp : Wrapper;
begin for i := 0 to SIZE do begin tmp := Wrapper(q.Pop); res[i] := tmp.Value; tmp.Destroy; end; Get := res; end;
destructor Container1.Destroy;
begin q.Destroy; inherited; end;
function Container1.GetName() : String;
begin GetName := 'Using builtin TObjectQueue'; end;
constructor Container2.Create();
begin q := TQueue.Create; end;
procedure Container2.Add(v: Byte);
begin if q.Count > SIZE then begin q.Pop; end; q.Push(Pointer(v)); end;
function Container2.Get() : Data;
var res : Data; i : Integer;
begin for i := 0 to SIZE do begin res[i] := Byte(q.Pop); end; Get := res; end;
destructor Container2.Destroy;
begin q.Destroy; inherited; end;
function Container2.GetName() : String;
begin GetName := 'Using builtin TQueue'; end;
constructor Circular.Create();
begin ix := 0; end;
procedure Circular.Add(v: Byte);
begin buf[ix] := v; ix := (ix + 1) mod (SIZE + 1); end;
function Circular.Get() : Data;
var res : Data; i : Integer;
begin for i := 0 to SIZE + 1 do begin res[i] := buf[(ix + i) mod (SIZE + 1)]; end; Get := res; end;
function Circular.GetName() : String;
begin GetName := 'Just circling around'; end;
constructor DoubleLinkedList.Create;
begin n := 0; end;
procedure DoubleLinkedList.Add(v : Byte);
var tmp : PElement;
begin new(tmp); tmp^.val := v; if n = 0 then begin tmp^.next := nil; tmp^.prev := nil; head := tmp; tail := tmp; Inc(n); end else if n <= SIZE then begin tmp^.next := nil; tmp^.prev := tail; tail^.next := tmp; tail := tmp; Inc(n); end else begin tmp^.next := nil; tmp^.prev := tail; tail^.next := tmp; tail := tmp; tmp := head^.next; dispose(head); head := tmp; head^.prev := nil; end; end;
function DoubleLinkedList.Get() : Data;
var res : Data; ix : Integer; curr, tmp : PElement;
begin curr := head; ix := 0; while curr <> nil do begin res[ix] := curr^.val; Inc(ix); tmp := curr; curr := curr^.next; dispose(tmp); end; Get := res; end;
function DoubleLinkedList.GetName() : String;
begin GetName := 'My own double linked list'; end;
procedure Test(impl : ITest);
var i : Integer; tmp : Data; t1, t2 : Integer;
begin t1 := GetTickCount; for i := 0 to 1234567 do begin impl.Add(i); end; t2 := GetTickCount; tmp := impl.Get; writeln(impl.Name); write('result: '); for i := 0 to SIZE do begin write(' ', tmp[i]); end; writeln; writeln('time: ', t2 - t1); end;
Måske kunne man komme et stykke vej med et par indeks-variable i kø-klassen (en der markerer start og en til slut) - og en uheldig lejlighedsvis oprydning. TQueue er ikke beregnet til store mængder data. De har nok været forhippet på at skulle nedarve fra et eller andet fremfor at gøre det pænt og effektivt.
mtj111: Hvis du har brug for en stak (FILO: først ind, sidst ud) behøver du slet ikke "move" noget. Du kan bruge den indbyggede TStack eller den helt simple løsning jeg lavede ovenfor (TMyStack).
.. og jeg tror (stadig) det er stakken du har brug for.
Hvis du har brug for en kø (FIFO: først ind, først ud) så er Arnes løsning meget effektiv.
Jeg synes det var on-topic nok, til at det burde belønnes.
Men da jeg ikke kan skrive flere points i tekstboksene (den siger blot "pointfejl" (eller noget i den dur) når jeg prøver at afgive dem), vil jeg bare give de lovede point. Hvis der er nogen der føler sig snydt vedr. point, så siger de endelig bare til :)
Med venlig hilsen Michael
Synes godt om
Ny brugerNybegynder
Din løsning...
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.