Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Zahlen überprüfen --> keine darf doppelt (https://www.delphipraxis.net/95008-zahlen-ueberpruefen-keine-darf-doppelt.html)

Christian18 29. Jun 2007 01:34


Zahlen überprüfen --> keine darf doppelt
 
Hallo,

ich soll als Hausarbeit ein kleines Tool schreiben, welches Zahlen miteinander vergleicht. Ich habe 4 Edit Felder in den Zahlen von eins bis vier eingetragen werden sollen. Bsp.: 1 2 3 4 Es darf keine Zahl doppelt eingetragen werden. Also so nicht: 1 2 2 4

Also ähnlich wie bei Sudoku!!!

Ich habe das Programm schon fertig. Es gibt nur ein Problem. Die Routine, die überprüft ob eine Zahl doppelt vorkommt, soll erweiterbar sein. Das heisst es kann passieren, dass mal 5 oder 6 Edit Felder hinzugefügt werden. Dann soll es mit so wenig aufwand wie möglich trotzdem Funktionieren. Und das ist mein Problem.

Ich hatte das Problem folgendermäßen gelöst. Ich habe alle Permutionen von 1234 gebildet und in ein Array gespeichert.

Dann habe ich die Eingabe des Benutzers mit dem Array verglichen. Wenn die Eingabe des Benutzers im Array vorhanden ist, dann ist die Eingabe richtig, sonst ist bei der Eingabe ein Fehler unterlaufen. Kann man das eigentlich so machen? Oder gibt es trotzdem eine Variante um an der überprüfung vorbei zu kommen? Weiss nicht!!!

So, das funktioniert alles super. Ich kann es auch auf 10 und mehr Edit Felder erweitern, bloß die ganze Angelegenheit dauert ein weilchen. Hat ihr vieleicht einen Tipp für mich wie ich das ganze schneller machen kann???

MFG Christian18

fwsp 29. Jun 2007 01:43

Re: Zahlen überprüfen --> keine darf doppelt
 
ich würde die eingaben in ein array lesen. anschließend das array sortieren. und dann musst du nur noch überprüfen ob 2 mal die gleiche zahl hintereinander im array steht.

bsp:
eingaben: 5, 6, 4, 6, 8
sortiert: 4, 5, 6, 6, 8
und wenn du dann an die dritte stelle kommst, merkst du ja, dass danach nochmal die gleiche zahl kommt. das ganze sollte dann nicht so lange dauern.

Christian18 29. Jun 2007 02:05

Re: Zahlen überprüfen --> keine darf doppelt
 
Hallo,

ja stimmt. Dein Lösungsansatz ist auf jeden Fall effiktiver als meiner. Gibt es vieleicht noch effektivere Ansätze??? Freu mich schon auf die Antworten.

[EDIT]
Ich vermute mal das es irgendwann mal ein Sudoku Spiel werden soll. Denn warum die erweiterung ??? Macht für mich keinen Sinn!!! aber naja mal schauen was kommt.

LG Chris

cruiser 29. Jun 2007 02:19

Re: Zahlen überprüfen --> keine darf doppelt
 
Hum...

1.) Controls durchgehn
2.) Wenn Edit in Zahl umwandeln
3.) Zahl mit Array prüfen
-> wenn nicht drin: reinschreiben
-> wenn drin: Routine abbrechen
evtl. Edit umfärben.

negaH 29. Jun 2007 03:49

Re: Zahlen überprüfen --> keine darf doppelt
 
Du benutzt ur sehr sehr wenig Zahlen -> 1 bis 4. Das lässt sich immer besser optimieren als zb. der Vorschlag die Zahlen in ein Array[] zu speichern, es zu sortieren und auf Duplikate zu prüfen. In deinem Falle ist dies nämlich sehr umständlich.

Baue dir ein Array[1..4] of Boolean. Initialisiere es mikt Fillchar(Array, SizeOf(Array), False), also alle Elemente = FALSE. Nachdem der Benutzer in einem Edit eine Zahl eingegeben hat, zb. im OnKeyPress(), wandelst du mit StrToInt(Edit.Text); dessen Zahl in einen Integer um. Dieser Wert wird als erstes überprüft auf >=1 und <=4, also StrToInt(Edit.Text) >= Low(Array) und <= High(Array). Danach benutzt du diesen Index ins Array und schaust nach ob dort TRUE oder FALSE steht. Bei TRUE hat schon ein anderes Edit die gleiche Zahl bei FALSE noch kein anderes Edit, ergo: das Array Elemlent auf TRRUE setzen.

