Avatar billede prodoc Nybegynder
01. april 2004 - 12:01 Der er 17 kommentarer og
2 løsninger

Random valg af linie i Listbox (ved tryk på button)

Jeg har et musikprogram hvor jeg gerne vil have en tilføjelse, og det er at man trykker på en knap og så afspiller den random valgte sange fra Listboxen...

http://www.eksperten.dk/spm/239332
Avatar billede borrisholt Novice
01. april 2004 - 12:03 #1
ListBox1.ItemIndex := Random (ListBox1.Items.Count-1);

Jens B
Avatar billede nlf Nybegynder
01. april 2004 - 18:59 #2
Samme svar som ovenstående, bare mindre:

ListBox1.ItemIndex := Random(High(ListBox1.Items));
Avatar billede hrc Mester
01. april 2004 - 23:02 #3
Smæk "randomize;" et eller andet sted i initialiseringen (OnCreate f.eks), så bliver det ikke samme "tilfældige" rækkefølge.
Avatar billede prodoc Nybegynder
02. april 2004 - 02:20 #4
bliver det da den samme (tilfældige) rækkefølge med borrisholt og nifs svar:
Dvs:  1. gang = 1, 4, 2, 5, 6 - 2. gang = 1, 4, 2, 5, 6 - 2 ??

Kunne da være dejligt hvis det var en forskellig random rækkefølge for hver gang man trykkede på knappen....

Men hvordan får man den til at gøre det, når rækkefølgen skal ændrer sig for hver gang man trykker på en button..... man kan jo ikke genstarte programmet for hver gang?
Avatar billede borrisholt Novice
02. april 2004 - 08:22 #5
Det er fordi du har glemt at kalde Randomize på FormCreate
Avatar billede prodoc Nybegynder
04. april 2004 - 17:58 #6
Borrisholt:
hvad skal jeg så skrive under form.create? :)
Avatar billede hrc Mester
05. april 2004 - 15:30 #7
prodic:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;
end;

