/**
* =====================================================================
* MUSTERLOESUNG: Die Grundrechenarten in JavaScript
* =====================================================================
*
* Diese Datei ist identisch mit "grundrechenarten.js", enthaelt aber
* ausformulierte Musterkommentare an allen 10 Aufgabenstellen.
* Nur fuer die Lehrperson bestimmt.
*
* Bewertungshinweis: Erwartet wird sinngemaesse Uebereinstimmung, nicht
* Wortgleichheit. Pro Aufgabe genuegen die fett markierten Kernpunkte.
* =====================================================================
*/
"use strict";
// =====================================================================
// TEIL 1 - Zwei Zahlen, vier Rechenarten
// =====================================================================
const a = 12;
const b = 5;
// --- AUFGABE 1 -------------------------------------------------------
// TODO: Erklaere, was die beiden Zeilen oben bewirken. Gehe dabei auf
// das Schluesselwort "const" ein.
// Antwort:
// Es werden zwei Variablen a und b deklariert und mit den Werten 12
// bzw. 5 initialisiert. "const" erzeugt eine Konstante: Der Name darf
// nach der Zuweisung nicht mehr auf einen anderen Wert zeigen - ein
// spaeteres "a = 20;" fuehrt zu einem TypeError. Mit "let" deklarierte
// Variablen sind dagegen jederzeit neu zuweisbar. Faustregel: immer
// const verwenden, let nur dort, wo sich der Wert wirklich aendern muss
// (z. B. der Zaehler einer Schleife).
// Kernpunkte: Deklaration + Initialisierung / const = nicht neu
// zuweisbar / Abgrenzung zu let.
function addieren(x, y) {
return x + y;
}
function subtrahieren(x, y) {
return x - y;
}
function multiplizieren(x, y) {
return x * y;
}
// --- AUFGABE 2 -------------------------------------------------------
// TODO: Beschreibe den Aufbau einer Funktion am Beispiel "addieren".
// Antwort:
// "function" leitet die Definition ein, danach folgt der Funktionsname
// "addieren". In den runden Klammern stehen die Parameter x und y -
// Platzhalter fuer die Werte, die beim Aufruf uebergeben werden
// (Argumente). Beim Aufruf addieren(12, 5) wird x zu 12 und y zu 5.
// Zwischen den geschweiften Klammern steht der Funktionsrumpf.
// "return" beendet die Funktion sofort und gibt den berechneten Wert an
// die aufrufende Stelle zurueck, wo er weiterverwendet werden kann.
// Ohne return liefert eine Funktion "undefined".
// Kernpunkte: Parameter sind Platzhalter / return gibt Wert zurueck und
// beendet die Funktion.
function dividieren(x, y) {
if (y === 0) {
return "Division durch 0 ist nicht definiert";
}
return x / y;
}
// --- AUFGABE 3 -------------------------------------------------------
// TODO: Warum enthaelt "dividieren" eine zusaetzliche if-Abfrage?
// Antwort:
// Die Division durch null ist mathematisch nicht definiert; Addition,
// Subtraktion und Multiplikation haben keinen vergleichbaren
// Sonderfall. JavaScript wirft dabei aber keinen Fehler, sondern
// liefert den speziellen Wert "Infinity" (bzw. -Infinity, und NaN bei
// 0 / 0). Das ist tueckisch, weil das Programm scheinbar normal
// weiterlaeuft und der unsinnige Wert erst spaeter auffaellt. Die
// if-Abfrage faengt den Fall ab und gibt eine verstaendliche Meldung
// zurueck.
// Kernpunkte: 12 / 0 ergibt Infinity, keinen Fehler / Abfangen macht
// den Fehler sichtbar.
console.log("=== TEIL 1: Grundrechenarten ===");
console.log(`${a} + ${b} = ${addieren(a, b)}`);
console.log(`${a} - ${b} = ${subtrahieren(a, b)}`);
console.log(`${a} * ${b} = ${multiplizieren(a, b)}`);
console.log(`${a} / ${b} = ${dividieren(a, b)}`);
console.log(`${a} / 0 = ${dividieren(a, 0)}`);
// --- AUFGABE 4 -------------------------------------------------------
// TODO: Wie heisst die Schreibweise mit Backticks und ${...}?
// Antwort:
// Das sind Template Literals (Vorlagenzeichenketten). Zwischen den
// Backticks darf beliebiger Text stehen, und in ${...} wird ein
// JavaScript-Ausdruck ausgewertet und sein Ergebnis an dieser Stelle
// eingesetzt - auch ein Funktionsaufruf wie ${addieren(a, b)}.
// Vorteile gegenueber der Verkettung mit +: deutlich besser lesbar,
// weniger Anfuehrungszeichen und Pluszeichen, kein vergessenes
// Leerzeichen, und mehrzeilige Zeichenketten sind direkt moeglich.
// Kernpunkte: Template Literal / Ausdruck in ${} wird eingesetzt /
// Lesbarkeit.
// =====================================================================
// TEIL 2 - Ganzzahldivision und Rest
// =====================================================================
const zaehler = 17;
const nenner = 5;
const ganzeTeile = Math.floor(zaehler / nenner);
const rest = zaehler % nenner;
console.log("\n=== TEIL 2: Division mit Rest ===");
console.log(`${zaehler} : ${nenner} = ${ganzeTeile} Rest ${rest}`);
// --- AUFGABE 5 -------------------------------------------------------
// TODO: Was macht Math.floor(), was macht %?
// Antwort:
// a) Math.floor() rundet eine Zahl immer ab, also zur naechstkleineren
// ganzen Zahl: 17 / 5 ergibt 3.4, Math.floor(3.4) ergibt 3. So
// erhaelt man aus einer normalen Division die Ganzzahldivision.
// (Achtung bei negativen Zahlen: Math.floor(-3.4) ist -4.)
// b) Der Modulo-Operator % liefert den Rest einer Ganzzahldivision:
// 17 % 5 ist 2, denn 5 passt dreimal in 17 und es bleiben 2 uebrig.
// Praktische Beispiele: Pruefen, ob eine Zahl gerade ist (n % 2);
// jede dritte Tabellenzeile einfaerben (i % 3 === 0); Sekunden in
// Minuten und Sekunden umrechnen; zyklisch durch eine Liste laufen
// (index % laenge).
// Kernpunkte: floor = abrunden / % = Rest / ein sinnvolles Beispiel.
const zahl = 42;
const istGerade = zahl % 2 === 0;
console.log(`Ist ${zahl} gerade? ${istGerade}`);
// --- AUFGABE 6 -------------------------------------------------------
// TODO: Unterschied zwischen "=" und "==="? Datentyp von istGerade?
// Antwort:
// "=" ist der Zuweisungsoperator: Der Wert rechts wird in die Variable
// links geschrieben. "===" ist der strikte Vergleichsoperator: Er
// prueft, ob zwei Werte gleich sind, UND ob sie denselben Datentyp
// haben, und liefert true oder false. ("==" vergleicht ebenfalls, wandelt
// die Typen aber vorher um - "5" == 5 ist true, "5" === 5 ist false;
// deshalb verwendet man in der Praxis ===.)
// Ausgewertet wird zuerst zahl % 2 (ergibt 0), dann der Vergleich
// 0 === 0 (ergibt true), und dieses Ergebnis wird zugewiesen.
// istGerade hat also den Datentyp boolean.
// Kernpunkte: Zuweisung vs. Vergleich / === prueft auch den Typ /
// boolean.
// =====================================================================
// TEIL 3 - Rangfolge der Operatoren (Punkt vor Strich)
// =====================================================================
const ohneKlammern = 2 + 3 * 4;
const mitKlammern = (2 + 3) * 4;
console.log("\n=== TEIL 3: Rangfolge ===");
console.log(`2 + 3 * 4 = ${ohneKlammern}`);
console.log(`(2 + 3) * 4 = ${mitKlammern}`);
// --- AUFGABE 7 -------------------------------------------------------
// TODO: Warum liefern beide Zeilen verschiedene Ergebnisse?
// Antwort:
// JavaScript haelt sich an dieselbe Rangfolge wie die Mathematik:
// Punktrechnung vor Strichrechnung. In der ersten Zeile wird deshalb
// zuerst 3 * 4 = 12 berechnet und dann 2 + 12 = 14. Klammern haben die
// hoechste Prioritaet und heben diese Reihenfolge auf: In der zweiten
// Zeile wird zuerst 2 + 3 = 5 gerechnet und dann 5 * 4 = 20.
// Merke: Wenn eine Rechnung laenger wird, setzt man Klammern lieber
// einmal zu viel als zu wenig - das kostet nichts und macht die Absicht
// eindeutig.
// Kernpunkte: 14 vs. 20 / Punkt vor Strich / Klammern haben Vorrang.
// =====================================================================
// TEIL 4 - Wenn Kommazahlen ungenau werden
// =====================================================================
const summe = 0.1 + 0.2;
console.log("\n=== TEIL 4: Kommazahlen ===");
console.log(`0.1 + 0.2 = ${summe}`);
console.log(`Ist 0.1 + 0.2 === 0.3 ? ${summe === 0.3}`);
console.log(`Gerundet auf 2 Stellen: ${summe.toFixed(2)}`);
// --- AUFGABE 8 -------------------------------------------------------
// TODO: Warum ueberrascht das Ergebnis von 0.1 + 0.2?
// Antwort:
// Die Konsole zeigt 0.30000000000000004, und der Vergleich mit 0.3
// ergibt false. Grund: JavaScript speichert alle Zahlen als binaere
// Gleitkommazahlen (Standard IEEE 754, 64 Bit). Im Zweiersystem lassen
// sich 0.1 und 0.2 nicht exakt darstellen - aehnlich wie sich 1/3 im
// Zehnersystem nicht exakt als endliche Dezimalzahl schreiben laesst.
// Beide Werte werden also minimal gerundet gespeichert, und beim
// Addieren summiert sich der Fehler zu einer sichtbaren Abweichung.
// toFixed(2) rundet auf zwei Nachkommastellen und liefert "0.30".
// Der Haken: Das Ergebnis ist ein String, keine Zahl - damit laesst
// sich nicht weiterrechnen ("0.30" + 1 ergibt "0.301"). toFixed eignet
// sich fuer die Ausgabe, nicht fuer die Rechnung. Fuer Vergleiche nimmt
// man Math.abs(x - y) < 1e-9, fuer Geldbetraege rechnet man in Rappen
// bzw. Cent mit ganzen Zahlen.
// Kernpunkte: 0.30000000000000004 / binaere Gleitkommadarstellung /
// toFixed liefert einen String.
// =====================================================================
// TEIL 5 - Eine Rechnung, viele Male: die Schleife
// =====================================================================
const reihe = 7;
console.log(`\n=== TEIL 5: Einmaleins der ${reihe} ===`);
for (let i = 1; i <= 10; i++) {
console.log(`${reihe} * ${i} = ${multiplizieren(reihe, i)}`);
}
// --- AUFGABE 9 -------------------------------------------------------
// TODO: Zerlege den Kopf der for-Schleife in seine drei Bestandteile.
// Antwort:
// 1. "let i = 1" - Initialisierung: wird genau einmal vor dem ersten
// Durchlauf ausgefuehrt und legt die Zaehlvariable an. Hier "let"
// statt "const", weil sich i in jedem Durchlauf aendert.
// 2. "i <= 10" - Bedingung: wird vor jedem Durchlauf geprueft.
// Solange sie true ergibt, laeuft die Schleife weiter; ergibt sie
// false, bricht sie ab.
// 3. "i++" - Fortschaltung: wird nach jedem Durchlauf
// ausgefuehrt und erhoeht i um 1 (Kurzform fuer i = i + 1).
// Der Rumpf wird 10-mal ausgefuehrt (i = 1 bis einschliesslich 10).
// Mit "i < 10" waeren es nur 9 Durchlaeufe - ein klassischer
// Fehler um eins.
// Kernpunkte: Initialisierung / Bedingung / Fortschaltung / 10-mal.
// =====================================================================
// TEIL 6 - Eingabe von aussen
// =====================================================================
const eingabe = process.argv[2];
const eingabeZahl = Number(eingabe);
console.log("\n=== TEIL 6: Eingabe ===");
if (eingabe === undefined) {
console.log("Keine Zahl uebergeben. Starte z. B. mit: node grundrechenarten.js 8");
} else if (Number.isNaN(eingabeZahl)) {
console.log(`"${eingabe}" ist keine gueltige Zahl.`);
} else {
console.log(`Deine Zahl: ${eingabeZahl}`);
console.log(`Verdoppelt: ${multiplizieren(eingabeZahl, 2)}`);
console.log(`Halbiert: ${dividieren(eingabeZahl, 2)}`);
console.log(`Um 10 vermindert: ${subtrahieren(eingabeZahl, 10)}`);
}
// --- AUFGABE 10 ------------------------------------------------------
// TODO: Warum ist Number(eingabe) noetig?
// Antwort:
// process.argv enthaelt die Kommandozeilenargumente, und zwar immer als
// Strings - "8" ist Text, nicht die Zahl 8. Number() wandelt den Text
// in eine Zahl um; laesst sich der Text nicht deuten, entsteht NaN
// ("not a number"), was mit Number.isNaN() geprueft wird.
// Ohne Umwandlung verhaelt sich JavaScript unterschiedlich:
// "8" + 2 ergibt "82" - der Operator + ist bei einem String die
// Verkettung, die Zahl 2 wird zu "2".
// "8" * 2 ergibt 16 - fuer * gibt es keine String-Bedeutung,
// deshalb wandelt JavaScript automatisch um.
// Genau diese Inkonsistenz macht die explizite Umwandlung noetig:
// Sonst funktionieren drei Rechenarten scheinbar, und die Addition
// liefert stillschweigend Unsinn.
// Kernpunkte: argv liefert Strings / "8" + 2 = "82" / "8" * 2 = 16.
// =====================================================================
// ZUSATZAUFGABE - Musterloesung
// =====================================================================
function potenzieren(basis, exponent) {
// Startwert 1, weil 1 das neutrale Element der Multiplikation ist.
let ergebnis = 1;
// Der Rumpf laeuft "exponent"-mal; jedes Mal wird einmal mit der
// Basis multipliziert. Bei exponent = 0 laeuft er nie, das Ergebnis
// bleibt 1 - genau wie es die Mathematik verlangt.
for (let i = 0; i < exponent; i++) {
ergebnis = ergebnis * basis;
}
return ergebnis;
}
console.log("\n=== ZUSATZ: Potenzieren ===");
console.log(`2 hoch 10 = ${potenzieren(2, 10)}`);
console.log(`5 hoch 0 = ${potenzieren(5, 0)}`);
// Kuerzer geht es mit dem eingebauten Operator ** :
console.log(`Zur Kontrolle mit ** : ${2 ** 10}`);