So gebaut
Ein Taschenrechner klingt nach einer Fingerübung, ist aber ein gutes Lehrstück: ein wenig Zustand, ein Raster voller Knöpfe, eine selbstgezeichnete Anzeige — und eine Entscheidung über Zahlen, die viele Sprachen falsch treffen.
1 · Warum 0,1 + 0,2 hier wirklich 0,3 ergibt
In den meisten Programmiersprachen ergibt 0.1 + 0.2 nicht 0,3, sondern
0,30000000000000004. Das liegt an Gleitkommazahlen, die im Binärsystem rechnen und
Zehntel gar nicht exakt darstellen können. Kiste hat dafür einen eigenen Zahlentyp:
dezimal rechnet exakt in Dezimalstellen — genau richtig für Geld und für
Rechner.
// Gerechnet wird mit "dezimal" — exakt, nicht mit Gleitkomma.
nimm gespeichert = 0.0 // eine Dezimalzahl
funktion anwenden() {
nimm jetzt = als_dezimal(anzeige)
wenn operator == "+" {
gespeichert = gespeichert + jetzt
}
} 2 · Teilen durch null, ohne Absturz
Kiste wirft bei einer Teilung durch null einen Fehler — richtig so, denn stillschweigend „unendlich" zurückzugeben verschleiert nur Programmierfehler. Ein Taschenrechner darf deswegen aber nicht abstürzen. Also fragen wir vorher.
// Teilen durch null wird abgefangen, bevor es passiert —
// das Programm zeigt "Nicht definiert" statt abzustuerzen.
wenn operator == "÷" {
wenn jetzt == 0 {
fehler = wahr
} sonst {
gespeichert = gespeichert / jetzt
}
} 3 · Eine Taste ist drei Zeilen
Jede Taste ist ein echter Knopf mit eigener Farbe. Weil sich das zwanzigmal wiederholt, wandert es in eine kleine Funktion, die den fertigen Knopf zurückgibt.
// Eine Taste bauen: Knopf anlegen, einfaerben, fertig.
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") }) 4 · Das Raster ordnet von selbst
Kein Ausrechnen von Positionen: gui.raster bekommt die Spaltenzahl und die
Tasten in Lesereihenfolge — den Rest macht es allein, auch beim Vergrößern des Fensters.
// Zwanzig Tasten, vier Spalten — das Raster ordnet sie selbst.
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
]) 5 · Die Anzeige ist gezeichnet, nicht gesetzt
Für die große Zahl mit der kleinen Nebenzeile darüber reicht kein normales Textfeld. Also ist die Anzeige eine Zeichenfläche, auf die das Programm selbst malt — Hintergrund, Nebenzeile, Ergebnis. Rechtsbündig wird sie, indem die Textbreite vom rechten Rand abgezogen wird.
// Die Anzeige ist eine kleine Zeichenflaeche. Rechtsbuendig heisst:
// die geschaetzte Textbreite vom rechten Rand abziehen.
funktion zeichne_anzeige() {
gui.leeren(feld)
gui.rechteck(feld, 0, 0, A_BREITE, A_HOEHE, FELD)
nimm gross = hübsch(anzeige)
nimm breite = länge(gross) * 19
gui.text_an(feld, A_BREITE - 16 - breite, 44, gross, HELL, 34)
} 6 · Tausender trennen
„8680" liest sich schlecht, „8 680" gut. Die Regel ist einfach: nach jeder dritten Stelle von rechts ein Leerzeichen — also Zeichen für Zeichen durchgehen und zählen, wie viele noch folgen.
// "1240" → "1 240": nach jeder dritten Stelle von rechts
// kommt ein schmales Leerzeichen.
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
} 7 · Maus und Tastatur, derselbe Code
Die Tastatur ruft genau dieselben Funktionen auf wie die Knöpfe. Deshalb gibt es die
Bedienlogik nur einmal — tippe, gleich,
alles_löschen — und beide Wege können gar nicht auseinanderlaufen.
// Maus UND Tastatur: derselbe Code, egal ob geklickt oder getippt.
gui.bei_taste(fenster, funktion(f) {
nimm t = gui.letzte_taste(f)
wenn text.enthält("0123456789", t) {
tippe(t)
}
wenn t == "=" oder t == "eingabe" {
gleich()
}
wenn t == "escape" {
alles_löschen()
}
}) Das ist der ganze Rechner: eine Datei, keine Zusatzdateien, kein Framework. Bau ihn nach, häng eine Wurzeltaste dran oder lass ihn sich den letzten Wert merken — der Quelltext liegt oben zum Mitnehmen.