Kleines Rechenquizprogramm

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
engeldimo123
Beiträge: 4
Registriert: Di 28. Sep 2021, 15:47

Kleines Rechenquizprogramm

Beitrag von engeldimo123 »

Hallo Leute, ich habe mir ein kleines Rechenquiz programmiert, bei dem die Rechenarten und die Zahlen per Zufall ausgegeben werden. Der Anwender muss dann jeweils nur das richtige Ergebnis eingeben. Meine Frage ist jetzt nur, wie bekomme ich das hin, dass den Rechenarten Minus und Geteilt die erste Zahl immer größer ist?

program rechenquiz;
uses crt;
var x,y,z,rechenart : integer;
weiter : char;

// Addition
procedure plus;
begin
randomize;
x := random(100);
y := random(100);
write(x,' + ',y,' = ');
readln(z);
if z = x+y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x+y);
end;

// Subtraktion
procedure minus;
begin
randomize;
x := random(100);
y := random(100);
if x > y then
write(x,' - ',y,' = ');
readln(z);
if z = x-y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x-y);
end;

// Multiplikation
procedure mal;
begin
randomize;
x := random(100);
y := random(10);
if x > y then
write(x,' * ',y,' = ');
readln(z);
if z = x*y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x*y);
end;

// Division
procedure geteilt;
begin
randomize;
x := random(100);
y := random(10);
if x > y then
write(x,' / ',y,' = ');
readln(z);
if z = x div y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x div y);
end;

procedure eingabe;
begin
repeat
case rechenart of
0: plus;
1: minus;
2: mal;
3: geteilt;
end;
randomize;
rechenart := random(4);
writeln;
write('weiter j/n : ');
read(weiter);
writeln;
until weiter = 'n';
end;

begin
clrscr;
writeln('Das ist ein Rechenquizprogramm');
writeln('------------------------------');
writeln;
eingabe;

readln;
end.


Danke schon mal im Voraus,
Dietmar Erler

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Kleines Rechenquizprogramm

Beitrag von wp_xyz »

engeldimo123 hat geschrieben:
Di 28. Sep 2021, 16:14
wie bekomme ich das hin, dass den Rechenarten Minus und Geteilt die erste Zahl immer größer ist?
Einfach die Zahlen vertauschen. Schreibe dir eine Prozedur dafür, weil das öfter vorkommt. Übergib die zwei Zahlen als var Parameter, damit die aufrufende Routine die vertauschten Zahlen wirklich empfängt.

Code: Alles auswählen

procedure Vertauschen(var a, b: Integer);
Definiere in dieser Prozedur eine Hilfs-Variable vom entsprechenden Typ. Schreibe die erste Zahl in die Hilfsvariable, dann die zweite Zahl in die erste und zum Schluss die Hilfsvariable in die erste. Probier auf Papier aus, ob dieser Algorithmus funktioniert (er funktioniert nicht, weil ich in die verbale Beschreibung einen Fehler eingebaut habe...).

Rufe diese Prozedur immer dann auf, wenn die erste Zahl kleiner ist als die zweite und du das anders haben willst.

Code: Alles auswählen

// Subtraktion
procedure minus;
begin
  //randomize;  // Es reicht, wenn das nur 1x zu Programmbeginn aufgerufen wird.
  x := random(100);
  y := random(100);
  if x < y then
    Vertauschen(x, y);
  write(x,' - ',y,' = ');
  readln(z);
  if z = x-y then
    writeln('Richtig gerechnet!')
  else 
    writeln('Leider falsch, richtig ist ',x-y);
end;
Noch eine Bemerkung zu "Eingabe":

Code: Alles auswählen

procedure eingabe;
begin
  repeat
    case rechenart of
      0: plus;
      1: minus;
      2: mal;
      3: geteilt;
    end;
    randomize;
    rechenart := random(4);
    writeln;
    write('weiter j/n : ');
    read(weiter);
    writeln;
  until weiter = 'n';
end;
Welchen Wert hat "rechenart" wohl, wenn die repeat-Schleife zum ersten Mal durchlaufen wird? Überlege dir, ob die Case-Anweisung an der richtigen Stelle ist.

engeldimo123
Beiträge: 4
Registriert: Di 28. Sep 2021, 15:47

Re: Kleines Rechenquizprogramm

Beitrag von engeldimo123 »

Hallo,
danke dir für die kleine Hilfestellung. Also ich habe mir die Prozedur hier geschrieben:

