Basierend auf den Prinzipien von Schreibe Dein Programm! und Die Macht der Abstraktion
â±ïž Dauer: 10 Minuten
Das ist Programmieren: Dem Computer genaue Anweisungen geben, die er versteht.
Das deutsche Lehrbuch Schreibe Dein Programm! von Prof. Dr. Herbert Klaeren und Dr. Michael Sperber (UniversitĂ€t TĂŒbingen) beschreibt es so:
"Programme lassen sich sehr systematisch konstruieren und â teilweise nahezu automatisch â aus der gegebenen Problemstellung ableiten."
Das bedeutet: Programmieren ist kein RĂ€tselraten, sondern ein systematischer Prozess!
Racket ist eine Programmiersprache, die:
Aus Die Macht der Abstraktion:
"Scheme ist eine kleine und leicht erlernbare Programmiersprache, die es erlaubt, die Konzepte der Programmierung zu prÀsentieren, ohne Zeit mit der Konstruktvielfalt anderer Programmiersprachen zu verlieren."
Programmiersprachen verwenden englische Wörter. Das ist gut, weil:
Du möchtest, dass ein Roboter-Koch dir ein Spiegelei macht. Welche Anweisungen sind zu ungenau?
Ordne die Anweisungen: Schreibe "gut" oder "schlecht" fĂŒr jede:
Merke: Computer brauchen GENAUE Anweisungen, genau wie unser Roboter-Koch!
â±ïž Dauer: 15 Minuten
Diese Schreibweise heiĂt PrĂ€fix-Notation (der Operator steht am Anfang/PrĂ€fix). Sie hat Vorteile:
(+ 1 2 3 4 5) statt 1+2+3+4+5(+ 2 3) ; â 5
(- 10 4) ; â 6
(* 5 6) ; â 30
(/ 20 4) ; â 5
So wertest du verschachtelte AusdrĂŒcke aus:
(+ 5 (* 3 2))?Ăbersetze diese mathematischen AusdrĂŒcke in Racket:
| Mathematik | Racket |
|---|---|
| 7 + 8 | (+ 7 8) |
| 10 - 3 | (- 10 3) |
| (5 + 5) Ă 2 | (* (+ 5 5) 2) |
| 100 Ă· (2 + 3) | (/ 100 (+ 2 3)) |
Probiere alle in WeScheme aus!
Was ergibt dieser Ausdruck? Löse ihn Schritt fĂŒr Schritt:
(* (+ 2 3) (- 10 6))
Hinweis: Erst (+ 2 3) = 5, dann (- 10 6) = 4, dann (* 5 4) = ?
(+ 2 3)â±ïž Dauer: 20 Minuten
(abs -5) ; abs = absolute value (Absolutwert) â 5
(max 3 7 2) ; max = maximum (Maximum) â 7
(min 3 7 2) ; min = minimum (Minimum) â 2
(sqr 4) ; sqr = square (Quadrat, 4ÂČ) â 16
(define age 10) ; age = Alter
(define favorite-number 42) ; favorite-number = Lieblingszahl
(+ age 5) ; â 15
(* favorite-number 2) ; â 84
Nach Schreibe Dein Programm! gehst du so vor:
(define (name param) ...)Aufgabe: Schreibe eine Funktion, die Jahre in Tage umrechnet.
; Rechnet die Anzahl der Jahre in Tage um
; years-to-days: number -> number
Eine Zahl rein (Jahre), eine Zahl raus (Tage)
(check-expect (years-to-days 1) 365)
(check-expect (years-to-days 2) 730)
(check-expect (years-to-days 0) 0)
(define (years-to-days years)
...)
(define (years-to-days years)
(* years 365))
Klicke "Run" â Alle Tests sollten grĂŒn sein! â
Aus Schreibe Dein Programm!:
"Schreibe zuerst Tests, dann den Code. Tests helfen dir, das Problem zu verstehen, bevor du es löst."
; Funktion, die 10 addiert:
(define (plus-ten x)
(+ x 10))
; Funktion, die das Quadrat berechnet:
(define (my-square x)
(* x x))
; Funktion mit zwei Parametern:
(define (rectangle-area width height)
(* width height))
(my-square 5) zurĂŒck?(define triple x (* x 3))(define (triple x) (* x 3))(define triple (x) (* x 3))Diese Funktion soll eine Zahl verdreifachen und dann 1 abziehen:
(define (mystery x)
(- (* x ?) ?))
Ersetze die ? so, dass (mystery 5) das Ergebnis 14 ergibt.
Hinweis: 5 Ă ? = 15, dann 15 - ? = 14
Diese Funktion soll den Durchschnitt von zwei Zahlen berechnen:
(define (average a b)
(? (? a b) 2))
(average 4 6) soll 5 ergeben. (average 10 20) soll 15 ergeben.
Hinweis: Durchschnitt = (a + b) Ă· 2
Diese Funktion berechnet, wie alt jemand in 10 Jahren sein wird:
(define (? current-age)
(? current-age ?))
(??? 25) soll 35 ergeben.
Gegeben sind diese Funktionen:
(define (double x) (* x 2))
(define (add-five x) (+ x 5))
Was ergibt (double (add-five 3))?
define erstellst du Variablen und Funktionenâ±ïž Dauer: 30 Minuten
Statt viele einzelne Variablen nutzen wir EINE Liste:
(define scores (list 100 250 180 90 300))
Aus inf-schule.de:
"Ein funktionales Programm besteht aus einer Sammlung an Funktionen, die durch geschickte Kombination miteinander auch sehr komplexe Probleme lösen können."
Listen sind dabei fundamental â der Name "LISP" steht fĂŒr "LISt Processing"!
(define numbers (list 10 20 30 40))
(first numbers) ; â 10
(rest numbers) ; â (list 20 30 40)
(first (rest numbers)) ; â 20
(cons 5 (list 10 20 30)) ; â (list 5 10 20 30)
(cons 1 empty) ; â (list 1)
Eine Funktion, die eine Liste verarbeitet, hat diese Schablone:
(define (process-list lst)
(if (empty? lst)
; Basisfall: Was tun bei leerer Liste?
basis-wert
; Rekursivfall: Verarbeite first, dann rest
(kombiniere (first lst)
(process-list (rest lst)))))
(first (list 5 10 15))?(rest (list 5 10 15))?(cons 1 (list 2 3))?Gegeben: (define zahlen (list 100 200 300))
Wie bekommst du das zweite Element (200)?
Tipp: Benutze first und rest zusammen!
Lösung: (first (rest zahlen))
Diese Funktion zÀhlt die Elemente einer Liste:
(define (my-length lst)
(if (empty? lst)
0
(+ 1 (my-length (rest lst)))))
Was ergibt (my-length (list "a" "b" "c" "d"))?
first gibt das erste Element, rest den Restcons fĂŒgt ein Element vorne anâ±ïž Dauer: 15 Minuten
; above = ĂŒbereinander
(above (circle 30 "solid" "red")
(circle 50 "solid" "blue"))
; beside = nebeneinander
(beside (square 40 "solid" "red")
(square 40 "solid" "green"))
; overlay = ĂŒberlagern
(overlay (circle 20 "solid" "white")
(circle 50 "solid" "black"))
(place-image (circle 20 "solid" "red")
100 150 ; x=100, y=150
(empty-scene 400 300))
Kombiniere drei Kreise ĂŒbereinander:
(above (circle 20 "solid" "white")
(circle 30 "solid" "white")
(circle 40 "solid" "white"))
Bonus: FĂŒge Augen hinzu mit overlay!
Erstelle eine Ampel mit drei Kreisen (rot, gelb, grĂŒn) ĂŒbereinander.
Lösung:
(above (circle 25 "solid" "red")
(circle 25 "solid" "yellow")
(circle 25 "solid" "green"))
â±ïž Dauer: 20 Minuten
Programme mit Animation haben einen Zustand (state), der sich ĂŒber die Zeit Ă€ndert. Das ist ein wichtiges Konzept in der Informatik!
In Schreibe Dein Programm! werden Studierende bis zum Ende des Semesters befÀhigt, PacMan, Tetris oder Asteroids nachzubauen.
y-Position: 10
; tick: Ball fĂ€llt (y wird gröĂer)
(define (tick y)
(+ y 3))
; draw: Zeichnet den Ball
(define (draw y)
(place-image (circle 20 "solid" "red")
200 y
(empty-scene 400 400)))
; Animation starten!
(big-bang 0
(on-tick tick)
(to-draw draw))
(define (handle-key y key)
(cond
[(string=? key "up") (- y 10)]
[(string=? key "down") (+ y 10)]
[else y]))
(big-bang 200
(to-draw draw)
(on-key handle-key))
on-tick?Der Ball fÀllt mit (+ y 3) um 3 Pixel pro Tick.
Wie Ànderst du den Code, damit er doppelt so schnell fÀllt?
Lösung: (+ y 6)
Wenn der Zustand die x-Position ist, wie bewegst du ein Objekt nach LINKS?
Tipp: Links bedeutet, x wird...
big-bang startet die Animationon-tick Ă€ndert den Zustand automatischto-draw zeichnet den aktuellen Zustandon-key reagiert auf Tasteneingabenâ±ïž Dauer: 15 Minuten | Teil 1 von 3
In den nĂ€chsten drei Modulen bauen wir Schritt fĂŒr Schritt das Snake-Spiel. Heute: Die Grundlagen der Spielwelt.
Zuerst definieren wir feste Werte, die sich nicht Àndern:
; GröĂe eines Schlangen-Segments in Pixeln
(define SIZE 20)
; Spielfeld-GröĂe
(define WIDTH 400)
(define HEIGHT 400)
; Der Hintergrund
(define BACKGROUND (empty-scene WIDTH HEIGHT "black"))
Wenn wir spĂ€ter die GröĂe Ă€ndern wollen, mĂŒssen wir nur EINE Zahl Ă€ndern, nicht hunderte im ganzen Code!
Die Schlange besteht aus mehreren Segmenten. Jedes Segment hat eine Position (x, y):
; Eine Position ist eine Liste mit x und y
(define pos1 (list 200 200)) ; Kopf
(define pos2 (list 180 200)) ; Mitte
(define pos3 (list 160 200)) ; Schwanz
; Die Schlange ist eine Liste von Positionen
(define my-snake (list pos1 pos2 pos3))
; Hole x-Koordinate aus einer Position
(define (pos-x pos) (first pos))
; Hole y-Koordinate aus einer Position
(define (pos-y pos) (second pos))
; Test:
(pos-x (list 200 150)) ; â 200
(pos-y (list 200 150)) ; â 150
; Zeichnet ein grĂŒnes Quadrat an Position pos
(define (draw-segment pos image)
(place-image
(square (- SIZE 2) "solid" "green")
(pos-x pos)
(pos-y pos)
image))
Wir benutzen Rekursion, um jedes Segment zu zeichnen:
; Zeichnet alle Segmente der Schlange
(define (draw-snake snake image)
(if (empty? snake)
image ; Fertig! Gib das Bild zurĂŒck
(draw-snake
(rest snake) ; Rekursion mit dem Rest
(draw-segment (first snake) image))))
; Futter ist auch eine Position
(define food (list 300 300))
; Zeichnet das Futter als roten Kreis
(define (draw-food food-pos image)
(place-image
(circle (/ SIZE 2) "solid" "red")
(pos-x food-pos)
(pos-y food-pos)
image))
; Zeichnet Schlange und Futter auf den Hintergrund
(define (draw-game snake food)
(draw-snake snake
(draw-food food BACKGROUND)))
Kopiere den gesamten Code in WeScheme und teste:
(draw-game my-snake food)
Du solltest eine grĂŒne Schlange und rotes Futter sehen!
Wie erstellst du eine Position bei x=100 und y=250?
(list
)
â±ïž Dauer: 20 Minuten | Teil 2 von 3
Jetzt bringen wir die Schlange in Bewegung! Das ist der kniffligste Teil.
Je nach Richtung Àndert sich x oder y:
; Berechnet die neue Kopf-Position
(define (new-head head direction)
(cond
[(string=? direction "up")
(list (pos-x head) (- (pos-y head) SIZE))]
[(string=? direction "down")
(list (pos-x head) (+ (pos-y head) SIZE))]
[(string=? direction "left")
(list (- (pos-x head) SIZE) (pos-y head))]
[(string=? direction "right")
(list (+ (pos-x head) SIZE) (pos-y head))]))
"up" bedeutet y wird kleiner (- SIZE)
"down" bedeutet y wird gröĂer (+ SIZE)
Wir brauchen eine Funktion, die das letzte Element einer Liste entfernt:
; Entfernt das letzte Element einer Liste
(define (drop-last lst)
(if (empty? (rest lst))
empty ; Nur noch 1 Element? Gib leere Liste zurĂŒck
(cons (first lst)
(drop-last (rest lst)))))
; Test:
(drop-last (list 1 2 3 4)) ; â (list 1 2 3)
; Bewegt die Schlange um einen Schritt
(define (move-snake snake direction)
(cons (new-head (first snake) direction) ; Neuer Kopf vorne
(drop-last snake))) ; Schwanz weg
Wir packen alles in einen "Zustand":
; Der Spielzustand ist eine Liste: (snake direction food)
(define START-STATE
(list
(list (list 200 200) (list 180 200) (list 160 200)) ; Schlange
"right" ; Richtung
(list 300 300))) ; Futter
; Hilfsfunktionen fĂŒr den Zustand
(define (game-snake state) (first state))
(define (game-direction state) (second state))
(define (game-food state) (third state))
; Wird bei jedem Zeitschritt aufgerufen
(define (tick state)
(list
(move-snake (game-snake state) (game-direction state))
(game-direction state)
(game-food state)))
; Starte eine einfache Animation
(big-bang START-STATE
(on-tick tick 0.2)
(to-draw (lambda (state)
(draw-game (game-snake state) (game-food state)))))
Die Schlange sollte sich jetzt nach rechts bewegen!
Der Kopf ist bei Position (100, 100). Die Richtung ist "right".
Wo ist der neue Kopf? (SIZE = 20)
â±ïž Dauer: 20 Minuten | Teil 3 von 3
Jetzt fĂŒgen wir Tastatur-Steuerung und Game Over hinzu!
Richtung: right | Klicke erst auf das Spielfeld!
; Reagiert auf Pfeiltasten
(define (handle-key state key)
(cond
[(string=? key "up")
(list (game-snake state) "up" (game-food state))]
[(string=? key "down")
(list (game-snake state) "down" (game-food state))]
[(string=? key "left")
(list (game-snake state) "left" (game-food state))]
[(string=? key "right")
(list (game-snake state) "right" (game-food state))]
[else state])) ; Andere Tasten ignorieren
; PrĂŒft, ob eine Position auĂerhalb des Spielfelds ist
(define (outside? pos)
(or (< (pos-x pos) 0)
(> (pos-x pos) WIDTH)
(< (pos-y pos) 0)
(> (pos-y pos) HEIGHT)))
; Spiel vorbei, wenn der Kopf auĂerhalb ist
(define (game-over? state)
(outside? (first (game-snake state))))
(big-bang START-STATE
(on-tick tick 0.2) ; Alle 0.2 Sekunden bewegen
(to-draw draw-state) ; Zeichnen
(on-key handle-key) ; Tastatur
(stop-when game-over?)) ; Beenden bei Kollision
; ===== SNAKE SPIEL =====
; FĂŒr DrRacket (nicht WeScheme!)
(require 2htdp/universe)
(require 2htdp/image)
; ===== KONSTANTEN =====
(define SIZE 20)
(define WIDTH 400)
(define HEIGHT 400)
(define BACKGROUND (empty-scene WIDTH HEIGHT "black"))
; ===== POSITION HELFER =====
(define (pos-x pos) (first pos))
(define (pos-y pos) (second pos))
; ===== SPIELZUSTAND HELFER =====
(define (game-snake state) (first state))
(define (game-direction state) (second state))
(define (game-food state) (third state))
; ===== ZEICHNEN =====
(define (draw-segment pos image)
(place-image
(square (- SIZE 2) "solid" "green")
(pos-x pos) (pos-y pos) image))
(define (draw-snake snake image)
(if (empty? snake) image
(draw-snake (rest snake)
(draw-segment (first snake) image))))
(define (draw-food food-pos image)
(place-image
(circle (/ SIZE 2) "solid" "red")
(pos-x food-pos) (pos-y food-pos) image))
(define (draw-state state)
(draw-snake (game-snake state)
(draw-food (game-food state) BACKGROUND)))
; ===== BEWEGUNG =====
(define (new-head head direction)
(cond
[(string=? direction "up")
(list (pos-x head) (- (pos-y head) SIZE))]
[(string=? direction "down")
(list (pos-x head) (+ (pos-y head) SIZE))]
[(string=? direction "left")
(list (- (pos-x head) SIZE) (pos-y head))]
[(string=? direction "right")
(list (+ (pos-x head) SIZE) (pos-y head))]))
(define (drop-last lst)
(if (empty? (rest lst)) empty
(cons (first lst) (drop-last (rest lst)))))
(define (move-snake snake direction)
(cons (new-head (first snake) direction)
(drop-last snake)))
; ===== TICK =====
(define (tick state)
(list
(move-snake (game-snake state) (game-direction state))
(game-direction state)
(game-food state)))
; ===== TASTATUR =====
(define (handle-key state key)
(cond
[(string=? key "up")
(list (game-snake state) "up" (game-food state))]
[(string=? key "down")
(list (game-snake state) "down" (game-food state))]
[(string=? key "left")
(list (game-snake state) "left" (game-food state))]
[(string=? key "right")
(list (game-snake state) "right" (game-food state))]
[else state]))
; ===== GAME OVER =====
(define (outside? pos)
(or (< (pos-x pos) 0)
(> (pos-x pos) WIDTH)
(< (pos-y pos) 0)
(> (pos-y pos) HEIGHT)))
(define (game-over? state)
(outside? (first (game-snake state))))
; ===== START =====
(define START-STATE
(list
(list (list 200 200) (list 180 200) (list 160 200))
"right"
(list 300 300)))
(big-bang START-STATE
(on-tick tick 0.2)
(to-draw draw-state)
(on-key handle-key)
(stop-when game-over?))
Versuche, das Spiel zu erweitern:
Du hast ein komplettes Spiel programmiert! Das zeigt, dass du verstehst:
đ Die wichtigsten Fehler und wie man sie vermeidet
(+ 1 2 3)) ; Zu viele! â
(+ 1 2 3 ; Zu wenige! â
(+ 1 2 3) ; â Richtig!
(2 + 3) ; â FALSCH
(+ 2 3) ; â RICHTIG
(define my name "Max") ; â
(define my-name "Max") ; â
(first empty) ; â FEHLER!
; Erst prĂŒfen:
(if (empty? lst) 0 (first lst)) ; â
(define double x (* x 2)) ; â
(define (double x) (* x 2)) ; â
| Englisch | Deutsch |
|---|---|
define | definieren |
list / first / rest | Liste / erstes / Rest |
empty / empty? | leer / ist leer? |
cons | konstruieren |
if / cond / else | wenn / Bedingungen / sonst |
circle / rectangle / square | Kreis / Rechteck / Quadrat |
above / beside / overlay | ĂŒbereinander / nebeneinander / ĂŒberlagern |
place-image / empty-scene | Bild platzieren / leere Szene |
big-bang / on-tick / to-draw | Animation / bei Tick / zeichnen |
on-key / stop-when | bei Taste / stoppen wenn |
snake / food / direction | Schlange / Futter / Richtung |
Alles Wichtige auf einen Blick!
(+ 1 2 3) ; â 6
(- 10 3) ; â 7
(* 4 5) ; â 20
(/ 20 4) ; â 5
(sqr 5) ; â 25
(abs -7) ; â 7
(define name 42)
(define (double x) (* x 2))
(list 1 2 3) (first lst) (rest lst)
(cons 0 lst) (empty? lst) (length lst)
(if bedingung dann sonst)
(cond [bed1 res1] [bed2 res2] [else default])
(circle r "solid" farbe)
(rectangle b h "solid" farbe)
(above bild1 bild2) (beside bild1 bild2)
(place-image bild x y szene)
(big-bang start-zustand
(on-tick tick-funktion)
(to-draw zeichen-funktion)
(on-key tasten-funktion)
(stop-when stopp-bedingung))
Herzlichen GlĂŒckwunsch! Du hast den gesamten Kurs durchgearbeitet!
(+ 2 3) statt 2 + 3(define (name param) body)first, rest, cons, empty?big-bang startet die Animationon-keyAus Schreibe Dein Programm!:
"Programmierung mittels Konstruktionsanleitungen kommt dem disziplinierten ingenieursmĂ€Ăigen Bauen nĂ€her als alles andere."
Du hast gelernt, Programme systematisch zu konstruieren â nicht nur zu "hacken". Das ist eine FĂ€higkeit, die dir bei jeder Programmiersprache helfen wird!
Du bist jetzt ein Racket-Programmierer!
Denk dran: Jeder Experte war einmal ein AnfÀnger.
Programmieren lernt man durch Ăben â also weitermachen! đ