Lange Rede kurzer Source

Delphi-Quellcode:
type
  TForm1 = class(TForm)
  private
    FZahlen: array[1..4] of Boolean;
  end;

procedure TForm1.EditKeyPress(var Key: Char; ....)
// alle deine Edits verweisen mit OnKeyPress() auf diesen Eventhandler
var
  I: Integer;
begin
  if Key in ['0'..'9'] then
  begin
    I := StrToInt(Key);
    if (I >= Low(FZahlen)) and (I <= High(FZahlen)) and not FZahlen[I] then FZahlen[I] := True
      else Key := #0; // lösche Taste, erscheint also nicht in Edit.Text
  end;
end;

procedure TForm1.ButtonRestartClick();
// lösche alle Edits und das FZahlen Array, Benutzer kann nun neu eingeben
var
  I: Integer;
begin
  FillChar(FZahlen, SizeOf(FZahlen), 0);
  for I := 0 to ControlCount -1  do
    if Controls[I] is TEdit then
       TEdit(Controls[I]).Text := '';
end;
Vorteile sind eben das es sehr einfach verständlich ist und aus Sicht der Performance wird es kein schnellers Verfahren geben in deinem Falle. Die Komplexität meines Vorschlages ist O(1) (Big O) und damit weitaus schneller als jeder Sortier- oder Suchalgorithmus.

Man kann das so wie oben machen immer dann wenn man nur sehr kleine und wenige Zahlen überprüfen/sortieren muß.

Gruß Hagen

oldmax 29. Jun 2007 06:43

Re: Zahlen überprüfen --> keine darf doppelt
 
Hi
Denkbar wäre auch eine globale Stringvariable, die im OnKeypressed-Ereignis über Posnach der Zahl sucht und diese eventuell anhängt.
Delphi-Quellcode:
if Pos(Key, Zahlenstring) =0 then ZahlenString:=ZahlenString+Key else Key:=#0;
In der Reset- Routine reicht dann ein
Delphi-Quellcode:
Zahlenstring:='';
ok, damit keine anderen Zeichen in den Zahlenstring kommen, die Abfrage ist bereits schon erklärt...
Gruß oldmax

Niels14 29. Jun 2007 07:16

Re: Zahlen überprüfen --> keine darf doppelt
 
Hi,
waere es in diesem Fall nicht schneller einen mit 0 initialisierten Bit Array zu haben?
Dann koenntest Du der Reihe nach jeden Bit Array auf Eins setzen (00001000000...), dessen Index der Zahl entspricht.
Davor kannst Du noch checken ob der schon eins hat oder nicht, also schon im String vorkommt.
Sollte eigentlich sehr platzsparend, schnell und einfach zu erweitern sein!
LG Niels

oldmax 29. Jun 2007 09:24

Re: Zahlen überprüfen --> keine darf doppelt
 
hi
Also, alle Abfragen mit onkeypressed sind nur geeignet für Zahlen von 0 - 9, aber was ist mit 164555 ?
dazu Zitat :
Zitat:

Die Routine, die überprüft ob eine Zahl doppelt vorkommt, soll erweiterbar sein.
Mehrstellige Zahlen lassen sich beim Verlassen eines Editfeldes checken, eben auch mit der Pos-Function. Dazu brauchts nur einen kleinen Trick: Die Zahlengrenzen müssen markiert werden. z.B. mit einem Semikolon. Dann wird der String nicht mehr mit '' initialisiert, sondern mit ';'
in der OnExit -Methode prüft man

Delphi-Quellcode:
If Pos(';'+Edit1.Text+';',Zahlenstring)=0 then ZahlenString:=Zahlenstring+Edit1.Text+';'
 else Edit1.Text:='';
Soviel zur Erweiterbarkeit und unbekannten Grenzen......
@niels
Natürlich gibt's Arrays und in einem Array [0..9] of Boolean kann ich jede Zahl markieren. Schneller.. hmmm, bei Zahlen von 0 bis 9 vermutlich nur für den PC, nicht für den Anwender, denn der merkt den Unterschied zwischen 3ms und 30 wohl kaum, obwohl da Faktor 10 drin ist.... Aber gut, manchmal machts schon Sinn, Geschwindigkeitsoptimiert zu denken und zu programmieren.
Gruß oldmax