Code: Alles auswählen

procedure vertauschen(a,b : integer);
begin
a := random(100);
b := random(100);
if a < b then
begin
x := b;
y := a;
end;
end;
Und dann habe ich noch bei der Pozededur eingabe die Random-Funktion vor die Case-Anweisung geschrieben, so vermeide ich, dass die erste Rechenart immer Plus ist. So scheint es jetzt gut zu funktionieren:

Code: Alles auswählen

program rechenquiz;
uses crt;
var x,y,z,rechenart : integer;
    weiter : char;

procedure vertauschen(a,b : integer);
begin
a := random(100);
b := random(100);
if a < b then
begin
x := b;
y := a;
end;
end;

// Addition
procedure plus;
begin
x := random(100);
y := random(100);
write(x,' + ',y,' = ');
readln(z);
if z = x+y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x+y);
end;

// Multiplikation
procedure mal;
begin
x := random(100);
y := random(10);
if x < y then
write(x,' * ',y,' = ');
readln(z);
if z = x*y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x*y);
end;

// Subtraktion
procedure minus;
begin
x := random(100);
y := random(100);
if x < y then
vertauschen(x,y);
write(x,' - ',y,' = ');
readln(z);
if z = x-y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x-y);
end;

// Division
procedure geteilt;
begin
x := random(100);
y := random(100);
if x > y then
vertauschen(x,y);
write(x,' / ',y,' = ');
readln(z);
if z = x div y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x div y);
end;

procedure eingabe;
begin

repeat
rechenart := random(4);
case rechenart of
0: plus;
1: minus;
2: mal;
3: geteilt;
end;

writeln;
write('weiter j/n : ');
read(weiter);
writeln;

until weiter = 'n';

end;

begin
clrscr;
randomize;
writeln('Dies ist ein kleines Rechenquiz mit den 4 Grundrechenarten');
writeln('-> Gib bei der Division nur die ganze Zahl ohne Rest ein');
writeln('----------------------------------------------------------');
writeln;

eingabe;

readln;
end.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Kleines Rechenquizprogramm

Beitrag von wp_xyz »

Ja, das mit der Case-Anweisung hast du jetzt richtig.

Aber die Vertauschen-Prozedur ist nicht gut. Wenn sie der Compiler akzeptiert, dann liegt das nur daran, dass du mit globalen Variablen arbeitest, was das alles sehr undurchsichtig macht. Und wenn sie überhaupt funktioniert, dann liegt das daran, dass du mit Zufallszahlen arbeitest und dadurch die Ergebnisse nicht gut prüfen kannst.

Das Hauptproblem mit Vertauschen ist, dass sie zwar zwei Zahlen a, b als Eingabeargumente verwendet, aber die vertauschten Zahlen nicht zurückliefert. Es funktioniert (wenn überhaupt) nur, weil du in der Prozedur auf globale Variablen zurückgreifst und dort das Ergebnis ablegst. Die Prozedur muss aber immer funktionieren, egal was es sonst noch für Variablen und Prozeduren gibt.

Jetzt nochmal, etwas ausführlicher: Die Prozedur erhält zwei Zahlen, a und b, als Eingabe, vertauscht sie und muss in a und b die vertauschten Zahlen an die aufrufenden Prozedur zurückgeben, also was vorher a war, ist nun b, und umgekehrt. Damit die Prozedur überhaupt Werte an die aufrufende Prozedur zurückgeben kann, müssen die Argumente als "var", also "variabel" oder "veränderbar" deklariert sein. Eine Beispiel-Programm mit einer etwas anderen Prozedur:

Code: Alles auswählen

program Test;

  procedure EinsAddieren(var a: Integer);
  begin
    a := a + 1;
  end;
  
var
  x: Integer;
begin
  x := 0;
  WriteLn(x);
  EinsAddieren(x);
  WriteLn(x);
 end.
x ist zunächst 0 und wird dann an die Prozedur EinsAddieren übergeben. In dieser Prozedur wird die Eingangsvariable, die hier "a" heißt, aber das tut nichts zur Sache, sie kann irgendwie heißen, eingelesen, um 1 erhöht und, weil es ein "var"-Parameter ist, ans hauptprogramm zurückgegeben. Die zweite WriteLn-Anweisung gibt daher den Wert 1 aus.

Um den Effekt des "var" zu verstehen, entferne es einmal testhalber, also deklariere procedure EinsAddieren(a: Integer) (ohne var). Nun gibt die zweite WriteLn-Anweisung den Wert 0 aus - weil EinsAddieren wegen des fehlenden "var" das Rechenergebnis nicht zurückgeben konnte.

Genauso machst du es mit der Vertauschen-Prozedur: procedure Vertauschen(var a: Integer; var b: Integer), oder einfacher Vertauschen(var a,b: Integer) - wir haben nun zwei Parameter, beide als "var" deklariert, die in der Prozedur verändert werden, und die aufrufende Prozedur kann mit den veränderten Werten weiterarbeiten.

Zum Vertauschen selbst brauchst du eine Hilfsvariable, nennen wir sie c. Diese brauchen wir nur innerhalb der Vertauschen-Prozedur, deshalb wird sie genau dort deklariert - nicht außerhalb als globale Variable, denn wenn du irgendwo anders noch eine globale Variable c bräuchtest, dann würdest du deren Wert durch die Vertauschungs-aktion verändern. Wichtige Regel: Globale Variablen soviel wie möglich vermeiden.

Mit dieser Hilfsvariablen c führen wir nun die Vertauschung durch: In c merken wir uns den aktuellen Wert von a. Dann schreiben wir in a den Wert von b hinein. Der erste Parameter, der a heißt, hat nun den Wert von b. Dadurch geht zwar der ursprüngliche Wert von a verloren, aber wir haben ihn ja vorher in c zwischengespeichert. Und zum Schluss schreiben wir den Wert von c - den ehemaligen Wert von a - in die Variable b. Der zweite Parameter, der b heißt, hat nun den Wert von a - die Werte sind vertauscht.

Als Code geht das so - bitte versuche, das unbedingt zu verstehen:

Code: Alles auswählen

procedure Vertauschen(var a, b: Integer),
var
  c: Integer;
begin
  c := a;
  a := b;
  b := c;
end;
Wenn die Vertauschen-Prozedur verlassen wird, existiert die Hilfsvariable c dann nicht mehr. Man kann von nirgendwo mehr auf c zurückgreifen, aber das ist der Sinn von lokalen Variablen

Wie man das nun anwendet erläutere ich an deiner Prozedur Minus:

Code: Alles auswählen

procedure Minus;
begin
  x := random(100);
  y := random(100);
  if x < y then
    Vertauschen(x, y);
  write(x,' - ',y,' = ');
  readln(z);
  if z = x-y then
    writeln('Richtig gerechnet!')
  else 
    writeln('Leider falsch, richtig ist ',x-y);
end;  
Du ziehst zwei Zufallszahlen, x und y. Wenn x kleiner als y ist, dann werden die beiden vertauscht - x ist nun größer als y. Du gibts die Rechenaufgabe aus, lässt den Benutzer das Ergebnis eingeben und meldest, ob es richtig ist. So soll es sein.

Obwohl: ganz zufrieden bin ich damit nicht. Es ist nämlich so, dass du die Werte x, y und z nirgendwo im Hauptprogramm benötigst. Daher solltest du gleich die oben erwähnte Regel anwenden und sie nicht als globale Variablen deklarieren, sondern lokal in der Minus-Prozedur (und natürlich genauso in den anderen Rechenprozeduren):

Code: Alles auswählen

procedure Minus;
var
  x, y, z: Integer;
begin
  x := random(100);
  y := random(100);
  if x < y then
    Vertauschen(x, y);
  write(x,' - ',y,' = ');
  readln(z);
  if z = x-y then
    writeln('Richtig gerechnet!')
  else 
    writeln('Leider falsch, richtig ist ',x-y);
end;  
Nun brauchst du den "var" Block gleich zu Beginn des Programms nicht mehr!

Bei der Eingabeprozedur ist es genauso. Die Variablen "rechenart" und "weiter" werden nur hier verwendet und sollten nicht global, sondern nur lokal existieren - jede Variable zuviel ist ein möglicher schwer zu findender Fehler!

Code: Alles auswählen

procedure Eingabe;
var
  rechenart: Integer;
  weiter: char;
begin
  repeat
    rechenart := random(4);
    case rechenart of
      0: plus;
      1: minus;
      2: mal;
      3: geteilt;
    end;
    writeln;
    write('weiter j/n : ');
    read(weiter);
    writeln;
  until weiter = 'n';
end;
Wie schon gesagt, solltest du Randomize nur 1x zu Beginn des Programms aufrufen. Das Hauptprogramm sieht nun so aus:

Code: Alles auswählen

program RechenQuiz;
uses crt;

... (hier alle Rechen-Prozeduren, sowie die Eingabe-Prozedur)

begin
  Randomize;
  ClearScr;
  Writeln('Das ist ein Rechenquizprogramm');
  Writeln('------------------------------');
  Writeln;
  Eingabe;
  Readln;
end.  

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1432
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Kleines Rechenquizprogramm

Beitrag von fliegermichl »

und was soll an 4 - 5 = -1 falsch sein?

siro
Beiträge: 730
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Kleines Rechenquizprogramm

Beitrag von siro »

!!! random(10) ergibt Werte zwischen 0..9
Das heisst bei einer Division kann das dann schief gehen.
Durch 0 teilen erzeugt einen Laufzeitfehler.

Eigentlich wäre wohl random(10)+1 bzw. random(100)+1 eher sinnvoll, dann bekommst Du Werte zwischen 1 und 10 bzw. zwischen 1 und 100.
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

engeldimo123
Beiträge: 4
Registriert: Di 28. Sep 2021, 15:47

Re: Kleines Rechenquizprogramm

Beitrag von engeldimo123 »

Hallo, habe mein kleines Rechenquiz nochmal überarbeitet und so
müsste es jetzt gut funktionieren.

Code: Alles auswählen

program rechenquiz;
uses crt;
var rechenart : integer;
    weiter : char;

procedure vertauschen(var a,b : integer);
var zwischenwert : integer;
begin
zwischenwert := a;
a := b;
b := zwischenwert;
end;

// Addition
procedure plus;
var x,y,z : integer;
begin
x := random(100)+1;
y := random(100)+1;
write(x,' + ',y,' = ');
readln(z);
if z = x+y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x+y);
end;

// Subtraktion
procedure minus;
var x,y,z : integer;
begin
x := random(100)+1;
y := random(100)+1;
if x < y then
vertauschen(x,y);
write(x,' - ',y,' = ');
readln(z);
if z = x-y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x-y);
end;

// Multiplikation
procedure mal;
var x,y,z : integer;
begin
x := random(100)+1;
y := random(10)+1;
if x < y then
write(x,' * ',y,' = ');
readln(z);
if z = x*y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x*y);
end;

// Division
procedure geteilt;
var x,y,z : integer;
begin
x := random(100)+1;
y := random(10)+1;
if x < y then
vertauschen(x,y);
write(x,' / ',y,' = ');
readln(z);
if z = x div y then
writeln('Richtig gerechnet!')
else writeln('Leider falsch, richtig ist ',x div y);
end;

procedure eingabe;
begin

repeat

rechenart := random(4);
case rechenart of
0: plus;
1: minus;
2: mal;
3: geteilt;
end;

writeln;
write('weiter j/n : ');
read(weiter);
writeln;

until weiter = 'n';

end;

begin
clrscr;

writeln('Dies ist ein kleines Rechenquiz mit den 4 Grundrechenarten');
writeln('-> Gib bei der Division nur die ganze Zahl ohne Rest ein');
writeln('----------------------------------------------------------');
writeln;
randomize;
eingabe;

readln;
end.


Grüße
Dietmar Erler

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Kleines Rechenquizprogramm

Beitrag von wp_xyz »

Ich hab's nicht getestet, sieht beim Drüberscrollen aber gut aus. Bis auf die unnötigen globalen variablen rechnenart und weiter - diese werden nur im Inneren der Prozedur Eingabe() benötigt, können also dort lokal deklariert werden.

Thandor
Beiträge: 153
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 3.0 mit FPC 3.2.2 (32Bit + 64bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Kleines Rechenquizprogramm

Beitrag von Thandor »

Du solltest dir nochmal die "Procedure mal" genauer ansehen. Da wird die Rechenaufgabe nur dann ausgegeben, wenn x < y aber das Ergebnis wird dennoch in jedem Fall abgefragt. Dass erscheint mir falsch. Ich denke mal, dass das "if x < y then" ein Copy-Paste Überbleibsel ist.

Wenn mann jetzt noch ein wenig "optimieren" möchte, könnte mann sich überlegen, die Rechenarten mit nur einer procedure abzubilden. Die Proceduren schauen doch alle sehr ähnlich aus...

Antworten