Zweite Version des Bruchrechners

Informatikkurs 2. Semester, Humboldt-Oberschule

Kapitel 7

Dr. Bernd Kokavecz


Übungsmaterial

Rechner1

Gerade wurde die Multiplikation zweier Brüche durchgeführt.

Hinweise zum Vorgehen

Die Anforderungsdefinition für den Bruchrechner wurde mit den Schülern gemeinsam erarbeitet. Auch ein Entwurf der grafischen Oberfläche wurde im Unterricht vorgenommen.

Die Schüler haben dann die entsprechenden Klassen des Fachkonzepts festgelegt. Sie haben die Aufgabe, die einzelnen Klassen zu programmieren und mit Hilfe von Testumgebungen zu testen.

Die grafische Oberfläche (letzter Abschnitt im Quelltext) wird vom Lehrer erstellt. Die Schüler haben die Aufgabe, die Oberfläche zu erweitern und zu verbessern.

Weitere Hinweise

Die Methode "Kürzen" ist in keiner Weise mathematisch elegant oder optimiert!

Es wurde auf eine weitgehende Trennung zwische GUI und Fachkonzept Wert gelegt.

Das Ergebnis der Programmierung wird zeigen, dass der Entwurf - bzw. bereits die Anforderungsdefinition - nicht gründlich genug erarbeitet wurde. So wäre es sinnvoll, die Operationen sofort beim Drücken der entsprechenden Tasten auszuführen (ähnl. umgekehrter polnischer Notation). Das Ergebnis des Kürzens sollte den zu kürzenden Bruch überschreiben usw... (Aus Fehlern lernen hoffentlich auch die Schüler).

Anforderungsdefinition

(Schülertext, Moritz Schindler)

Es soll ein Taschenrechner für Bruchrechnungen programmiert werden. Seine Rechenmöglichkeiten müssen das Addieren, Subtrahieren, Multiplizieren und Dividieren zweier Brüche abdecken. Die Eingabe wie auch die Ausgabe soll in Form von Brüchen erfolgen. Das Ergebnis muss vollsständig gekürzt ausgegeben werden. Eine weitere Funktion des Rechners soll darin bestehen, einen angegebenen Bruch so weit wie möglich zu kürzen oder ihn mit einem Faktor zu erweitern. Eine Darstellung der Zwischenschritte ist nicht erforderlich, ebenso die Fähigkeit des Rückwärtsrechnens. Als Interface wird ein Grafik-Front-End gewünscht, die Zahleneingabe soll per Tastatur erfolgen. Das Frontend soll vollständig vom Kernprogramm getrennt sein.

Entwurf des Interfaces:

entwurf

Die Bedienung des Rechners erfolgt in folgender Weise:

  1. Um eine der Grundrechenarten zwischen den zwei Brüchen zu wählen:
    Es werden die beiden Brüche eingegeben indem man mit der Maus das betreffende Feld selektiert und mit der Tastatur den Wert eingibt. Die Rechenart wird über die 4 Buttons zwischen den Brüchen festgelegt. Nachdem die Eingabe erfolgt ist, wird der '='-Button der sich zwischen dem zweiten und dem dritten Bruch befindet gedrückt. Nun erfolgt die Berechnung, das Ergebnis wird vollständig gekürzt angegeben.
  2. Um einen Bruch vollständig zu kürzen
    Der zu kürzende Bruch wird in den Feldern des 1. Bruches eingetragen. Nach dem Drücken des "Kürzen"- Buttons erfolgt die Ausgabe des Ergebnisses im 3. Bruchfeld.
  3. Um einen Bruch mit einer Zahl zu erweitern
    Der zu erweiternde Bruch wird in den Feldern des ersten Bruches eingegeben, die Zahl mit der zu erweitern ist in das dafür vorgesehene Feld neben dem "Erweitern"-Button. Nach Drücken dieses Buttons erfolgt die Ausgabe des Ergebnisses.
  4. Zurücksetzen der Eingabemaske
    Durch das Drücken des Clear-Buttons werden alle Eingabefelder gelöscht, eine neue Berechnung kann gestartet werden.
  5. Beenden des Programms:
    Es besteht die Möglichkeit, das Programm zu beenden. Die erste Möglichkeit stellt der "Beenden"-Button dar, des weiteren kann das Programm mit dem X-Button rechts oben geschlossen werden.

Aufstellung der benötigten Klassen für das Fachkonzept ("Kernprogramm"):

bild2


Gesamtquelltext für Bruchrechner Version 2
===========================================
----------------------------------------------------------------------------
Datei gzahl.py
----------------------------------------------------------------------------

#! /usr/bin/python

class GZahl:
   """
   /* Klasse stellt eine ganze Zahl bereit
   """
   def __init__(self):
      """
      /* Der Initial-Wert ist 0
      """
      self.Wert=0

   def setWert(self,wert):
      """
      /* Der Wert wird auf wert gesetzt. Ist wert ein REAL, so
      /* werden die Nachkommastellen weggeschnitten, bei anderen
      /* Objekten erfolgt eine Fehlermeldung über StdIO.
      /* ----------------------------------------------------------
      /* wert : integer : Wert der ganzen Zahl
      """
      try:
         self.Wert=int(wert)
      except:
         print "Objekte der Klasse GZahl können nur ganzzahlige Werte annehmen!"

   def getWert(self):
      """
      /* liefert den Wert zurück
      /* -------------------------------------
      /* Rückgabewert : integer
      """
      return self.Wert


if __name__ == "__main__":
   mGZ=GZahl()
   mGZ.setWert("ttt")
   print mGZ.getWert()

----------------------------------------------------------------------
Bruch HAT zwei GanzZahlen (Zähler und Nenner)
Datei: bruch.py
----------------------------------------------------------------------
#! /usr/bin/python

from xterm import * # nur für Testumgebung
from gzahl import *
import string

class Bruch:
   """
   /* Klasse Bruch stellt Zähler und Nenner und erforderliche Methoden bereit.
   """
   def __init__(self):
      """
      /* Zaehler und Nenner werden aus der Klasse GZahl gebildet
      """
      self.zaehler=GZahl()
      self.nenner=GZahl()

   def setZaehler(self,wert):
      """
      /* setzt den Zähler. Ist wert ein REAL, so werden die Stellen nach dem
      /* Komma abgeschnitten, ist wert ein anderes Objekt, so wird eine
      /* Fehlermeldung über StdIO ausgegeben (s. Klasse GZahl)
      /* --------------------------------------------------------------------
      /* wert : integer : Wert für Zähler
      """
      self.zaehler.setWert(wert)

   def setNenner(self,wert):
      """
      /* setzt den Nenner. Ist wert ein REAL, so werden die Stellen nach dem
      /* Komma abgeschnitten, ist wert ein anderes Objekt, so wird eine
      /* Fehlermeldung über StdIO ausgegeben (s. Klasse GZahl)
      /* --------------------------------------------------------------------
      /* wert : integer : Wert für Nenner
      """
      self.nenner.setWert(wert)

   def getZaehler(self):
      """
      /* liefert den Wert des Zählers
      /* --------------------------------------
      /* Rückgabewert : integer
      """
      return self.zaehler.getWert()

   def getNenner(self):
      """
      /* liefert den Wert des Nenners
      /* --------------------------------------
      /* Rückgabewert : integer
      """
      return self.nenner.getWert()

   def invertieren(self):
      """
      /* vertauscht Zähler und Nenner (Kehrbruch)
      """
      zwi=self.zaehler
      self.zaehler=self.nenner
      self.nenner=zwi

   def erweitern_mit(self,n):
      """
      /* Der Bruch wird mit n erweitert
      /* ---------------------------------------------------
      /* n : integer : Wert, mit dem erweitert werden soll
      """
      self.zaehler.setWert(self.zaehler.getWert()*n)
      self.nenner.setWert(self.nenner.getWert()*n)
      
   def kuerzen_durch(self,n):
      """
      /* Der Bruch wird durch n gekürzt
      /* ---------------------------------------------------
      /* n : integer : Wert, durch den gekürzt werden soll
      """
      if (self.zaehler.getWert()%n==0) and (self.nenner.getWert()%n==0):
         self.zaehler.setWert(self.zaehler.getWert()/n)
         self.nenner.setWert(self.nenner.getWert()/n)
         return 1 # true
      else:
         return 0 # false
      
   def kuerzen(self):
      """
      /* primitiver Algorithmus, der soweit wie möglich kürzt
      """
      x=1
      while x:
         N=self.nenner.getWert()
         x=0
         for i in range(2,N+1):
            x = x or self.kuerzen_durch(i)
                                                  

# Testumgebung:

if __name__ == "__main__":
   terminal=XTerm()
   mein_Bruch=Bruch()
   terminal.clear()
   terminal.output("Zähler eingeben: ")
   mein_Bruch.setZaehler(string.atoi(terminal.input()))
   terminal.output("\nNenner eingeben: ")
   mein_Bruch.setNenner(string.atoi(terminal.input()))
   terminal.goto(0,10)
   mein_Bruch.kuerzen()
   terminal.output("\nZähler: "+str(mein_Bruch.getZaehler()))
   terminal.output("\nNenner : "+str(mein_Bruch.getNenner())+"\n")
   mein_Bruch.invertieren()
   terminal.output("\nZähler: "+str(mein_Bruch.getZaehler()))
   terminal.output("\nNenner : "+str(mein_Bruch.getNenner())+"\n")




--------------------------------------------------------------------
Bruchrechner HAT 3 Brüche
Datei bruchrechner.py
--------------------------------------------------------------------
#! /usr/bin/python
 
from xterm import * # für Testumgebung
from bruch import *
import string

class Bruchrechner:
   """
   /* stellet das Fachkonzept für einen Bruchrechner dar
   """
   def __init__(self):
      """
      /* Attribute sind: Bruch1, Bruch2, Ergebnis, Operator (default: +)
      """
      self.bruch1=Bruch()
      self.bruch2=Bruch()
      self.ergebnis=Bruch()
      self.operator='+'

   def setOperator(self,wert):
      """
      /* Der Operator wird auf eine der Operationen + - * : gesetzt
      /* Es erfolgt noch keine Berechnung
      /* ----------------------------------------------------------
      /* wert : character : gewünschte Operation (ohne Fehlerabfragen)
      """
      self.operator=wert

   def getOperator(self):
      """
      /* liefert die ausgewählte Operation zurück
      """
      return self.operator

   def setBruch(self,zaehler,nenner,Nr):
      """
      /* Bruch1 oder Bruch2 können definiert werden. 
      /* --------------------------------------------
      /* zaehler : integer 
      /* nenner  : integer 
      /* Nr      : integer : 1 oder 2  für Bruch1 oder Bruch2
      """
      if Nr == 1 :
          self.bruch1.setZaehler(zaehler)
          self.bruch1.setNenner(nenner)
      else:
          self.bruch2.setZaehler(zaehler)
          self.bruch2.setNenner(nenner)
          
   def getBruch(self,Nr):
      """
      /* liefert den Bruch als Zahlenpaar zurück 
      /* -----------------------------------------------
      /* Nr: integer : 1,2 oder 3 für Ergebnisbruch
      /* -----------------------------------------------
      /* Rückgabe : Paar : (integer,integer)
      """
      if Nr == 1 :
          return self.bruch1.getZaehler(), self.bruch1.getNenner()
      if Nr == 2:
          return self.bruch2.getZaehler(), self.bruch2.getNenner()
      if Nr == 3:
          return self.ergebnis.getZaehler(), self.ergebnis.getNenner()

   def erweitern_mit(self,n):
      """
      /* Bruch1 wird mit n erweitert (monadische Operation)
      /* -----------------------------------------------
      /* n : integer
      """
      self.bruch1.erweitern_mit(n)
      
   def kuerzen(self,Nr):
      """
      /* Bruch1, Bruch2 oder ergebnis werden soweit wie möglich gekürzt
      /* --------------------------------------------------------------
      /* Nr : 1,2 oder 3 für Ergebnisbruch
      """
      if Nr==1:
         self.bruch1.kuerzen()
      if Nr==2:
         self.bruch2.kuerzen()
      if Nr==3:
         self.ergebnis.kuerzen()

   def berechne(self):
      """
      /* Entsprechend der Operations-Vorwahl wird die Berechnung aus den
      /* beiden Brüchen durchgeführt und als Ergebnisbruch gespeichert.
      /* (diadische Operationen: + - * : ). 
      /* Fehlermeldung, wenn Nenner 0 über StdIO
      """
      if (self.bruch2.getNenner()==0) or (self.bruch1.getNenner()==0):
            print "Fehler! DIVISION durch 0!"
      else:
         if self.operator=='+':
            n=self.bruch1.getNenner()
            self.bruch1.erweitern_mit(self.bruch2.getNenner())
            self.bruch2.erweitern_mit(n)
            self.ergebnis.setZaehler \
                     (self.bruch1.getZaehler()+self.bruch2.getZaehler())
            self.ergebnis.setNenner(self.bruch1.getNenner())
         if self.operator=='-':
            n=self.bruch1.getNenner()
            self.bruch1.erweitern_mit(self.bruch2.getNenner())
            self.bruch2.erweitern_mit(n)
            self.ergebnis.setZaehler \
                     (self.bruch1.getZaehler()-self.bruch2.getZaehler())
            self.ergebnis.setNenner(self.bruch1.getNenner())
         if self.operator=='*':
            self.ergebnis.setZaehler \
                     (self.bruch1.getZaehler()*self.bruch2.getZaehler())
            self.ergebnis.setNenner \
                     (self.bruch1.getNenner()*self.bruch2.getNenner())
         if self.operator==':':
            if self.bruch2.getZaehler()==0:
               print "Fehler! DIVISION durch 0!"
            else:
               self.bruch2.invertieren()
               self.setOperator('*')
               self.berechne()
         self.kuerzen(3)

# Testumgebung:

if __name__ == "__main__":
   terminal=XTerm()
   terminal.clear()
   terminal.output("Zähler1 eingeben: ")
   z1=string.atoi(terminal.input())
   terminal.output("\nNenner1 eingeben: ")
   n1=string.atoi(terminal.input())
   terminal.output("Zähler2 eingeben: ")
   z2=string.atoi(terminal.input())
   terminal.output("\nNenner2 eingeben: ")
   n2=string.atoi(terminal.input())
   terminal.output("Operation + - * : ")
   o=terminal.input()
   terminal.goto(0,10)

   mB=Bruchrechner()
   mB.setOperator(o[0])
   mB.setBruch(z1,n1,1)
   mB.setBruch(z2,n2,2)
   mB.berechne()

   terminal.output("\nErgebnis: "+str(mB.getBruch(3))+"\n")


-----------------------------------------------------------------
Graphische Benutzeroberfläche:     mFenster  KENNT  mRechner
Datei: rechner_GUI.py
-----------------------------------------------------------------
#! /usr/bin/python

import Tkinter
from Tkconstants import *
from bruchrechner import *

class xBruch:
   """
   /* GUI-Darstellung eines Bruches
   """
   def __init__(self,win):
      """
      /* Zwei Eingabefelder werden übereinander angeordnet (Zähler,Nenner).
      /* ------------------------------------------------------------------
      /* win kennzeichnet das Parent-Objekt, in das der Bruch gelegt wird.
      """
      zWert=Tkinter.StringVar()
      self.zaehler=Tkinter.Entry(win,textvariable=zWert, width=5)
      self.zaehler.pack()
      nWert=Tkinter.StringVar()
      self.nenner=Tkinter.Entry(win,textvariable=nWert, width=5)
      self.nenner.pack()

   def getZaehler(self):
      """
      /* liefert den eingegebenen Wert aus dem Zähler-Eingabefeld 
      """
      self.zaehler.selection_range(0,"end") 
      return (self.zaehler.selection_get())
 
   def getNenner(self):
      """
      /* liefert den eingegebenen Wert aus dem Nenner-Eingabefeld 
      """
      self.nenner.selection_range(0,"end") 
      return (self.nenner.selection_get())

   def setZaehler(self,wert):
      """
      /* Vorbesetzung des Zähler-Eingabefeldes mit einem Wert
      """
      self.zaehler.delete(0,"end")
      self.zaehler.insert(0,wert)         

   def setNenner(self,wert):
      """
      /* Vorbesetzung des Nenner-Eingabefeldes mit einem Wert
      """
      self.nenner.delete(0,"end")
      self.nenner.insert(0,wert)         

  
class xTaste:
   """
   /* GUI-Darstellung einer Taste
   """
   def __init__(self, win, Text, Bef, Seite=TOP):
      """
      /* win kennzeichnet das Parent-Objekt, in das die Taste gelegt wird.
      /* Text : string : Beschriftung der Taste
      /* Bef  : Methodenreferenz : Methode, die beim Drücken der Taste 
      /*                           ausgeführt werden soll.
      /* Seite: opt. Parameter, um Tasten auch nebeneinander anordnen zu können
      """
      Knopf=Tkinter.Button(win, text=Text, command=Bef)
      Knopf.pack(side=Seite, padx=5, pady=12) 

class xRechner:
   """
   /* GUI-Darstellung des Rechners
   """
   def __init__(self,Rechner):
      """
      /* Bei der Objekterzeugung wird ein Fenster mit dem Rechner
      /* geöffnet
      /* ---------------------------------------------------------
      /* Rechner: Referenz auf ein Objekt der Klasse Bruchrechner
      """
      self.Rechner=Rechner
      win=Tkinter.Tk()
      Fensterbreite=str(450)
      Fensterhoehe=str(350)
      geometrie=Fensterbreite+"x"+Fensterhoehe+"+0+0"
      win.geometry(geometrie)
      win.title("Bruchrechner")

      Rahmen0=Tkinter.Frame(win, relief=SUNKEN, bd=2)
      Rahmen0.pack(padx=5, pady=9) 
      RahmenX=Tkinter.Frame(win, bd=2)
      RahmenX.pack(padx=5, pady=9) 
      Rahmen1=Tkinter.Frame(Rahmen0, relief=SUNKEN, bd=2)
      Rahmen1.pack(side=LEFT, padx=5, pady=9) 
      Rahmen2=Tkinter.Frame(Rahmen0, relief=SUNKEN, bd=2)
      Rahmen2.pack(side=LEFT, padx=5, pady=9) 
      Rahmen3=Tkinter.Frame(Rahmen0, relief=SUNKEN, bd=2)
      Rahmen3.pack(side=LEFT, padx=5, pady=9) 
      Rahmen4=Tkinter.Frame(Rahmen0, relief=SUNKEN, bd=2)
      Rahmen4.pack(side=LEFT, padx=5, pady=9) 
      Rahmen5=Tkinter.Frame(Rahmen0, relief=SUNKEN, bd=2)
      Rahmen5.pack(side=LEFT, padx=5, pady=9) 

      self.B1=xBruch(Rahmen1)
      self.B2=xBruch(Rahmen3)
      self.B3=xBruch(Rahmen5)

      K0=xTaste(Rahmen4,' = ', self.ist)
      K1=xTaste(Rahmen2,' + ', self.plus)
      K2=xTaste(Rahmen2,' - ', self.minus)
      K3=xTaste(Rahmen2,' * ', self.mal)
      K4=xTaste(Rahmen2,' : ', self.geteilt)
      KY=xTaste(RahmenX,' kürzen ',self.kuerzen, "left")
      KX=xTaste(RahmenX,' E N D E ', "exit", "left")
      
   def ist(self):
      """
      /* Methode, die beim Drücken der = Taste aufgerufen wird
      """
      self.Rechner.setBruch(self.B1.getZaehler(),self.B1.getNenner(),1)
      self.Rechner.setBruch(self.B2.getZaehler(),self.B2.getNenner(),2)
      self.Rechner.berechne()
      self.B3.setZaehler(self.Rechner.getBruch(3)[0])
      self.B3.setNenner(self.Rechner.getBruch(3)[1])

   def plus(self):
      """
      /* Methode, die beim Drücken der + Taste aufgerufen wird
      """
      self.Rechner.setOperator('+')

   def minus(self):
      """
      /* Methode, die beim Drücken der - Taste aufgerufen wird
      """
      self.Rechner.setOperator('-')

   def mal(self):
      """
      /* Methode, die beim Drücken der * Taste aufgerufen wird
      """
      self.Rechner.setOperator('*')

   def geteilt(self):
      """
      /* Methode, die beim Drücken der : Taste aufgerufen wird
      """
      self.Rechner.setOperator(':')

   def kuerzen(self):
      """
      /* Methode, die beim Drücken der Kürzen-Taste aufgerufen wird
      """
      self.Rechner.setBruch(self.B1.getZaehler(),self.B1.getNenner(),1)
      self.Rechner.kuerzen(1)
      self.B3.setZaehler(self.Rechner.getBruch(1)[0])
      self.B3.setNenner(self.Rechner.getBruch(1)[1])
      
if __name__ == "__main__":
   mRechner=Bruchrechner()
   mWindow=xRechner(mRechner)
   Tkinter.mainloop()



python zurück zur Startseite

Dr. Bernd Kokavecz
Datum: 11.03.2001
kokavecz@humboldt.be.schule.de