Schleifen und Kommentare

Wir wollen uns nun eine sinnvollere Aufgabe überlegen:

Aufgabe 1:

Vorgegeben:

int x;

Ziel: Ausgeben, ob x eine Primzahl ist.

Hilfestellung:

for(A; B; C) {
    Anweisungen
}

Schleifen sind dazu da um Anweisungen wiederholt auszuführen.

A, B und C sind Anweisungen, sie sind im Vergleich zu den Semikolons zwischen ihnen optional.

Anweisung A ist eine Initialisierungsanweisung, hier wird meist eine Laufvariable initialisiert, int i = 0 zum Beispiel.

B ist eine Abbruchbedingung, ein Beispiel ist i < 100.

C ist ein für gewöhnlich eine Inkrement/Dekrement-Anweisung i++.

Ein Beispiel für eine Schleife, die alle Zahlen von 1 bis 100 ausgibt:

for(int i = 1; i < 101; i++) {
    System.out.println(i);
}

Der Vorteil von i < 101 gegenüber i <= 100 ist, dass bei < 101 der Wert der Abbruchbedingung minus den Initialisierungswert die Anzahl der Iterationen entspricht: 101 - 1 = 100! Durch einen solchen Stil kann man Fehler vermeiden.

Wichtig ist außerdem noch, dass vor jedem Schleifendurchlauf zuerst überprüft wird, ob die Abbruchbedingung erfüllt ist.

Wenn nicht, wird die Schleife und anschließend die Inkrement/Dekrement Anweisung ausgeführt, dann beginnt das Selbe von vorn.

Viel Erfolg bei der Aufgabe!

Lösung Aufgabe 1:
int x = 55;
boolean prim = true;

for(int i = 2; (i <= x / 2) && prim; i++) {
    if(x % i == 0) {
        prim = false;
        System.out.print(x + " / " + i + " = ");
        System.out.println(x / i);
    }
}

System.out.println(x + " ist eine Primzahl: " + prim);

Bevor wir den Code untersuchen, wollen wir uns die Definition einer Primzahl ansehen:

Eine Zahl, die nur durch sich selbst und Eins ohne Rest und ganzzahlig teilbar ist.

Eine Zahl x lässt sich also am Einfachsten auf diese Definition überprüfen, indem man sie durch alle Zahlen von 2 bis x-1 teilt. Wenn immer ein Rest übrig bleibt, dann handelt es sich um eine Primzahl.

Da das Teilen von x durch ein Zahl z mit z > x/2 und z < x stehts ein Ergebnis zwischen 1 und 2 zur Folge hat, reicht es alle Zahlen von 2 bis x/2 zu überprüfen.