negaH 29. Jun 2007 16:20

Re: Zahlen überprüfen --> keine darf doppelt
 
Es geht nicht darum "Geschwindigkeitsoptimiert" zu arbeiten, sondern um effektiv und effizient zu arbeiten. Entscheidend ist das der Programmierer hierbei die zu dem Problem optimal passende Lösung findet. Sie sollte schlicht sein und nach Möglichkeit annerkannte Standard-Algos. der Programmierung benutzen, wenn sie auch Sinn machen.

Beispiel eben 6 Zahlen zwischen 1 bis 6 auf Duplikate prüfen. Ein sehr einfaches Problem mag man meinen, aber an Hand der unterschiedlichen Lösungsansätze kann man sehr wohl erkennen wie der Wissenstand, bzw. die Fähigkeit des Programmieres ein Problem analytisch zerlegen zu können, gereift ist.

Standdardlösung wäre es die Zahlen in ein Array/Liste zu packen, diese zu sortieren und vor dem Einfügen eines neuen Wertes per binärer Suche in dieser Liste den Wert zu überprüfen. Eine idelae Aufgabe für eine TIntegerList (TList Nachlkommen) die mit Duplicates=dupIgnore arbeitet. Aber in diesem Falle ist es auf Grund der geringen Zahlen/Menge offensichtlich das man eine viel simplere Lösung benutzten kann. Also einfach ein Array/Liste mit der maximalen Anzahl aller vorkommenden Zahlen initialisieren und dann einfach jede Zahl aus dem Array rausstreichen die eingegeben wurde. Da unsere jeweilige Zahl der direkte Index in dieses Array[] darstellt entfällt das Durchsuchen/Sortieren eines Speicherbereiches. Nun stellt sich die Frage welche Datentypen der Programmierer dafür wählt. Auch hier gibts essentiell zwei unterschiedliche Denkweisen:

1.) man nehme schon vorhandene Klassen/Code egal ob er überhaupt geeignet ist. Das sind Vorschläge wie "nim TList und caste alle deine Integer von/nach Pointer", oder "nimm einen String der erstmal leer ist, hänge die Zahlen als String hintendran und überprüfe mit Pos ob die neue Zahl vorkommt im String".

2.) man benutzt die Fähigkeit der Programmiersprache gezielt eigene Datentypen zu konstruieren, eben ein array[1...6] of Boolean.

Der Unterschied in beiden Arbeitsstilen ist der das der eine immer nur das Problem zu lösen versucht indem er Standardgeschichten die schon fertig sind benutzt. Er passt quasi die Lösung des Problemes an die ihm zur Verfügung stehenden Mittel an. Benötigt man zur Lösung des Problemes einen Hammer und die Programmiersprache enthält keine vorgefertigte Klasse/Code "Hammer" dann wird ein solcher Programmierer es schwerer haben. Der andere baut sich eine eigene Umwelt in der er das jeweilige Problem optimal lösen kann, indem er der Programmiersprache sagt was er haben möchte als Umwelt. Man könnte sagen das dies den Unterschied zwischen einem Anwendungs- und Systementwickler ausmacht.

Bei meinen Postings ging es also nicht darum das der erzeugte Code nun die gleiche Aufgabe in 3ms statt 30ms lösst, sondern darum aufzuzeigen das in diesem Falle auch eine ganz ganz simple Lösung optimal sein kann. Es ist klar das wenn sich die Aufgabenstellung verändert, zb. > 100 Zahlen und nicht nur einstellige Zahlen, sich auch die benutzte Lösung unterscheiden muß. Das was mich "wurmt" ist eher der Punkt das Vorschläge kamen die eher sehr unelegant sind, zb. mit Strings zu rechnen statt gezielt eigene Datentypen zu benutzen ! Das ist so als würde der Programmierer nur 50% der Leistungsfähigkeit seiner Programmiersprache benutzen, denn man programmiert nicht nur Klassen, Prozeduren und Formulare sondern meist als Schritt davor programmiert/deklariert man seine Datentypen zugeschnitten auf das Problem.

Gruß Hagen

marabu 29. Jun 2007 18:48

Re: Zahlen überprüfen --> keine darf doppelt
 
Hallo,

ich hätte auch noch einen Lösungsansatz - ohne Anspruch auf Reife:

Delphi-Quellcode:
uses
  StrUtils;

type
  TNumberSet = set of 1..9;

var
  NumberSet: TNumberSet;

function CheckNumber(var ns: TNumberSet; n: Integer): Boolean;
begin
  Result := True;
  if not (n in ns)
    then ns := ns + [n]
    else Result := False;
end;

procedure TDemoForm.ButtonClick(Sender: TObject);
const
  FMT = '%d ist %s';
var
  i, n: Integer;
  b: Boolean;
begin
  NumberSet := [];
  for i := 1 to 9 do
  begin
    n := Succ(Random(9));
    b := CheckNumber(NumberSet, n);
    ShowMessage(Format(FMT, [n, IfThen(b, 'okay', 'doppelt')]));
  end;
end;
Freundliche Grüße

cruiser 29. Jun 2007 18:49

Re: Zahlen überprüfen --> keine darf doppelt
 
Mal mein Ansatz als Code:

Delphi-Quellcode:
function CheckDuplicates(ParentControl: TWinControl; var WrongEdit: TEdit): Boolean;
var
  i,n: Integer;
  ba: array of Boolean;
begin
  Result := False;
  n := 0;

  // Edits zaehlen
  for i := 0 to Pred(ParentControl.ControlCount) do
    if ParentControl.Controls[i].InheritsFrom(TEdit) then
      Inc(n);
  // Keine Edits? Dann Ende.
  if n = 0 then exit;
  // ByteArray setzen
  SetLength(ba, n);
  // Edits checken
  for i := 0 to Pred(ParentControl.ControlCount) do
    if ParentControl.Controls[i].InheritsFrom(TEdit) then
    begin
      // Zahl auslesen, wenn keine Zahl dann 0, wenn 0 dann ignorieren
      n := StrToIntDef((ParentControl.Controls[i] as TEdit).Text, 0) - 1;
      if n >= 0 then
      begin
        // Mit Array vergleichen
        If ba[n] then
        begin
          WrongEdit := (ParentControl.Controls[i] as TEdit);
          Result := True;
          Exit;
        end else
          ba[n] := true;
      end;
    end;
end;
Es geht die Edits unter einem Control durch, zählt TEdit-Ableger, und prüft gegen ein dynamisches Bool-Array.

Trifft es auf ein Edit mit einer Zahl, die schon existiert, bricht es ab und notiert dieses Edit in WrongEdit. So kann man dieses selektieren, markieren, etc.

Aufgerufen wereden könnt es etwa so:
Delphi-Quellcode:
var
  we: Tedit;
begin
  if CheckDuplicates(Panel1, we) then
  begin
    // mach etwas mit we
  end;
end;
Ein netter Nebeneffekt ist, dass es Zahlen, die grösser als die Anzahl der Edits sind ebenfalls als Doppelte angezeigt werden...

[edit] noch ein bissel Entbugged ;) [/edit]

negaH 29. Jun 2007 21:40

Re: Zahlen überprüfen --> keine darf doppelt
 
Hi wenn du das schon so machst dann kannst du auch gleich auf Duplikat prüfen ohne dein Array[] of Boolean

Delphi-Quellcode:
function CheckDuplicates(const Edit: TEdit): Boolean;
var
  I: Integer;
  Zahl: String;
begin
  Result := False;
  if Edit is TEdit then
  begin
    Zahl := AnsiUpperCase(FullTrim(Edit.Text));
    with Edit.Parent do
      for I := 0 to ControlCount -1 do
        if (Controls[I] <> Edit) and (Controls[I] is TEdit) and AnsiCompareText(FullTrim(TEdit(Controls[I]).Text)), Zahl) = 0 then
          Exit;
    Result := True;
  end;
end;

procedure TForm1.EditChange(Sender: TObject);
// alle TEdit.OnChange verweisen auf diesen Eventhandler
begin
  if CheckDuplicates(Sender as TEdit) then
    with Sender as TEdit do
      raise Exception.CreateFmt('Der Wert %s aus %s kommt schon vor', [Text, Name]);
end;
Gruß Hagen

cruiser 29. Jun 2007 22:41

Re: Zahlen überprüfen --> keine darf doppelt
 