Jeg kan anbefale dig at slå op i Delphis hjælp. Den er bekvent placeret på F1-tasten og man skriver blot randomize;, stiller cursoren inde i ordet og trykker F1.
I den forbindelse vil jeg understrege, at det ikke er ulovligt at søge oplysninger selv - dine to sidste spørgsmål synes jeg ikke røber stor aktivitet fra din side.
Avatar billede prodoc Nybegynder
07. april 2004 - 13:18 #8
HA HA...
Troede bare ikke at en Randomize ville gøre det selv.... man plejer jo normalt at skulle binde den til noget....
men tak for rådet...........
Avatar billede prodoc Nybegynder
07. april 2004 - 13:25 #9
Kan man lave en randomize der kun vælger de "ItemIndex'es" der ikke gør er blevet taget..... (dvs den ikke vælger den samme linie to gange?
Avatar billede hrc Mester
08. april 2004 - 01:02 #10
procedure TfrmMain.Button1Click(Sender: TObject);

  procedure Reset;
  var
    i : integer;
  begin
    for i := 0 to ListBox1.Count - 1 do
      ListBox1.Items.Objects[i] := nil;
  end;

  function PickOne : string;
  var
    bOK : boolean;
    i : integer;
  begin
    result := '';

    // Check for any to pick from (they might all be taken)
    i := -1; bOK := false;
    while (i < ListBox1.Count - 1) and not bOK do begin
      inc(i);
      bOK := not boolean(ListBox1.Items.Objects[i]);
    end;

    i := -1; // A none-to-pick-value
    if bOK then begin // There are some...
      bOK := false; // Reset flag
      while not bOK do begin
        i := random(ListBox1.Count);
        bOK := boolean(ListBox1.Items.Objects[i]);
      end;
      // Set picked flag
      ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True
    end;

    if bOK then
      result := ListBox1.Items[i];
  end;

begin
  MessageDlg(PickOne, mtInformation, [mbOK], 0);
end;

Det grimme består i, at da en linie i en TStringList (som TListBox'en bruger til linierne) ikke har en "Tag"-property som man kan bruge (modsat de visuelle komponenter), så bruger jeg en property der er beregnet til at indeholde objekter (pointere) som flag på om den er valgt eller ej.

Det, at jeg sætter den fundne linies objekt til pointer(1), får pointeren til at pege på adresse 1 i hukommelsen - nu bruger jeg ikke objects til andet - og det er derfor det virker!!! Man bruger en property hvis navn indikerer et helt andet brug.

Den pænere måde er at lave et objekt der indeholder linien i ListBoksen og en property der hedder Picked. Objektet placeres i liniens Object-property.

Kig her: Et pænere eksempel, men hvis du ikke har erfaring udi objekter så er det måske spildte kræfter ;-)

type
  TLine = class // Først lige en lille klasse
  private
    fText : string;
    fPicked : boolean; // Er den tidligere valgt?
  public
    constructor Create(const aText : string);
    property Picked : boolean read fPicked write fPicked;
  end;

implementation

constructor TLine.Create(const aText : string);
begin
  inherited Create;
  fText := aText;
  fPicked := false;
end;

// Indlæser fra en tekstfil, opretter objekterne og fylder dem i listboksen
procedure TForm1.ReadText;
var
  fp : text;
  st : string;
  Line : TLine;
begin
  if not FileExists('test.dat') then
    exit;

  assignfile(fp,'test.dat');
  reset(fp);
  ListBox1.Items.BeginUpdate;
  try
    ListBox1.Clear;
    while not eof(fp) do begin
      readln(fp,st);
      Line := TLine.Create(st);
      ListBox1.AddItem(Line.Text,Line);
    end; // while
  finally
    ListBox1.Items.EndUpdate;
    closefile(fp);
  end;
end;

// For at frigive objekterne må man kalde nedenstående
procedure TForm1.ClearText;
var
  i : integer;
begin
  for i := 0 to ListBox1.Count - 1 do
    ListBox1.Items.Objects[i].Free;
  ListBox1.Clear;
end;

function TForm1.PickOne : string;
var
  bOK : boolean;
  i : integer;
begin
  result := '';

  // Check for any to pick from (they might all be taken)
  i := -1; bOK := false;
  while (i < ListBox1.Count - 1) and not bOK do begin
    inc(i);
    bOK := not TLine(ListBox1.Items.Objects[i]).Picked;
  end;

  i := -1; // A none-to-pick-value
  if bOK then begin // There are some...
    bOK := false; // Reset flag
    while not bOK do begin
      i := random(ListBox1.Count);
      bOK := not TLine(ListBox1.Items.Objects[i]).Picked;
    end;

    // Set picked flag
    TLine(ListBox1.Items.Objects[i]).Picked := true;
  end;

  if bOK then
    result := ListBox1.Items[i];
end;

Det ændrede ikke meget ved PickOne, men den er pænere. Man kan så diskutere om ikke en løsning der fylder 20 linier er bedre end den der fylder det dobbelte.
Avatar billede prodoc Nybegynder
11. april 2004 - 21:36 #11
Hrc:
Tak for den gode beskrivelse..... men det tyder lidt på at løkken:

procedure TfrmMain.Button1Click(Sender: TObject);

  procedure Reset;
  var
    i : integer;
  begin
    for i := 0 to ListBox1.Count - 1 do
      ListBox1.Items.Objects[i] := nil;
  end;

  function PickOne : string;
  var
    bOK : boolean;
    i : integer;
  begin
    result := '';

    // Check for any to pick from (they might all be taken)
    i := -1; bOK := false;
    while (i < ListBox1.Count - 1) and not bOK do begin
      inc(i);
      bOK := not boolean(ListBox1.Items.Objects[i]);
    end;

    i := -1; // A none-to-pick-value
    if bOK then begin // There are some...
      bOK := false; // Reset flag
      while not bOK do begin
        i := random(ListBox1.Count);
        bOK := boolean(ListBox1.Items.Objects[i]);
      end;
      // Set picked flag
      ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True
    end;

    if bOK then
      result := ListBox1.Items[i];
  end;

begin
  MessageDlg(PickOne, mtInformation, [mbOK], 0);
end;

er uendelig, for min pc går kold hver gang jeg starter programmet....
Avatar billede borrisholt Novice
12. april 2004 - 16:14 #12
Jeg kan umuligt gennemskue hvad du har gang i med her er en algoritme der kan gøre det for dig :

var
  i: Integer;
  PrevSelected: Boolean;
  AllSelected: Boolean;
begin
  AllSelected := True;
  for i := 0 to ListBox1.Items.Count - 1 do
    AllSelected := AllSelected AND (ListBox1.Items.Objects[i] <> nil);

  if AllSelected then
    Exit;

  PrevSelected := True;
  while PrevSelected do
  begin
    i := Random(ListBox1.Items.Count);
    PrevSelected := ListBox1.Items.Objects[i] <> nil;
  end;

  if i >= 0 then
  begin
    ListBox1.Items.Objects[i] := TObject(i+1);//DU MÅ Ikke STOPPE 0 (NUL) In s på denne plads, så virker det ikkke
    ListBox2.Items.Add(ListBox1.Items[i]);
  end;
end;


Jens B
Avatar billede hrc Mester
14. april 2004 - 00:37 #13
borrisholt: Der manglede kun et lille "not" i mit eksempel - som i øvrigt gør nøjagtigt det samme som dit.

  function PickOne : string;
  var
    bOK : boolean;
    i : integer;
  begin
    result := '';

    // Check for any to pick from (they might all be taken)
    i := -1; bOK := false;
    while (i < ListBox1.Count - 1) and not bOK do begin
      inc(i);
      bOK := not boolean(ListBox1.Items.Objects[i]);
    end;

    i := -1; // A none-to-pick-value
    if bOK then begin // There are some...
      bOK := false; // Reset flag
      while not bOK do begin
        i := random(ListBox1.Count);
        bOK := NOT boolean(ListBox1.Items.Objects[i]); // << Her var fejlen.
      end;
      // Set picked flag
      ListBox1.Items.Objects[i] := pointer(1); // a.k.a. True
    end;

    if bOK then
      result := ListBox1.Items[i];
  end;

og tjaa, jeg ved ikke, men hos mig virker det altså med at

  boolean(ListBox1.Items.Objects[i])

returnerer true eller false
Avatar billede borrisholt Novice
14. april 2004 - 08:29 #14
hrc>> Det er i høj grad et tempraments spørgsmål om man buuger  boolean(ListBox1.Items.Objects[i]) eller ListBox1.Items.Objects[i] <> nil;

Den første er noget der stammer fra C++ kulturen.

Jens B
Avatar billede hrc Mester
14. april 2004 - 14:32 #15
borrisholt: Jeg synes nu ikke der er den store forskel fra det du "umuligt kan gennemskue" og dit eksempel. Opbygningen er den samme. Om vi typecaster eller tjekker om noget er nil er detaljer.

I øvrigt: At nogen skulle sidestille min programmering med noget fra C++ kulturen - tjaa, Jeg typecaster, det indrømmer jeg, men det er nu alligevel første gang det er sket. Troede alligevel at kvaliteten var bedre end det ;-)
Avatar billede borrisholt Novice
14. april 2004 - 14:40 #16
Fair nok, lad os lige fjerne et par mis forståelser :
1)
  jeg havde en skod dag i går, og endnu mere i Mandags ! Hotlinen !