Zeile 4:
for(int i = 2; (i <= x / 2) && prim; i++) {

Wir wollen x durch einige Zahlen ab der 2 teilen, also initialisieren wir i mit dieser Zahl.

Außerdem haben wir soeben festgestellt, dass wir alle Werte bis x/2 überprüfen wollen, daher geben wir als Abbruchbedingung i <= x/2 an; auf den Teil && prim werden wir gleich genauer eingehen.

Da wir x durch jede natürliche Zahl teilen wollen, wählen wir als Inkrement ein klassisches i++.

Zeile 5:
if(x % i == 0) {

Zu gut deutsch: wenn man x durch i restlos teilen kann.

Zeile 6:
prim = false;

Wir haben uns dazu entschieden eine boolsche Variable zu verwenden. Wenn nun nach der Schleife prim false ist, dann wissen wir, dass ein Teiler gefunden wurde und dass x keine Primzahl ist.

Dadurch, dass wir in Zeile 4 && prim überprüfe, bricht die Schleife nach dem ersten gefundenen Teiler ab.

Aufgabe 2:

  1. Erweitere das Programm so, dass der Benutzer eine Zahl eingibt und für jede Zahl bis hin zur eingegebenen wird ermittelt, ob es sich um eine Primzahl handelt.

  2. Kommentiere den Code so, dass eine dritte Person sofort erkennt, was jener für eine Funktion hat.

Mit

Scanner sc = new Scanner(System.in);
int y = sc.nextInt();

können Zahleneingaben abgefragt werden, zusätzlich muss die Anweisung import java.util.Scanner; oberhalb der Klasse stehen:

package serlo;

import java.util.Scanner;

public class Testklasse {

In Java können im Quellcode Kommentare mit // platziert werden:

// Das ist ein Kommentar, der bei der Programmausführung ignoriert wird.

Mehrzeilige Kommentare können mit /* */ platziert werden.

Lösung Aufgabe 2:
package serlo;

import java.util.Scanner;

public class Testklasse {

    // Berechnet für alle Zahlen bis hin zur vom Benutzer eingegebenen, ob es
    // sich hierbei um eine Primzahl handelt.
    public static void main(String[] args) {

        // Scanner um die Benutzereingabe abzufragen
        Scanner sc = new Scanner(System.in);
        // Eingabeaufforderung
        System.out.println("Bitte geben sie eine Zahl ein:");
        // Die Zahl wird eingelesen
        int y = sc.nextInt();
        // Läuft alle Zahlen bis zur eingegebenen durch
        for(int x = 1; x <= y; x++) {
            boolean prim = true;

            // Läuft alle potenziellen Teiler durch
            for(int i = 2; (i <= x / 2) && prim; i++) {
                // überprüft, ob i ein Teiler von x ist
                if(x % i == 0) {
                    prim = false;
                    /* System.out.print(x + " / " + i + " = ");
                    System.out.println(x / i); Mehrzeiliger Kommentar */
                }
            }

            System.out.println(x + " ist eine Primzahl: " + prim);
        }

    }

}

Die Kommentierung in dieser Lösung ist selbstverständlich übertrieben, wichtig beim Kommentieren ist, dass man seinen eigenen Code auch nach einigen Monaten lesen kann.

Vor allem bei komplizierten Konstrukten ist das Kommentieren wichtig, später werden wir noch etwas genau darauf eingehen.

Der Code sollte recht verständlich sein, mit etwas Farbe lässt sich dies sogar noch steigern:

Bei IntelliJ lässt sich die Farbe mit File --> Settings und dann weiter unter Editor --> Colors & Fonts --> Java anpassen. Wer eine andere Version oder eine andere Entwicklungsumgebung verwendet, wird zur Not sicher im Internet fündig.

Aufgabe 3:

Reduziere die Kommentare auf ein sinnvolles Maß und teste das Programm mit der Eingabe 100.000 einmal mit (i <= x / 2) && prim und einmal mit i < x als Abbruchbedingung.

Versuch den Code weiter zu optimieren.

Tipp: Entwicklungsumgebungen haben in der Konsole für gewöhnlich einen roten, quadratischen Stopp-Knopf, um die Codeausführung vorzeitig abzubrechen.

Lösung Aufgabe 3:

Jetzt sollte klar sein, warum es Sinn macht den Code zumindest oberflächlich zu optimieren.

Ein Möglichkeit den Code weiter zu optimieren wäre i <= Math.sqrt(x) && prim als Abbruchbedingung. Math.sqrt() ermittelt die Quadratwurzel einer Zahl, somit werden bei Primzahlen noch weniger Zahlen durchprobiert, ein Testlauf (z.B. bis 300.000) bestätigt die höhere Geschwindigkeit.

Wieviel Kommentare gut sind, muss jeder selbst für sich herausfinden, wir werden später auch noch auf sogenannte JavaDoc-Kommentare eingehen. Hier ein Beispiel für ein akzeptabel kommentiertes Programm:

    // Berechnet für alle Zahlen bis hin zur vom Benutzer eingegebenen,
    // ob es sich hierbei um eine Primzahl handelt.
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        System.out.println("Bitte geben sie eine Zahl ein:");
        int y = sc.nextInt();
        // Läuft alle Zahlen bis zur eingegebenen durch
        for(int x = 1; x <= y; x++) {
            boolean prim = true;

            // Läuft alle potenziellen Teiler durch
            for(int i = 2; i <= Math.sqrt(x) && prim; i++) {
                if(x % i == 0) {
                    prim = false;
                }
            }

            System.out.println(x + " ist eine Primzahl: " + prim);
        }

    }

Andere Schleifentypen:

Neben der for()-Schleife gibt es auch noch die while()- und die do-while()-Schleife:

for(int i = 0; i < 10; i++) {
    System.out.print(i);
}

System.out.println();

int i = 0;
while(i < 10) {
    System.out.print(i);
    i++;
}

System.out.println();

i = 0;
do {
    System.out.print(i);
    i++;
} while(i < 10);

Die do-while Schleife wird meist verwendet, wenn man den Schleifenkörper mindestens einmal ausführen möchte.

Aufgabe 4:

$$\sum_{k=7}^{666} \frac{k}{10}$$

Lass diese Summe mit Hilfe einer Schleife berechnen und gib das Ergebnis aus.

Hier noch ein Link für diejenigen unter euch, denen das Summenzeichen unbekannt ist: Summenzeichen.

Lösung Aufgabe 4:

Zuerst sollte die Summe vereinfacht werden:

$$\sum_{k=7}^{666} \frac{k}{10}$$

$$= \sum_{k=7}^{666} k \frac{1}{10}$$

$$= \frac{1}{10} \sum_{k=7}^{666} k$$

Der Code könnte jetzt in etwa so aussehen:

int summe = 0;

for(int i = 7; i <= 666; i++) {
    summe += i;
}

System.out.println("Die Summe ist: " + (summe * 0.1));

Aufgabe 5:

Schleifen eignen sich ideal dafür um mit Arrays zu arbeiten.

Erziele die folgende Bilschirmausgabe

Montag ist der 1. Tag der Woche.
[...]
Sonntag ist der 7. Tag der Woche.

verwende zur Bildschirmausgabe nur die folgende Zeile: System.out.println(woche[i]);.

Lösung Aufgabe 5:
String[] woche = {  "Montag", "Dienstag", "Mittwoch", "Donnerstag",
                    "Freitag", "Samstag", "Sonntag"};

// komplettes Array durchlaufen
for(int i = 0; i < 7; i++) {
    woche[i] += " ist der " + (i + 1) + ". Tag der Woche.";
    System.out.println(woche[i]);
}

Da wir in den Zeilen unterschiedliche Wochentage brauchen, können wir diese in Zeile 1 und 2 gleich zuweisen.

Wochentag ist der x. Tag der Woche.

Da wir von 0 bis 6 zählen, müssen wir (i + 1) verwenden.
Jeder String ist danach fertig und wenn wir ihn gleich ausgeben, können wir uns eine zweite Schleife sparen.

Kommentieren Kommentare