Hm... ja, setzt aber voraus, dass man bei jedem Edit das event definiert...
Najo.

Viele Wege führ'n nach Rom :mrgreen:

negaH 30. Jun 2007 12:59

Re: Zahlen überprüfen --> keine darf doppelt
 
Und? alle TEdits auf dem Form per STRG+Maus Taste markieren, in OnChange im OI dann auf TForm1.EditCHange() setzen.
Geht man diesen Weg nicht so muß man denoch irgendeinen Zeitpunkt, bei dem sich der Inhalt eines der TEdits verändert, aussuchen und dann die Funktion CheckDuplicates() per Hand im Source aufrufen. Na, da bevorzuge ich doch die Events im OI auf einen gemeinsammen Eventhandler zu setzen, das kostet dann keinerlei extra Quelltext.

Gruß Hagen

cruiser 30. Jun 2007 17:54

Re: Zahlen überprüfen --> keine darf doppelt
 
Ich sag doch gar nix dagegen, Hagen... :angel2:
Da ich nicht vor der Aufgabe stand und nur einen Lösungsansatz zeigen wollt, hab ich nur nicht weiter gedacht als nötig war :oops:

oldmax 2. Jul 2007 06:47

Re: Zahlen überprüfen --> keine darf doppelt
 
Hi
@ Hagen
Nun, nicht das ich dir auf die Füße treten will, aber manchmal irrt auch ein Fachmann.
Zitat:
Zitat:

Das was mich "wurmt" ist eher der Punkt das Vorschläge kamen die eher sehr unelegant sind, zb. mit Strings zu rechnen statt gezielt eigene Datentypen zu benutzen !
Wo bitteschön wird denn mit Strings gerechnet. Um ein Ergebnis eines Editfeldes zu prüfen, muß ich nicht immer eigene Datentypen erfinden... und bis ein Anfänger das Verständnis dafür hat, sind Hilfen über das gesamte Delphispektrum erlaubt. Ein fortgeschrittener Programmierer wird hier nicht wegen eines so banalen Problemes um Hilfe bitten. Was hilft es dem Fragesteller zu erklären "du mußt eigene Datentypen definieren und in einer Klasse sich selbst überlassen" oder so...
Sorry, für mich sind die Inhalte eines Editfeldes immer noch Strings und daher ist ein Vergleich mit Strings durchaus etwas, was dich nicht wurmen sollte.
Andererseits, mir ist's letztlich egal, ob's dich wurmt
Gruß oldmax

hoika 2. Jul 2007 07:30

Re: Zahlen überprüfen --> keine darf doppelt
 
Hallo,

so denke ich auch.
Warum neues (Datentyp) erfinden, wenn für die Aufgabe
auch TStringList geht.

mit Duplicates:= [dupError]

und try except um das Insert bekommt man ganz schnell Dopplungen raus.

Zu den Edits:
Ich würde hier noch Tag benutzen,
um die Zahlen-Edits von viell. vorhandenen
anderen Edits zu unterscheiden,also etwa so

Delphi-Quellcode:
for i:= 0 to Components.Count do
begin
  if Components[i] is TEdit then
  begin
    Edit:= TEdit(Components);
    if Edit.Tag=1 then
    begin
      // hier kommt jetzt das "versuche einzufügen"
      // das Edit kann z.B. benutzt werden,
      // um per Edit.Focus den falschen Eintrag zu markieren  
 
    end;
  end;
end;
Heiko

Sidorion 2. Jul 2007 10:13

Re: Zahlen überprüfen --> keine darf doppelt
 
Ne Stringliste wäre auch mein Vorschlag gewesen, da man sich hier zudam das Konvertieren nach Integer spart.
Damit das mit den Duplicates auch funktioniert nuss man allerdings die Stringliste auch sorted=true setzen.
Wenn man kein exception-handlig machen will, kann man die Liste auch auf dupIgnore stellen. Dann merkt man sich einfach vor dem Einfügen die Anzahl, fügt ein und wenn sich die Anzahl nicht erhöht hat, gabs die Zahl schonmal.

Diese Lösung ist nicht nur elegant, sondern auch effektiv, da die Stringliste eine Binärsuche verwendet, sobald sie sortiert ist.


Alle Zeitangaben in WEZ +1. Es ist jetzt 16:05 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