EMZETT.
Login

Kurz: (define (anwenden f x) (f x)) (define (zweimal f) (lambda (x) (f (f x)))) (define (kompose . fs) (if (null? fs) identity (lambda (x) ((car fs) ((apply kompose (cdr fs)) x))))) (define (addierer n) (lambda (x) (+ x n)))

Teil des Kurses Racket

Kapitel 4 von 8 im Kurs Racket. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

Funktionen als Werte

#lang racket
 
(define (anwenden f x) (f x))
(define (zweimal f) (lambda (x) (f (f x))))
(define (kompose . fs)
  (if (null? fs) identity (lambda (x) ((car fs) ((apply kompose (cdr fs)) x)))))
(define (addierer n) (lambda (x) (+ x n)))
 
(displayln (anwenden add1 5))
(displayln ((zweimal add1) 5))
(displayln ((kompose add1 (lambda (x) (* x 10))) 4))
(displayln (map (addierer 100) '(1 2 3)))
(displayln ((curry + 5) 10))
(displayln ((curryr - 1) 10))
(displayln ((compose add1 sqr) 5))
(displayln (map + '(1 2 3) '(10 20 30)))
(displayln (apply map list '((1 2 3) (4 5 6))))
(displayln (sort '("ccc" "a" "bb") < #:key string-length))
(displayln (foldr cons '() '(1 2 3)))
(displayln (foldl cons '() '(1 2 3)))
(displayln (filter-not even? '(1 2 3 4)))
(displayln (let loop ([n 5] [acc '()]) (if (zero? n) acc (loop (sub1 n) (cons n acc)))))

Ausgabe:

6
7
41
(101 102 103)
15
9
26
(11 22 33)
((1 4) (2 5) (3 6))
(a bb ccc)
(1 2 3)
(3 2 1)
(1 3)
(1 2 3 4 5)

Closures und Zustand

#lang racket
 
(define (make-zaehler)
  (define n 0)
  (lambda () (set! n (add1 n)) n))
 
(define a (make-zaehler))
(define b (make-zaehler))
(a) (a)
(displayln (list (a) (b)))
 
(define (memoize f)
  (define cache (make-hash))
  (lambda args
    (hash-ref! cache args (lambda () (apply f args)))))
 
(define langsam-fib
  (lambda (n) (if (< n 2) n (+ (langsam-fib (- n 1)) (langsam-fib (- n 2))))))
(set! langsam-fib (memoize langsam-fib))
(displayln (langsam-fib 80))
 
(define-values (get set) (let ([x 0]) (values (lambda () x) (lambda (v) (set! x v)))))
(set 42)
(displayln (get))

Ausgabe:

1
2
(3 1)
23416728348467685
42

Makros

define-syntax-rule erzeugt einfache Syntaxerweiterungen:

#lang racket
 
(define-syntax-rule (wiederhole n rumpf ...)
  (for ([i n]) rumpf ...))
 
(define-syntax-rule (tausche! a b)
  (let ([tmp a]) (set! a b) (set! b tmp)))
 
(define-syntax-rule (mein-unless bedingung rumpf ...)
  (if bedingung (void) (begin rumpf ...)))
 
(define-syntax-rule (zeit-block name rumpf ...)
  (begin (printf "Start ~a~n" name) (begin0 (let () rumpf ...) (printf "Ende ~a~n" name))))
 
(wiederhole 3 (display "x "))
(newline)
(define p 1)
(define q 2)
(tausche! p q)
(displayln (list p q))
(mein-unless #f (displayln "läuft"))
(displayln (zeit-block "Rechnung" (+ 40 2)))
 
(define-syntax for-each-paar
  (syntax-rules ()
    [(_ ((x y) liste) rumpf ...)
     (for-each (lambda (paar) (let ([x (car paar)] [y (cdr paar)]) rumpf ...)) liste)]))
(for-each-paar ((k v) '((a . 1) (b . 2))) (printf "~a=~a " k v))
(newline)

Ausgabe:

x x x
(2 1)
läuft
Start Rechnung
Ende Rechnung
42
a=1 b=2

Tipp

Racket-Makros sind hygienisch: Hilfsvariablen wie tmp kollidieren nicht mit Namen des Aufrufers.

Fehler und Verträge

#lang racket
 
(define (teile a b)
  (when (zero? b) (raise-user-error 'teile "Division durch null"))
  (/ a b))
 
(with-handlers ([exn:fail? (lambda (e) (displayln (string-append "Fehler: " (exn-message e))))])
  (teile 1 0))
 
(with-handlers ([exn:fail:contract:divide-by-zero? (lambda (e) (displayln "durch null"))])
  (/ 1 0))
 
(with-handlers ([string? (lambda (s) (displayln (string-append "String geworfen: " s)))])
  (raise "etwas ist schiefgelaufen"))
 
(define/contract (quadrat x)
  (-> number? number?)
  (* x x))
(displayln (quadrat 6))
(with-handlers ([exn:fail:contract? (lambda (e) (displayln "Vertrag verletzt"))])
  (quadrat "x"))
 
(dynamic-wind
  (lambda () (displayln "vorher"))
  (lambda () (displayln "mittendrin"))
  (lambda () (displayln "nachher")))
(displayln (call-with-current-continuation (lambda (k) (+ 1 (k 42)))))

Ausgabe:

Fehler: teile: Division durch null
durch null
String geworfen: etwas ist schiefgelaufen
36
Vertrag verletzt
vorher
mittendrin
nachher
42

Merke

  • Funktionen sind Werte: lambda, curry, compose, map, foldl
  • Closures kapseln Zustand mit set!; Memoization per Hashtabelle
  • define-syntax-rule und syntax-rules erzeugen hygienische Makros
  • with-handlers, raise, define/contract und dynamic-wind behandeln Fehler

Übungsaufgabe

Schreibe ein Makro my-while, das eine Bedingung wiederholt prüft.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Strings, Vektoren, Hashtabellen und Strukturen

Weiter: Klassen, Module und Typen

Alle Kapitel: Racket im Überblick