2)
  Det var prodoc's jeg ikke lige kunne overskue !
3)
  Den der med at tage den boolske værdi på en pointer, DET VAR DET jeg mente var en C++ ting !
4)
  LÆS http://www.eksperten.dk/spm/488793

Jens B
Avatar billede hrc Mester
14. april 2004 - 15:13 #17
borrisholt (og undskyld til prodoc for at bruge tråden som platform for dette):

ad. 1: Jeg var overhovedet ikke fornærmet eller noget. Skoddage har vi alle og det skinnede lidt igennem i dit indlæg.

ad. 2: Det som prodoc rodede med var jo et eksempel jeg havde lavet.

ad. 3: Indrømmet. Når jeg typecaster kan det blive underligt. Min (usikre) favorit-linie er inc(i,integer(i < 10)); - og det er ret nørdet. Heldigvis returnerer Delphis boolske udtryk altid 0 eller 1 - havde været uheldigt om integer(i < 10) returnerede et tilfældigt tal <> 0. Det er den slags skod-kode hvorom jeg moraliserer når andre bruger det.

ad. 4: Tak for de rosende ord - det betyder en del når det kommer fra dig (kompliment hermed returneret). Jeg har altid været inkarneret delphi-programmør, men har lært en masse i den korte tid jeg har spøgt på eksperten. Med min tilstædeværelse håber jeg at kunne præge andres programmering så så de rutinemæssigt undgår "globale variable", glemte objekter og anden usikker kodning.

ad. 4b: Min offentlige email-adresse er i øvrigt "hrc_public snabela hotmail dot com" - håber notationen forvirrer eventuelle smail-sugende spamrobotter.
Avatar billede borrisholt Novice
14. april 2004 - 17:44 #18
hrc>> Bruger du msn eller ICQ

ICQ : 31-312-742
MSN : Jens snabela Borrisholt dot com

Jens B
Avatar billede prodoc Nybegynder
15. april 2004 - 11:55 #19
Det er helt i orden drenge:)
og Borrisholt, undskyld hvis jeg trængte mig på!
Borrisholt: jeg har prøvet dig eksempel:
var
  i: Integer;
  PrevSelected: Boolean;
  AllSelected: Boolean;
begin
  AllSelected := True;
  for i := 0 to ListBox1.Items.Count - 1 do
    AllSelected := AllSelected AND (ListBox1.Items.Objects[i] <> nil);

  if AllSelected then
    Exit;

  PrevSelected := True;
  while PrevSelected do
  begin
    i := Random(ListBox1.Items.Count);
    PrevSelected := ListBox1.Items.Objects[i] <> nil;
  end;

  if i >= 0 then
  begin
    ListBox1.Items.Objects[i] := TObject(i+1);//DU MÅ Ikke STOPPE 0 (NUL) In s på denne plads, så virker det ikkke
    ListBox2.Items.Add(ListBox1.Items[i]);
  end;
end;

-Men er det ikke muligt at den bare markerer linien i Listbox1 (og ikke skriver det til Listbox2, har prøvet men den vil bare ikke)

-og kan man få den til at loope, så når den har taget alle linierne (random) een gang så starter den forfra med en random?
Avatar billede Ny bruger Nybegynder

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester