Taschenrechner in fünf Etappen
Bau dir in einer halben Stunde einen richtigen Taschenrechner — mit Verlauf, Tastatur und exakter Rechnung. Schon nach Etappe 2 rechnet er.
Am Ende dieses Tutorials hast du kein Übungsprogramm, sondern ein Werkzeug, das du wirklich benutzt: ein Taschenrechner mit Tastenfeld, Verlauf der letzten Rechnungen, Tastatur-Bedienung und einer Anzeige, die deutsche Zahlen schreibt.
Es ist derselbe Rechner, der auf der Seite Mit Kiste gebaut steht — kein abgespeckter Übungsklon.
Wir bauen ihn in fünf Etappen. Jede endet mit einem Programm, das startet und etwas tut. Schon nach Etappe 2 rechnet dein Rechner — den Rest baust du an etwas Fertiges an, nicht ins Blaue hinein.
Du brauchst nur Kiste. Falls es noch nicht installiert ist: hier herunterladen, ein Klick, fertig.
Etappe 1 · Ein Fenster mit einer Anzeige
Fang mit dem an, was der Benutzer zuerst sieht. Leg eine Datei taschenrechner.ki an.
Die Anzeige ist keine normale Textzeile, sondern eine Leinwand — eine Fläche, auf die das Programm selbst malt. Der Grund: gleich soll dort eine große Zahl rechtsbündig stehen und darüber klein die laufende Rechnung. Das kann kein Standard-Etikett, aber selber malen ist einfacher als es klingt.
// Etappe 1 — ein Fenster mit einer Anzeige.
nutze gui
fest A_BREITE = 336
fest A_HOEHE = 96
fest FELD = "#0f1318"
fest HELL = "#f2f4f7"
nimm anzeige = "0"
nimm feld = gui.leinwand(A_BREITE, A_HOEHE)
funktion zeichne_anzeige() {
gui.leeren(feld)
gui.rechteck(feld, 0, 0, A_BREITE, A_HOEHE, FELD)
nimm breite = länge(anzeige) * 19
gui.text_an(feld, A_BREITE - 16 - breite, 44, anzeige, HELL, 34)
}
gui.thema("dunkel")
zeichne_anzeige()
nimm fenster = gui.fenster_öffne("Kiste — Taschenrechner", 344, 104)
gui.zeige(fenster, feld)
gui.starte()
Bauen und starten — Programme mit Fenster laufen immer über kiste build:
kiste build taschenrechner.ki
taschenrechner.exe
Du siehst ein dunkles Fenster mit einer 0 am rechten Rand.
Der Rechtsbündig-Trick: gui.text_an setzt Text an einer linken Position. Damit die
Zahl rechts klebt, schätzen wir ihre Breite (länge * 19 bei Schriftgröße 34) und ziehen
sie vom rechten Rand ab. Nicht auf den Pixel genau, aber für Ziffern völlig ausreichend.
Warum fest statt nimm? fest ist ein Wert, der sich nie ändert. Schreibst du
später versehentlich A_BREITE = 400, sagt Kiste dir das, statt still etwas kaputtzumachen.
Etappe 2 · Es rechnet
Jetzt kommen Tasten dazu — und damit der Moment, in dem aus dem Fenster ein Rechner wird.
Ein Rechner muss sich vier Dinge merken:
nimm anzeige = "0" // was gerade groß dasteht
nimm gespeichert = 0.0 // die Zahl von vor dem Rechenzeichen
nimm operator = "" // welches Zeichen gedrückt wurde
nimm neue_zahl = wahr // fängt die nächste Ziffer eine neue Zahl an?
neue_zahl ist der unscheinbarste, aber wichtigste Merker. Tippst du 12, sollen die
Ziffern aneinanderhängen. Tippst du 12 + 3, muss die 3 eine neue Zahl beginnen und
darf nicht 123 ergeben. Genau das schaltet dieser Merker um.
Die Ziffern
funktion tippe(z) {
wenn neue_zahl {
anzeige = z
neue_zahl = falsch
} sonst {
wenn anzeige == "0" {
anzeige = z
} sonst {
anzeige = anzeige + z
}
}
zeichne_anzeige()
}
Das Rechnen
anwenden führt die vorgemerkte Rechnung aus. Beachte den Zahlentyp: wir wandeln die
Anzeige mit als_dezimal um, nicht mit als_komma. Dazu gleich mehr.
funktion anwenden() {
nimm jetzt = als_dezimal(anzeige)
wenn operator == "+" {
gespeichert = gespeichert + jetzt
}
wenn operator == "-" {
gespeichert = gespeichert - jetzt
}
wenn operator == "×" {
gespeichert = gespeichert * jetzt
}
wenn operator == "÷" {
gespeichert = gespeichert / jetzt
}
anzeige = "{gespeichert}"
}
funktion rechne_weiter(op) {
wenn operator == "" {
gespeichert = als_dezimal(anzeige)
} sonst {
wenn nicht neue_zahl {
anwenden()
}
}
operator = op
neue_zahl = wahr
zeichne_anzeige()
}
funktion gleich() {
wenn operator != "" {
anwenden()
operator = ""
neue_zahl = wahr
zeichne_anzeige()
}
}
Der Kniff in rechne_weiter: Drückst du 2 + 3 + 4, soll das zweite + erst 2 + 3
ausrechnen und mit dem Ergebnis weitermachen. Genau das macht der sonst-Zweig — er
kettet Rechnungen, ohne dass du jedes Mal = drücken musst.
Die Tasten anlegen und verdrahten
Jede Taste entsteht in zwei Schritten: erst der Knopf, dann was er tun soll.
nimm k7 = gui.knopf_neu("7")
gui.bei_klick(k7, funktion(k) { tippe("7") })
Das wiederholst du für die Ziffern 0 bis 9. Für die Rechenzeichen dasselbe Muster, nur mit
rechne_weiter:
nimm k_mal = gui.knopf_neu("×")
gui.bei_klick(k_mal, funktion(k) { rechne_weiter("×") })
nimm k_gleich = gui.knopf_neu("=")
gui.bei_klick(k_gleich, funktion(k) { gleich() })
Die Tasten anordnen
Positionen ausrechnen musst du nicht. Du sagst gui.raster, wie viele Spalten du willst,
und gibst die Tasten in Lesereihenfolge:
// Die letzte Reihe ist noch nicht voll — Komma, Löschen und Prozent
// kommen in Etappe 3 dazu.
nimm gitter = gui.raster(4, [
k7, k8, k9, k_div,
k4, k5, k6, k_mal,
k1, k2, k3, k_minus,
k0, k_gleich, k_plus
])
Zum Schluss die Anzeige über das Tastenfeld stapeln und das Fenster größer machen. Die letzten Zeilen aus Etappe 1 ersetzt du dabei — das Fenster zeigt jetzt nicht mehr die Leinwand allein, sondern beides übereinander:
nimm inhalt = gui.vertikal([feld, gitter])
nimm fenster = gui.fenster_öffne("Kiste — Taschenrechner", 344, 300)
gui.zeige(fenster, inhalt)
gui.starte()
Lässt du die alten Zeilen versehentlich stehen, öffnen sich beim Start zwei Fenster — dann weißt du, woran es liegt.
Neu bauen, starten, 7 × 8 = klicken: 56. Du hast einen funktionierenden Rechner.
Warum dezimal und nicht komma?
Tipp in fast jeder anderen Programmiersprache 0.1 + 0.2 ein. Das Ergebnis ist nicht 0,3,
sondern 0,30000000000000004. Das ist kein Fehler dieser Sprachen, sondern die Natur von
Gleitkommazahlen: Sie rechnen im Binärsystem und können ein Zehntel gar nicht exakt
darstellen — so wie du ein Drittel nicht exakt als Dezimalzahl schreiben kannst.
Kiste hat für genau diesen Fall einen eigenen Zahlentyp. dezimal rechnet in
Dezimalstellen und ist exakt. Deshalb steht in deinem Rechner nach 0,1 + 0,2 wirklich
0,3. Für Geld gilt dasselbe — und ein Rechner ist Geld sehr ähnlich.
Etappe 3 · Die fehlenden Tasten, und nichts stürzt mehr ab
Jetzt kommen C, ±, %, das Komma und die Rücktaste dazu. Alle nach demselben Muster
wie bisher. Interessant sind zwei Stellen.
Vorher eine Zeile ganz oben: Ab hier arbeiten wir mit Text — nachsehen, ob schon ein
Komma drinsteht, das letzte Zeichen abschneiden. Diese Funktionen stecken im Modul text,
und Module holst du dir, wenn du sie brauchst. Ergänze also unter nutze gui:
nutze gui
nutze text
Vergisst du das, sagt Kiste dir beim Start, dass es text nicht kennt.
Erstens: Teilen durch null. Kiste bricht bei einer Teilung durch null mit einem Fehler
ab — richtig so, denn stillschweigend „unendlich" zu liefern würde nur Programmierfehler
verstecken. Ein Taschenrechner darf deswegen aber nicht abstürzen, wenn jemand 5 ÷ 0
tippt. Also fragen wir vorher und merken uns einen Fehlerzustand:
nimm fehler = falsch
// … in anwenden():
wenn operator == "÷" {
wenn jetzt == 0 {
fehler = wahr
} sonst {
gespeichert = gespeichert / jetzt
}
}
Die Anzeige zeigt dann statt der Zahl einen Text:
nimm gross = anzeige
wenn fehler {
gross = "Nicht definiert"
}
Und jede Taste, die eine neue Eingabe beginnt, räumt den Fehler weg:
funktion tippe(z) {
wenn fehler {
alles_löschen()
}
// … wie bisher
}
Zweitens: das Komma darf nur einmal vorkommen. 3,,14 wäre keine Zahl. Eine Zeile
verhindert es:
funktion komma() {
wenn neue_zahl {
anzeige = "0."
neue_zahl = falsch
} sonst {
wenn nicht text.enthält(anzeige, ".") {
anzeige = anzeige + "."
}
}
zeichne_anzeige()
}
Beachte: intern schreiben wir den Punkt — das ist die Schreibweise, die als_dezimal
versteht. Zum Komma machen wir es erst beim Anzeigen, in der nächsten Etappe.
Die Rücktaste schneidet das letzte Zeichen ab, das Vorzeichen hängt ein Minus davor oder nimmt es weg, und Prozent teilt durch 100. Das Tastenfeld hat jetzt fünf volle Reihen:
nimm gitter = gui.raster(4, [
k_c, k_vz, k_proz, k_div,
k7, k8, k9, k_mal,
k4, k5, k6, k_minus,
k1, k2, k3, k_plus,
k_rueck, k0, k_komma, k_gleich
])
Etappe 4 · Eigenes Aussehen und deutsche Zahlen
Der Rechner funktioniert — jetzt soll er auch nach etwas aussehen.
Farben. Jeder Knopf kann seine eigene Hintergrundfarbe bekommen. Weil sich das zwanzigmal wiederholt, wandert das Anlegen in eine kleine Funktion, die den fertigen Knopf zurückgibt:
fest ZIFFER = "#2b323c"
fest FUNKTION = "#232932"
fest OPERATOR = "#4a3a18"
fest GLEICH = "#a06c0f"
funktion taste(beschriftung, farbe) {
nimm k = gui.knopf_neu(beschriftung)
gui.setze_knopf_farbe(k, farbe)
gib k
}
nimm k7 = taste("7", ZIFFER)
gui.bei_klick(k7, funktion(k) { tippe("7") })
Aus zwei Zeilen pro Taste werden so wieder zwei — nur sieht sie jetzt aus, wie du willst.
Die Rechenzeichen bekommen einen warmen Ton, das = den kräftigsten: So sieht man auf
einen Blick, was Ziffer ist und was rechnet.
Deutsche Zahlen. 8680 liest sich schlecht, 8 680 gut. Die Regel: nach jeder dritten
Stelle von rechts ein Leerzeichen. Also gehen wir die Zahl Zeichen für Zeichen durch und
zählen, wie viele noch folgen:
funktion gruppiert(t) {
nimm aus = ""
nimm n = länge(t)
nimm i = 0
solange i < n {
aus = aus + text.zeichen_bei(t, i)
nimm rest = n - i - 1
wenn rest > 0 und rest % 3 == 0 {
aus = aus + " "
}
i = i + 1
}
gib aus
}
Und der Punkt wird zum Komma. Dafür zerlegen wir die Zahl an ihrem Punkt: vorne gruppieren, hinten unverändert lassen.
funktion hübsch(t) {
nimm stücke = text.zerlegt(t, ".")
wenn länge(stücke) == 2 {
gib gruppiert(stücke[0]) + "," + stücke[1]
}
gib gruppiert(t)
}
Die kleine Zeile. Über der großen Zahl steht ab jetzt, was gerade gerechnet wird —
1 240 × 7 =. Dafür merkt sich das Programm einen zweiten Text neben und malt ihn kleiner
und grauer über die Zahl.
Etappe 5 · Verlauf und Tastatur
Zwei Dinge fehlen zum fertigen Programm.
Der Verlauf ist eine zweite Leinwand rechts neben dem Tastenfeld. Sie zeigt die letzten sechs Rechnungen. Statt einer wachsenden Liste nehmen wir sechs feste Plätze und schieben bei jeder neuen Rechnung alles um einen Platz nach hinten:
nimm v_rechnung = ["", "", "", "", "", ""]
nimm v_ergebnis = ["", "", "", "", "", ""]
funktion merke(r, e) {
nimm a = v_rechnung
nimm b = v_ergebnis
nimm i = 5
solange i > 0 {
a[i] = a[i - 1]
b[i] = b[i - 1]
i = i - 1
}
a[0] = r
b[0] = e
zeichne_verlauf()
}
Nebeneinander gestellt wird beides mit gui.horizontal:
nimm links = gui.vertikal([feld, gitter])
nimm inhalt = gui.horizontal([links, verlauf])
Die Tastatur ist der letzte Schliff — und fast geschenkt, weil die ganze Bedienlogik schon in Funktionen steckt. Der Tasten-Handler ruft einfach dieselben Funktionen auf wie die Knöpfe:
gui.bei_taste(fenster, funktion(f) {
nimm t = gui.letzte_taste(f)
wenn text.enthält("0123456789", t) {
tippe(t)
}
wenn t == "," oder t == "." {
komma()
}
wenn t == "+" {
rechne_weiter("+")
}
wenn t == "=" oder t == "eingabe" {
gleich()
}
wenn t == "escape" {
alles_löschen()
}
wenn t == "rücktaste" {
rücktaste()
}
})
Weil beide Wege dieselben Funktionen benutzen, können sie gar nicht auseinanderlaufen. Was du an einer Taste änderst, gilt sofort für Maus und Tastatur.
Fenstergröße: Setz sie genau auf den Inhalt — 524 × 304. Kiste verteilt überschüssigen
Platz nicht, er bliebe sonst als toter Rand unten rechts stehen.
Fertig — und jetzt?
Das ist der ganze Rechner: eine einzige Datei, keine Zusatzdateien, kein Framework.
Den kompletten Quelltext kannst du hier herunterladen — vergleich ihn ruhig mit deinem, wenn irgendwo etwas klemmt.
Drei Ideen zum Weiterbauen, von leicht nach schwer:
- Eine Wurzeltaste.
mathe.wurzelgibt es schon — du musst sie nur an einen Knopf hängen. Wo passt sie ins Raster? - Ein Speicher (M+ / MR). Eine Variable mehr, zwei Tasten mehr — und dein Rechner kann sich eine Zahl merken.
- Der Verlauf zum Anklicken. Ein Klick auf eine alte Rechnung holt ihr Ergebnis zurück
in die Anzeige. Dafür brauchst du
gui.bei_maus_klickauf der Verlaufs-Leinwand und ein bisschen Rechnen, welche Zeile getroffen wurde.