Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
Repository files navigation
#+title: LISP-Kurs
#+author: Sebastian Jordan
#+latex_header: \usepackage[ngerman]{babel}
* License
Copyright (C) 2015 Sebastian Jordan. Permission is granted to copy,
distribute and/or modify this document under the terms of the GNU
Free Documentation License, Version 1.3 or any later version
published by the Free Software Foundation; with no Invariant
Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of
the license is included in the section entitled "GNU Free
Documentation License".
* Anleitung fuer dieses Dokument
Dieses Dokument enthaelt sehr viel Schemecode. Du kannst den Code
extrahieren, sofern du GNU Make und Emacs auf deinem System
installiert hast. Fuehre einfach den Befehl
#+begin_src sh
make all
#+end_src
im Verzeichnis aus, in dem die Datei =README.org= liegt. Eine
pdf-Version dieses Dokuments wird ebenfalls erzeugt, sofern =latex=
auf deinem System installiert ist.
* Einfuehrung
Dieser Text soll dabei Helfen, einen konzeptuellen Zugang zum
Schreiben von Computerprogrammen aus einer funktionalen Perspektive
zu geben.
** Themen
* Rekursion
* Datenstrukturen
* Streams & Lazy evaluation
* Concurrency
** Material
* [[https://www.gnu.org/software/emacs/][Emacs]] :: Programmiereditor mit gutem LISP/Scheme-Support
* [[http://www.vim.org/][Vim]] :: Standard Linuxtexteditor der auf fast allen Systemen
installiert ist (Vi)
* [[https://mitpress.mit.edu/sicp/][Structure and Interpretation of Computer Programs]] :: Ein wirklich
sehr gutes Lehrbuch zum Thema, das den Author stark
beeinflusst hat.
* [[https://www.gnu.org/software/guile/][Guile]] :: Wir benutzen Guile als Schemeinterpreter
* [[http://orgmode.org/][org-mode]] :: Dieses Dokument ist mit Hilfe von org-mode verfasst.
* Ein Beispiel: Wurzel nach Newtonverfahren
#+caption: =sqrt-newton.scm=
#+begin_src scheme :tangle examples/sqrt-newton.scm
(define (sqrt-newton x)
;; iteration step
(define (sqrt-iter guess)
(if (good-enough? guess)
guess
(sqrt-iter (improve guess))))
;; test if we are already good enough with our guess
(define (good-enough? guess)
(<= (abs (- (square guess) x)) 0.0001))
;; square a number
(define (square n) (* n n))
;; improve a guess
(define (improve guess)
(avarage guess (/ x guess)))
(define (avarage a b)
(/ (+ a b) 2.0))
;; start the iteration with 1 as initial guess
(sqrt-iter 1))
;; test the function
(define test (sqrt-newton 4))
(display "Die Wurzel von 4 ist ")
(display test)
test
#+end_src
#+RESULTS:
: 2.0000000929222947
Diese Beispiel findest du unter dem Namen =examples/sqrt-newton.scm=, wenn du
die =.scm=-Dateien gemaess der obigen Anleitung erzeugt hast.
* Wiederholung: Funktionen & Blockstruktur
** Aufgabe 1.3
Hier ist eine Beispielloesung fuer die Aufgabe 1.3 aus dem Buch:
#+begin_src scheme
(define (add-and-mult a b c)
(define (second-greatest m n o)
(if (and (<= m n) (<= m o))
(min n o)
(if (and (<= n m) (<= n o))
(min m o)
(min m n))))
(define greatest max)
(define (square x) (* x x))
(+ (square (second-greatest a b c))
(square (greatest a b c))))
;; test the function
(add-and-mult 4 2 3)
#+end_src
#+RESULTS:
: 25
Das Ergebnis des Tests:
#+RESULTS:
: 25
** Aufgabe 1.8
Zum Loesen der Aufgabe 1.8 verwenden wir die selbe Strategie wie
fuer das Finden der Quadratwurzel. Wir veraendern allerdings die
=improve=-Funktion.
#+begin_src scheme
(define (cuberoot-newton x)
(define (cubert-iter guess)
(if (good-enough? guess)
guess
(cubert-iter (improve guess))))
(define (good-enough? guess)
(>= 0.001
(abs (- (cube guess) x))))
;; new improve function
(define (improve guess)
(/ (+ (* 2. guess) (/ x (square guess)))
3.))
(define (cube n) (* n (square n)))
(define (square n) (* n n))
(cubert-iter 1))
;; test the function
(cuberoot-newton 125.0)
#+end_src
#+RESULTS:
: 5.000000000287929
* Rekursion
Wir wollen die Fakultaet einer Zahl berechnen. Dazu uebertragen wir
die Definition der Fakultaet in Scheme.
\begin{align}
!x &= x \cdot !(x - 1) \\
!0 &= 1
\end{align}
Eine intuitivie Definition der Fakultaet koennte folgende sein:
#+begin_src scheme :exports both
(define (factorial x)
(if (<= x 1)
1 ;; base case
(* x (factorial (- x 1))))) ;; recursive step
;; test the function
(factorial 6)
#+end_src
#+RESULTS:
: 720
Dies ist eine vereinfachte Darstellung der Auswertung der obigen
Funktion. Wie wir sehen koennen, benoetigt die Funktion "linear
viel" Speicher.
#+begin_src scheme
(factorial 5)
(* 5 (factorial 4))
(* 5 (* 4 (factorial 3)))
(* 5 (* 4 (* 3 (factorial 2))))
(* 5 (* 4 (* 3 (* 2 (factorial 1)))))
(* 5 (* 4 (* 3 (* 2 1))))
(* 5 (* 4 (* 3 2)))
(* 5 (* 4 6))
(* 5 24)
120
#+end_src
Unser Ziel ist es, nur konstant viel Speicher -- also unabhaengig
von der eingegebnene Zahl -- zu verbrauche. Dazu wandeln wir die
rekursive Definition der Fakultaet in einen iterativen Prozess um.
#+begin_src scheme :exports both
(define (factorial x)
(define (iter accu count)
(if (= count x)
(* count accu)
(iter (* accu count) (+ count 1))))
(iter 1 1))
(factorial 5)
#+end_src
#+RESULTS:
: 120
Fuer eine Iteration benoetigen wir (genauso wie in Python und Co)
nur konstant viel Speicher. Hier ist dargestellt wie der
Interpreter den Funktionskoerper der obigen Funktion auswerten
koennte.
#+begin_src scheme
(define x 3)
(iter 1 1)
(iter 1 2)
(iter 2 3)
6
#+end_src
** Fibonacci
Die ersten 10 Elemente der Fibonaccireihe.
| fib(n) | 0 | 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 | 34 |
|--------+---+---+---+---+---+---+---+----+----+----+
| n | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
Eine rekursive Definition der Reihe:
\begin{equation}
fib(n) = fib(n -1) + fib(n - 2)
\end{equation}
Hier ist eine Pseudoimplementation der Fibnoaccireihe im
imperativen Stil:
#+begin_src
int a <- 0
int b <- 1
for i in (3..n)
int c <- a + b
a <- b
b <- c
return b
#+end_src
Als Uebungsvorschlag: Du koenntest versuchen, die Fibonaccireihe
als Rekursion & Iteration zu implementieren (Siehe Fakultaet).
Eine rekursive Beispielimplementation fuer die Fibonaccireihe:
#+begin_src scheme :exports both
(define (fib-rec n)
(cond ((< n 1) (error "FIB-REC: index to small"))
((= n 1) 0)
((= n 2) 1)
((> n 2) (+ (fib-rec (- n 1)) (fib-rec (- n 2))))))
(fib-rec 10)
#+end_src
#+results:
: 34
Hier ist eine iterative Beispielimplementation der Fibonaccizahlen.
#+begin_src scheme :exports both
(define (fib-iter n)
(define (iter counter n-1 n-2)
(if (= counter n)
n-1
(iter (1+ counter) (+ n-1 n-2) n-1)))
(cond ((= n 1) 0)
((= n 2) 1)
(else (iter 2 1 0))))
(fib-iter 10)
#+end_src
#+results:
: 34
* Listen
Listen bestehen aus Paaren.
** Paare
Paare sind zusammengesetzte Datenstrukturen, das heisst, dass sich
Paare in kleinere Bestandteile zerlegen lassen und, vor allem, sich
aus kleineren Bestandteilen zusammen bauen lassen.
Paare sind "Behaelter", die genau 2 Werte speichern koennen. Es
gibt einen "ersten" Wert und einen "zweiten" Wert, die eindeutig
Adressierbar sein muessen.
Es folgt eine Beispielinterface fuer das Programmieren mit Paaren:
#+begin_src scheme
(define (pair a b)
(error "PAIR: undefined"))
(define (1st p)
(error "1ST: undefined"))
(define (2nd p)
(error "2ND: undefined"))
;; What would you get?
(1st (pair 1 2)) ;; 1
(2nd (pair 1 2)) ;; 2
(1nd (2nd (2nd (pair 1
(pair 2
(pair 3
4))))))
;; 3
#+end_src
*** Und in Scheme?
In Scheme sind die =pair=-, =1st=- und =2nd=-Funktion schon definiert.
* pair == cons
* 1st == car
* 2nd == cdr
Beispiel fuer =car=:
#+begin_src scheme :exports both
(define new-pair (cons 1 2))
(car new-pair)
#+end_src
#+results:
: 1
Beispiel fuer =cdr=:
#+begin_src scheme
(define new-pair (cons 1 2))
(cdr new-pair)
#+end_src
#+results:
: 2
Andere nuetzliche Funktionen im Zusammenhang mit Paaren:
#+begin_src scheme
(pair? (cons 1 2)) ;; #t
(pair? 1) ;; #f
#nil ;; #nil ist der sogenannte Nullzeiger und signalisiert KEINEN
;; Wert.
(null? #nil) ;; #t
(null? 1) ;; #f
#+end_src
** Definition der Primitiven
Hier ist eine Definition von "Paaren" (ohne =pair?=).
#+caption: =<<custom-definition-pairs>>=
#+name: custom-definition-pairs
#+begin_src scheme
(define (my-cons a b)
(define (dispatch mode)
(cond ((= mode 1) a)
((= mode 2) b)
(else (error "COND: Argument not [1..3] -- " mode))))
dispatch)
(define (my-car list)
(list 1))
(define (my-cdr list)
(list 2))
#+end_src
Auswertung der Implementation per Befehlssubstitution:
#+begin_src scheme
;; testevaluation (KOMMENTIEREN)
(my-car (my-cons 5 8))
;; zuerst werten wir den Rueckgabewert von my-cons aus. my-cons gibt
;; uns eine Funktion zurueck (dispatch), die hier durch das lambda
;; dargestellt wird.
(my-car (lambda (mode) (cond ((= mode 1) 5)
((= mode 2) 8))))
;; Jetzt wird my-car ausgewertet. my-car "bewirkt" dass das Argument
;; (also in diesem Fall die "lambda"-Funktion ein Argument bekommt und
;; dann ausgewertet wird.
((lambda (mode)
(cond ((= mode 1) 5)
((= mode 2) 8)))
1)
;; Nun wird der Aufruf der "lambda"-Funktion durch den Koerper der
;; Funktion ersetzt.
((define mode 1)
(cond ((= mode 1) 5)
((= mode 2) 8)))
5
#+end_src
** Jetzt wirklich Listen
Listen sind in Scheme einfach nur "geschachtelte" Paare.
#+caption: =<<definitions-lists>>=
#+name: definitions-lists
#+begin_src scheme :tangle examples/definitions-lists.scm
;; definitions-lists
;;;;;;;;;;;;;;;;;;;;
(define empty-list #nil)
(define (list-empty? list) (null? list))
;; put an element in front of the list
(define (prepend elem list)
(cons elem list))
;; put an element in the end of a list
(define (append list elem)
(if (null? list)
(cons elem #nil)
(cons (car list) (append (cdr list) elem))))
;; get the first element of a list
(define (head list)
(cond ((pair? list) (car list))
((null? list) (error "HEAD: list is empty"))
(else (error "HEAD: object is not a list"))))
;; get all but the first element of a list
(define (tail list)
(cond ((pair? list) (cdr list))
((null? list) (error "TAIL: list is empty"))
(else (error "TAIL: object is not a list"))))
;; get all but the last element of a list
(define (init list)
(cond ((null? list) (error "INIT: empty list given"))
((null? (cdr list)) #nil)
(else (cons (car list)
(init (cdr list))))))
;; get the last element of a list
(define (last list)
(cond ((null? list) (error "LAST: empty list given"))
((null? (cdr list)) (car list))
(else (last (cdr list)))))
;; get the n-th element of a list (starting with 0)
(define (index list n)
(if (= n 0)
(car list)
(index (cdr list) (1- n))))
#+end_src
** Generalisierung
Wenn wir mit Listen zu tun haben, dann kommen bestimmte "Probleme"
oft vor. Betrachten wir zum Beispiel das folgende Stueckchen Code:
#+caption: =examples/map-01.scm=
#+begin_src scheme :tangle examples/map-01.scm :noweb no-export
<<definitions-lists>>
;; This procedure adds 1 to every element
(define (add-one list)
(if (list-empty? list)
empty-list
(prepend (1+ (head list))
(add-one (tail list)))))
(define numbers '(1 2 3 4))
(display "The original list is ")
(write numbers)
(newline)
(display "add-one applied to the list results in ")
(write (add-one numbers))
(newline)
;; This procedure multiplies every element by 2
(define (mult-two list)
(if (list-empty? list)
empty-list
(prepend (* 2 (head list))
(mult-two (tail list)))))
(define numbers '(1 2 3 4))
(display "The original list is ")
(write numbers)
(newline)
(display "mult-two applied to the list results in ")
(write (mult-two numbers))
(newline)
#+end_src
Beide Funktionen machen etwas sehr Aehnliches. Es wird ueber eine
Liste iteriert. Dabei wird auf jedes Element eine Operation
angewendet und so eine neue Liste erzeugt.
#+begin_example scheme
(define (<function> list)
(if (list-empty? list)
empty-list
(prepend (<operation> (head list))
(<function> (tail list)))))
#+end_example
Die Generalisierung dieser beiden Funktionen wird =map= genannt.
Wir koennen diese Idee allgemein in Scheme formulieren:
#+caption: =<<definition-map>>=
#+name: definition-map
#+begin_src scheme :noweb no-export
(define (map operation list)
(if (list-empty? list)
empty-list
(prepend (operation (head list))
(map operation (tail list)))))
#+end_src
Im folgenden Stueckchen Code benutzen wir =map= um weitere
Funktionen zu definieren.
#+caption: =examples/map-02.scm=
#+begin_src scheme :noweb no-export :tangle examples/map-02.scm
<<definitions-lists>>
<<definition-map>>
(define (add-one list)
(map 1+ list))
(define (mult-two list)
(map (lambda (x)
(* 2 x))
list))
(write (mult-two '(1 2 3 4 5)))
(newline)
(write (add-one '(1 2 3 4 5)))
(newline)
#+end_src
** Hausaufgabe
Wir stellen uns einmal vor, dass wir eine Liste von Zahlen gegeben
haben und wollen alle Zahlen aufsummieren. Der Code dafuer wurde
wohl in etwa folgendermasse aussehen:
#+caption: =examples/sum.scm=
#+begin_src scheme :tangle examples/sum.scm :noweb no-export
;; We have to include the definitions for our list primitives
<<definitions-lists>>
(define (sum-list list)
(define (iter accu current)
(if (list-empty? current)
accu
(iter (+ accu (head current)) (tail current))))
(iter 0 list))
(define numbers '(1 2 3 4 5 6))
(display "The sum of ")
(write numbers)
(display " is ")
(write (sum-list numbers))
(newline)
#+end_src
Wie koennen wir diese Funktion generalisieren? Wenn du im Internet
recherchieren willst, dann suche nach den Stichworten =fold=, =left
fold=, =foldl=, wie zum Beispiel [[https://duckduckgo.com/?q%3Dscheme%2Bfold&t%3Dffab][hier]] (der Link funktioniert aus
irgendeineem Grund nicht auf der github-Seite) geschehen.
*** Loesung
Die =sum-list= Funktion macht prinzipiell 2 Dinge:
1. Die Funktion iteriert ueber die List (so wie in =map=).
2. Die Funktion akkumuliert Werte, die in der Liste gespeichert
sind mittels einer Kombinationsfunktion.
#+caption: =<<definition-foldl>>=
#+name: definition-foldl
#+begin_src scheme
(define (foldl accu-fun start list)
(cond ((list-empty? list) start)
(else (foldl accu-fun
(accu-fun start (head list))
(tail list)))))
#+end_src
Wir koennen nun die =foldl=-Funktion fuer verschiedene Dinge nutzen:
#+caption: =examples/foldl.scm=
#+begin_src scheme :tangle examples/foldl.scm :noweb no-export
;; We have to include list primitives
<<definitions-lists>>
;; ... and foldl
<<definition-foldl>>
<<definition-map>>
;; The sum function
(define (sum list) (foldl + 0 list))
;; The length function
(define (length list)
(foldl (lambda (accu e)
(1+ accu))
0
list))
;; We can even define a filter function
(define (filter predicate list)
(foldl (lambda (accu-list current)
(if (predicate current)
(append accu-list current)
accu-list))
empty-list
list))
#+end_src
#+caption: =<<definition-filter>>=
#+name: definition-filter
#+begin_src scheme
(define (filter pred list)
(foldr (lambda (x accu)
(if (pred x)
(prepend x accu)
accu))
empty-list
list))
#+end_src
** foldr
Manchmal wollen wir aber auch ueber eine Liste von "hinten" aus
iterieren. Aehnlich wie =foldl= wollen wir eine
Accumulationsfunktion und einen Startwert angeben koennen. Die
Funktion soll dabei jedes Element nur einmal ansehen.
#+caption: =<<definition-foldr>>=
#+name: definition-foldr
#+begin_src scheme
(define (foldr f start list)
(cond ((null? list) start)
(else (f (car list)
(foldr f start (cdr list))))))
#+end_src
** Hausaufgaben
* =mkList= :: Hat 1 Argument, n. Soll Liste der Laenge n erzeugen,
mit nur 1en drin.
#+begin_src scheme
(define (mkList n)
(cond ((= n 0) #nil)
(else (cons 1 (mkList (- n 1))))))
#+end_src
* =mkNumbers= :: Hat 1 Argument, n. Soll Liste erzeugen, mit den
Zahlen 1 bis n.
#+begin_src scheme
(define (mkNumbers n)
(define (iter current)
(cond ((= current n) #nill)
((< current n)
(cons (+ 1 current) (iter (+ 1 current))))
(else (error "Internal error"))))
(iter 0))
(define (mkNumbers2 n)
(define (iter current acc)
(cond ((= current n) acc)
((< current n) (iter (+ 1 current) (append acc (+ 1 current))))))
(iter 0 #nil))
(define (mkNumber3 n)
(if (= n 0)
#nil
(append (mkNumber3 (- n 1)) n)))
#+end_src
* =iter-list= :: Hat 3 Argumente
* iter-fun :: Ist eine Funktion, die ein Argument hat
* start-val :: Hat den passenden Typen zu iter-fun
* n :: Integer, so viele Elemente soll die Liste am Ende haben
#+caption: =<<definition-iter-list.scm>>=
#+name: definition-iter-list.scm
#+begin_src scheme
(define (iter-list fun start len)
(define (iter current current-elem)
(cond ((= current len) #nil)
((< current len)
(cons current-elem (iter (+ 1 current) (fun current-elem))))
(else (error "Internal error"))))
(iter 0 start))
#+end_src
#+begin_src scheme
(iter-list 1+ 0 10)
;; '(0 1 2 3 4 5 6 7 8 9)
(iter-list (lambda (x) (cons 1 x)) empty-list 3)
;; '(#nil '(1) '(1 1))
#+end_src
** The List dropth, the List taketh
Manchmal sind wir an den ersten n Elementen einer Liste
interessiert. Wir koennen dann entsprechend oft =head= und =tail=
anwenden.
#+begin_src scheme
(define (first-3-elems xs)
(list (head xs) (head (tail xs)) (head (tail (tail xs)))))
#+end_src
Die Funktion =first-3-elems= nimmt eine Liste entgegen und gibt
eine neue Liste zurueck, die die ersten 3 Elemente enthaelt. Wir
koennen diese Funktion zu einer allgemeineren Funktion
abstrahieren, die eine Liste und eine Zahl n entgegen nimmt und die
ersten n Elemente der Liste zurueck gibt.
#+caption: =<<definition-take>>=
#+name: definition-take
#+begin_src scheme
(define (take n xs)
(cond ((= n 0) empty-list)
((list-empty? xs)
(error "-- TAKE: tried to get an element from the empty list"))
(else (cons (head xs) (take (1- n) (tail xs))))))
#+end_src
Analog dazu koennen wir auch eine Funktion definieren, die die
ersten n Elemente einer Liste verwirft und den "Rest" zurueck gibt.
#+caption: =<<definition-drop>>=
#+name: definition-drop
#+begin_src scheme
(define (drop n xs)
(cond ((= 0 n) xs)
((list-empty? xs)
(error "-- DROP: cannot drop another element from the empty list"))
(else (drop (1- n) (tail xs)))))
#+end_src
** Was noch fehlt... Sortieren!
Wir haben gelernt, wie wir
* Listen (mit Hilfe von "higher order functions" erzeugen koenne
* primitive Operationen auf Listen durchfuehren koennen, die
einzelne Elemente der Liste manipulieren
* wiederkehrende Operationen abstrahieren koennen und "higher order
functions" nutzen koennen um weniger ( = besseren) Code zu
schreiben.
Wir haben noch nicht gelernt, wie wir Listen sortieren. Hier ist
eine Beispielimplementation von Quicksort. Sie sortiert eine Liste
von Zahlen aufsteigend der Groesse nach.
#+caption: =examples/quicksort.scm=
#+begin_src scheme :tangle examples/quicksort.scm :noweb no-export
<<definitions-lists>>
<<definition-map>>
<<definition-foldl>>
<<definition-foldr>>
<<definition-filter>>
(define (concat l1 l2)
(foldr (lambda (x accu)
(prepend x accu))
l2
l1))
(define (concat3 l1 l2 l3)
(concat l1
(concat l2 l3)))
(define (quicksort numbers)
(define (qs)
(let* ((pivot (head numbers))
(lower (filter (lambda (x) (< x pivot))
(tail numbers)))
(bigger (filter (lambda (x) (>= x pivot))
(tail numbers))))
(begin
(write lower)
(display " ")
(write pivot)
(display " ")
(write bigger)
(newline)
(concat3 (quicksort lower)
(list pivot)
(quicksort bigger)))))
(cond ((list-empty? numbers) empty-list)
(else (qs))))
#+end_src
Leider ist die Verwendung dieser Funktion darauf beschraenkt,
Zahlen der Groesse nach zu sortieren. Wir koennen diese
Beispielimplementation abstrahieren, indem wir "offen" lassen,
welche Vergleichsoperation beim Vergleich verwendet werden soll.
Auf diese Art koennen wir alle Listen nach beliebigen Kriterien
sortieren.
#+name: =<<definition-sort>>=
#+name: definition-sort
#+begin_src scheme
(define (quicksort smaller-than xs)
(if (list-empty? xs)
empty-list
(let*
((pivot (head xs))
(non-pivot (tail xs))
(< (lambda (x) (smaller-than x pivot)))
(>= (lambda (x) (not (smaller-than x pivot))))
(smaller (filter < non-pivot))
(bigger (filter >= non-pivot)))
(concat3 (quicksort smaller-than smaller)
(list pivot)
(quicksort smaller-than bigger)))))
;; Hier ist noch eine Implementation von mergesort
(define (mergesort smaller-or-equal-than xs)
(define (merge as bs)
(cond ((list-empty? as) bs)
((list-empty? bs) as)
(else (let
((a (head as))
(b (head bs)))
(if (smaller-or-equal-than a b)
(prepend a
(merge (tail as) bs))
(prepend b
(merge as (tail bs))))))))
(let*
((len (length xs))
(first-half (take (quotient len 2) xs))
(second-half (drop (quotient len 2) xs)))
(if (<= (length xs) 1)
xs
(merge (mergesort smaller-or-equal-than
first-half)
(mergesort smaller-or-equal-than
second-half)))))
#+end_src
Der Vollstaendigkeit halber, hier noch einmal die Definition von
=concat3= sauber notiert.
#+name: definition-concat
#+begin_src scheme
(define (concat l1 l2)
"Concatenate l1 with l2"
;; We choose foldr to prepend all the elements of l1 to l2. If we
;; chose to fold from the left and append every element of l2 to l1,
;; we would had a runtime behavior of O(n*m + n^2/2) where
;;
;; * n = length of l1
;; * m = length of l2
;;
;; This way we have O(n) as runtime behavior. (Why?)
(foldr (lambda (current accu)
(prepend current accu))
l2
l1))
(define (concat3 l1 l2 l3)
"Concatenate 3 lists l1 l2 l3"
;; First we concatenate l3 and l2, which in turn gets concatenated
;; with l1, which gives us a runtime behavior of $ O(n + m) $ where
;;
;; * n = length of l1
;; * m = length of l2
;; ( What would be the runtime behavior of
;; (concat (concat l1 l2) l3)
;; ?)
(concat l1
(concat l2
l3)))
#+end_src
** Zusammenfassung, eine kleine Library
Bis hier her haben wir uns angesehen, was wir alles mit Listen
anstellen koennen. Wir haben gelernt wie wir Listen als Paare
darstellen koennen und haben sogar Paare als Funktionen
dargestellt. Wir haben mathematische Probleme effizient geloest
(Fibonacci, Fakultaet) und daraus wiederkehrende Prozesse zu
Funktionen abstrahiert. Die Funktionen, die wir dabei definiert
haben, koennen wir zu einer Library zusammenfassen. Wir nennen sie
=lists.scm=.
#+caption: =<<lists.scm>>= (=lib/lists.scm=)
#+name: lists.scm
#+begin_src scheme :tangle lib/lists.scm :noweb no-export
<<definitions-lists>>
<<definition-map>>
<<definition-foldl>>
<<definition-foldr>>
<<definition-filter>>
<<definition-iter-list>>
<<definition-take>>
<<definition-drop>>
;; We have to define concat3 before the sorting algorithms because we
;; use these in their definition.
<<definition-concat>>
<<definition-sort>>
#+end_src
* Binaere Suchbaeume
Baeume sind genauso wie Listen in erster Linie eine Abstraktion
ueber Daten. Listen abstrahieren Daten als eine Sequence die von
vorne nach hinten durchgeblaettert werden kann. Das soll uns
ermoeglichen, ueber Daten als eine Einheit nachdenken zu koennen.
Listen sind fuer viele Dinge gut, vor allem wenn es um iterative
Prozesse geht. Fuer manche Dinge eignen sich Listen allerdings
nicht so gut, wie zum Beispiel das Finden von Daten, welches nur mit
einer Zeitkomplexitaet von $O(n)$ realisiert werden kann, selbst
wenn die Liste bereits sortiert ist. Listen sind auch nicht so
toll, wenn es um das hinzufuegen neuer Daten geht. Das hinzufuegen
eines Elements zum Beginn einer Liste geht schnell, aber alles
andere dauert viel laenger. Zur Erinnerung: Die =append=-Funktion
muss bis ans Ende der Liste iterieren, wenn es Element angehaengt
werden soll.
** Struktur
Baeume sind, so wie Listen, rekursive Datenstrukturen. Fuer Listen
beduetet dass, dass jede Liste entweder leer ist, oder die
Verkettung eines Elements mit einer List. Baeume sind sehr
aehnlich definiert: Jeder Baum ist entweder leer oder ein Element
verkettet mit zwei Unterbaeumen. Die Unterbaeume heissen "linker
Unterbaum" und "rechter Unterbaum".
#+caption: =<<tree-constructors>>=
#+name: tree-constructors
#+begin_src scheme
;; Definitions of tree constructors
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; a leaf is just an empty list
(define leaf empty-list)
(define leaf? null?)
;; a branch is a value and two subtrees
(define (branch val left right)
(list val left right))
(define branch-value car)
(define branch-left cadr)
(define branch-right caddr)
#+end_src
Die Baeume, die wir hier betrachten wollen heissen "binaere
Suchbaeume", binaer, weil jeder Branch ("Zweig") zwei Unterbaeume
hat und Suchbaum, weil wir die Baeume, die wir benutzen geordnet
sein sollen, also zum Suchen und Finden von Werten (Daten)
verwendet werden. Die Ordnung auf unserem Baum koennen wir
folgendermassen beschreiben: Fuer jeden Zweig in unserem Baum gilt,
dass alle Elemente, die im linken Unterbaum existieren kleiner als
das im Zweig gespeicherte Element sind und alle Elemente im rechten
Unterbaum sind grosser als das im Zweig gespeicherte Element. Dies
ermoeglicht uns, ad hoc einen Algorithmus zu beschreiben, der
beliebige vergleichbare(!!!) Elemente in unserem Baum zu finden.
#+begin_src scheme
<<tree-constructors>>
(define (lookup-integer tree n)
(cond ((leaf? tree) #f)
((= n (branch-value tree)) #t)
((< n (branch-value tree))
(lookup-integer (branch-left tree) n))
(else
(lookup-integer (branch-right tree) n))))
#+end_src
Es ist leicht zusehen, dass der gegebene Algorithmus rekursiv
definiert ist. Wenn wir wissen wollen, ob eine Zahl in einem Leaf
("Blatt") zu finden ist, dann antwortet der Algorithmus mit =#f=,
da ein Leaf keine Werte enthaelt. Sollte die Funktionen einen
Branch finden, dann ueberpruefen wir zu erst, ob das gesuchte
Element im aktuellen Branch gespeichert ist, sollte das der Fall
sein, geben wir =#t= zurueck. Sollte auch das nicht der Fall sein,
wird ueberprueft, ob die gesuchte Zahl kleiner oder groesser ist,
als das aktuelle Element (=branch-value=). Wenn das gesuchte
Element nun also kleiner ist, wissen wir, dass es auf Grund der
oben definierten Ordnung nur im linken Unterbaum stecken kann. Im
"groesser"-Fall kann es wiederum nur im rechten Unterbaum stecken
und wir setzen unsere Suche dort fort.
Es stellt sich nun die Frage, wie wir einen Baum konstruieren
koennen, und es stellt sich heraus, dass die Konstruktion fast
genauso wie die Suche ablaeuft. Wenn wir eine Element $x$ zu einem
leeren Baum, also Leaf, hinzufuegen wollen, dass geben wir einen
Branch zurueck, dessen =branch-value= gerade $x$ ist und dessen
Unterbaeume leere sind. Sollten wir versuchen das Element zu einem
Branch hinzuzufuegen, dann tritt einer der folgenden drei Faelle
ein:
- x = =branch-value= :: Das Element ist bereits im Baum enthalten
und wir geben einfach den Baum selbst zurueck.
- x < =branch-value= :: Um die Ordnung des Baumes zu bewahren,
wissen wir, dass $x$ dem linken Unterbaum hinzugefuegt werden
muss.
- x < =branch-value= :: Aequivalent zum obigen Fall muessen wir $x$
dem rechten Unterbaum hinzufuegen, um die Ordung zu wahren.
#+begin_src scheme
(define (insert-int tree x)
(cond ((leaf? tree) (branch x leaf leaf))
((= x (branch-value tree)) tree)
((< x (branch-value tree))
(branch (branch-value tree)
(insert-int (branch-left tree) x)
(branch-right tree)))
(else
(branch (branch-value tree)
(branch-left tree)
(insert-int (branch-right tree) x)))))
#+end_src
Damit haben wir die atomaren Operationen auf unserem Baum
definiert. Die Funktionen die wir definiert haben sind aber auf
=integer=-Werte spezialisiert, wir wuerden aber gern Baeume mit
beliebigen vergleichbaren Elementen betrachten koennen. Dafuer
brauchen wir eine Moeglichkeit Vergleiche zu kodieren. Wir waehlen
dazu folgende Form:
Ein Vergleich ist eine Funktion mit zwei Parametern.
1. Wenn diese Funktion $1$ zurueck gibt, ist das erste Argument
groesser als das zweite Argument.
2. Wenn die Funktion $0$ zurueck gibt, sind beide Argumente
gleichwertig.
3. Wenn die Funktion $-1$ zurueck gibt, ist das erste Argument
groesser als zweite.
Die folgende Funktion "implementiert" eine Vergleichsfunktion fuer
#+begin_src scheme
(define (comp-int x y)
(cond ((< x y) -1)
((> x y) 1)
(else 0)))
#+end_src
Mit einer solchen Funktion koennen wir eine =insert=-Funktion
definieren, unabhaengig von konkreten Vergleichsoperationen
funktioniert.
#+caption: =<<tree-insert>>=
#+name: tree-insert
#+begin_src scheme
(define (insert comp elem tree)
(cond ((leaf? tree) (branch elem leaf leaf))
(else
(let* ((left (branch-left tree))
(right (branch-right tree))
(val (branch-value tree))
(comp-val (comp elem val)))
(cond ((= comp-val 1)
(branch val left (insert comp elem right)))
((= comp-val -1)
(branch val (insert comp elem left) right))
((= comp-val 0)
(branch elem left right))
(else (error "insert: supplied comparison funktion is illdefined"
comp)))))))
#+end_src
Die =lookup=-Funktion ist aequivalent.
#+caption: =<<tree-lookup>>=
#+name: tree-lookup
#+begin_src scheme
(define (lookup comp elem tree)
(cond ((leaf? tree) #f)
(else
(let* ((val (branch-value tree))
(left (branch-left tree))
(right (branch-right tree))
(comp-val (comp elem val)))
(cond ((= comp-val 0) val)
((= comp-val 1)
(lookup comp elem left))
((= comp-val -1)
(lookup comp elem right))
(else
(error "lookup: supplied comp function is ill defined "
comp)))))))
#+end_src
Wir koennen nun Werte einem Baum hinzufuegen und ueberpruefen, ob
ein Wert im Baum gespeichert ist. Manchmal wollen wir aber auch
einen Wert aus dem Baum loeschen. Loeschen soll an dieser Stelle
konstruktiv verstanden werden. Wir wollen bestehende Baeume nicht
modifizieren (in der Tat wissen wir noch gar nicht wie wir
IRGENDEINEN Wert modifizieren koennen), sondern einen neuen Baum
erschaffen, in dem alle Werte aus dem alten Baum vorhanden sind,
bis auf den zu loeschenden Wert. Das Loeschen von Werten aus
Baeumen ist ein wenig komplizierter als das Loeschen von Werten aus
Listen (=filter=), da wir darauf achten muessen, dass die Struktur
des Baumes erhalten bleibt. Die Strategie beim Loeschen ist
folgendermassen:
Wenn wir Wert $x$ aus Baum $t$ loeschen wollen, suchen wir zu erst
die Stelle an der $x$ im Baum steht. Falls $x \not\in t$ geben wir
einfach den urspruenglichen Baum zurueck. Falls $x \in t$,
unterscheiden wir drei Faelle:
- $x$ hat kein Kind :: In diesem Fall ersetzen wir den Branch auf
dem $x$ gespeichert ist mit einem Leaf.
- $x$ hat ein Kind :: In diesem Fall ersetzen wir $x$ durch sein
Kind.
- $x$ hat zwei Kinder :: Wir tauschen $x$ mit dem "linkesten" Wert
im rechten Unterbaum von $x$, und fuehren diesen Fall damit
auf Fall 1 oder 2 zurueck.
#+caption: =<<tree-delete>>=
#+name: tree-delete
#+begin_src scheme
(define (delete comp elem tree)
;; delete the leftest node of a tree and return a pair of the
;; deletet value and the new tree
(define (delete-leftest t)
(let ((left (branch-left t))
(right (branch-right t))
(val (branch-value t)))
(cond ((leaf? left)
(cons val
right))
(else
(let ((pair (delete-leftest left)))
(cons (car pair)
(branch val (cdr pair) right)))))))
(cond ((null? tree) leaf) ;; The value is not in the tree
(else
(let* ((left (branch-left tree))
(val (branch-value tree))
(right (branch-right tree))
(comp-val (comp elem val)))
(cond ((= 1 comp-val)
(branch val left (delete comp elem right)))
((= -1 comp-val)
(branch val (delete comp elem left) right))
((and (leaf? left) (leaf? right)) leaf)
((leaf? right) left)
((leaf? left) right)
(else
(let ((l (delete-leftest right)))
(branch (car l) left (cdr l)))))))))
#+end_src
* Module
Guile/Scheme stellt ein Modulesystem bereit, auf das wir von nun an
zurueck greifen wollen. Module werden mit dem Befehl =use-modules=
geladen. Weitere Infos dazu findet ihr im [[https://www.gnu.org/software/guile/manual/html_node/Using-Modules.html#Using-Modules][Handbuch]] von Guile. Um
z.B. die =fold=-Implementation fuer Listen zu benutzen, laden wir
das Modul =(srfi srfi-1)=.
#+begin_src scheme :exports both
(use-modules (srfi srfi-1))
(define (sum ls)
(fold + 0 ls))
(sum '(1 2 3 4 5))
#+end_src
Eine [[https://www.gnu.org/software/guile/manual/html_node/Guile-Modules.html#Guile-Modules][Liste der verfuegbaren Standardmodule]] findet ihr auch im
Handbuch.
* Pattern matching
Es kann immer mal wieder vorkommen, dass wir Funktionen
schreiben/benutzen, deren Rueckgabewerte oder Argumente
umterschiedliche Strukturen haben koennen. Das einfachste Beispiel
an dieser Stelle ist =map= fuer Listen.
#+begin_src scheme
(define (map fun l)
(cond ((null? l) l)
(else
(cons (fun (car l))
(map fun (cdr l))))))
#+end_src
Wir unterscheiden, ob das Argument =l= gerade =#nil= ist oder ein
Paar. Dieses Muster kommt in funktionaler Programmierung relativ
haeufig vor. Scheme bietet eine andere Moeglichkeit dieses Problem
anzugehen: Pattern matching.
#+begin_src scheme
(use-modules (ice-9 match))
(define (map fun l)
(match l
(()
'())
((head tail ...)
(cons (fun head)
(map fun tail)))))
#+end_src
Diese Implementation von =map= "ueberprueft" die Struktur von =l=.
Handelt es sich um die leere Liste, wird diese leere Liste zurueck
gegeben. An sonsten wird die Liste aufgeteilt in =head= und =tail=,
wobei die Elemente der Liste an die entsprechenden Namen gebunden
werden, =fun= wird auf =head= angewendet und das Ergebnis dann mit
dem "gemapten" =tail= "geconst".
Weitere Infos zum Thema "pattern matching" findest du im [[https://www.gnu.org/software/guile/manual/html_node/Pattern-Matching.html#Pattern-Matching][Handbuch
von Guile]].
* Streams
Streams sind (so, wie Listen auch) Abstaktionen ueber Daten. Wir
stellen uns Streams als (un-)endlich lange Sammlung von Daten vor,
die wir nacheinander abrufen koennen. Ein Stream muss folgenden
Gesetzmaessigkeiten gehorchen:
* =(car-stream (cons-stream a b))= = =a=
* =(cdr-stream (const-stream a b))= = =b=
Das sieht ja erstmal genauso wie die Definition einer Liste aus. Es
gibt aber einen kleinen Unterschied zwischen Listen uns Streams:
Streams berechnen ihr die enthaltenen Werte nur auf Abruf.
#+caption: =examples/strict-map.scm=
#+begin_src scheme :tangle examples/strict-map.scm :noweb no-export
<<definitions-lists>>
<<definition-map>>
(define (print-and-mult-2 x)
(display x)
(newline)
(* 2 x))
(head (map print-and-mult-2 (list 1 2 3 4)))
#+end_src
* Functor
Ein Functor ist eine Abstraktion ueber Daten. Ein Functor
ermoeglicht das Transformieren von Daten innerhalb einer
Datenstruktur. Diese Transformationsfunktion wird haeufig =map=
oder =fmap= genannt.
Diese =fmap=-Funktion muss ein paar Regeln gehorchen, um in die
Kategorie Functor zu fallen.
1. =(fmap id x) = x=
2. =(fmap g (fmap f x)) = (fmap (compose g f) x)=
Die erste Functorregel sagt aus, dass wenn ich =fmap= mit der
Indentitaet auf einen Wert anwende, dann soll der Wert selbst dabei
herrauskommen. Die Idenitaet ist die Funktion, die Werte in sich
selbst ueberfuert.
#+begin_src scheme
(define (id x) x)
#+end_src
Fuer Listen erwarten wir das intuitiv von der =map=-Funktion:
#+begin_src scheme
(map id (list 1 2 3 4))
<-> (list (id 1) (id 2) (id 3) (id 4))
<-> (list 1 2 3 4)
#+end_src
Die zweite Functorregel sagt aus, dass es keine Rolle spielen darf,
ob wir zweimal =fmap= auf einen Wert anwenden (erst =f=, dann =g=),
oder ob wir die beiden Funktionen verketten und dann nur einmal
=fmap= anwenden. Die =compose= Funktion ist folgendermasse definiert.
#+begin_src scheme
(define (compose g f)
(lambda (x)
(g (f x))))
#+end_src
Noch einmal am Beispiel von Listen:
#+begin_src scheme
(map 2* (map 1+ (list 1 2 3)))
<-> (map 2* (list (1+ 1) (1+ 2) (1+ 3)))
<-> (list (2* (1+ 1)) (2* (1+ 2)) (2* (1+ 3)))
<-> (list ((compose 2* 1+) 1) ((compose 2* 1+) 2) ((compose 2* 1+) 3)
<-> (map (compose 2* 1+) (list 1 2 3))
#+end_src
Wir haben bereits Beispiele fuer Functors gesehen: Listen und
Baeume. Dabei ist die =map=-Funktion fuer Listen auch die
=fmap=-Funktion von Functors. Ein weiteres Beispiel fuer Functors
ist der Datentyp =maybe=.
** Beispiel: Maybe
Manchmal haben wir es mit Berechnungen zutun, die kein Ergebnis
liefern. Ein Beispiel dafuer, waere die Funktion =div=, die 2
Zahlen =a= und =b= nimmt und das Ergebnis der Division =a= / =b=
zurueck gibt.
Falls =b= gleich =0= ist, ist diese Operation nicht definiert. In
vielen Programmiersprachen wird dieses Problem durch Exceptions
behandelt, andere Sprachen geben im Fehlerfall einfach den
=0=-Zeiger zurueck und setzen eine Fehlervariable auf eine
speziellen Fehlercode.
Wir wollen uns einmal anschauen, wie wir eine potentiell
ergebnislose Operation ohne diese Spezialwerkzeuge behandeln
koennen. Dazu benoetigen wir eine Art Behaelter, der entweder
nichts (=nothing=) oder genau einen Wert (=just VALUE=) enthalten
kann. Wir muessen irgendwie Werte in diesen Behaelter tun koenne
und auch wieder extrahieren koennen. Dazu definieren wir folgende
Regeln:
* =(nothing? nothing) = #t=
* =(nothing? (just x)) = #f=
* =(just? nothing) = #f=
* =(just? (just x)) = #t=
* =(maybe x nothing) = x=
* =(maybe x (just y)) = y=
* =(from-just nothing) = UNDEFINED=
* =(from-just (just x)) = x=
Wir koennen mit diesen Operationen nun die =div=-Funktion
definieren:
#+begin_src scheme
(define (div x y)
(if (equal? y 0)
nothing
(just (/ x y))))
#+end_src
Stellen wir uns nun vor, dass wir das Ergebnis weiter verwenden
wollen, in dem wir eine Funktion definieren, die eine Zahl das
Reziproke einer Zahl findet (d.h. 1/x) und dann 1 hinzu addiert.
#+begin_src scheme
(define (rezi-and-add n)
(let ((rezi (div 1 n)))
(if (nothing? rezi)
nothing
(just (+ 1 (from-just rezi))))))
#+end_src
Diese erste Implementation sieht irgendwie umstaendlich aus. Wir
entpacken das Ergebnis der division nur, um es danach wieder zu
verpacken, ausserdem muessen wir uns explizit um den Fehlerfall
kuemmern. Was wir brauchen, ist eine Funktion, die einen
=maybe=-Wert nimmt und eine Operation darauf anwendet, sofern der
Wert nicht leer ist. Wir nennen diese Funktion =mmap= (MaybeMAP).
Diese Funktion soll die folgenden Regeln befolgen:
* =(mmap f nothing) = nothing=
* =(mmap f (just x)) = (just (f x))=
Mithilfe dieser Funktion koennen wir die =rezi-and-add=-Funktion
viel einfacher implementieren:
#+begin_src scheme
(define (rezi-and-add n)
(mmap 1+ (div 1 n)))
#+end_src
Diese zweite verbesserte Version hat die selbe Funktionalitaet
ohne, dass wir uns explizit um den Fehlerfall kuemmern muessen.
Implementieren wir nun die interne Repraesentation unseres neuen
Datentyps.
#+caption: =<<definition-maybe>>=
#+name: definition-maybe
#+begin_src scheme
(define (just val)
(cons 'just
val))
(define nothing
(cons 'nothing #nil))
(define (just? mval)
(equal? (car mval) 'just))
(define (nothing? mval)
(equal? (car mval) 'nothing))
(define (maybe alt mval)
(if (just? mval)
(cdr mval)
alt))
(define (from-just mval)
(if (just? mval)
(cdr mval)
(error "FROM-JUST: cannot extract value from nothing" mval)))
(define (mmap fun mval)
(if (just? mval)
(just (fun (from-just mval)))
nothing))
#+end_src
Wir koennen nun eine kleine Functorbibliothek erstellen. Ziel ist
es, eine =fmap=-Funktion zu schreiben, die auf Listen, Maybewerten,
Baeumen und eventuell noch anderen Datenstrukturen operieren kann.
* Data directed programming
Stellen wir uns vor, dass wir eine Bibliothek schreiben wollen, die
ein einheitliches Interface fuer den Umgang mit Baeumen, Listen und
Doppellisten ermoeglichen soll. Wir koennen zum Beispiel fuer alle
drei Datentypen =map= und =foldl= definieren. Falls wir es
schaffen, eine einzige =foldl= Funktion zu schreiben, bekommen wir
automatisch eine =length=-Funktion "for free". Um dies zu
bewerkstelligen, muessen wir irgendwie entscheiden koennen, mit
welchem Datentyp wir es zu tun haben. Dazu nutzen "type tags".
** Tagged data
#+caption: =<<definition-type-tags>>=
#+name: definition-type-tags
#+begin_src scheme
(define (attach-tag type-tag value)
(cons type-tag value))
(define (get-tagged-type val)
(cond ((pair? val) (car val))
(else (error "GET-TAGGED-TYPE: not a tagged value" val))))
(define (get-tagged-value val)
(cond ((pair? val) (cdr val))
(else (error "GET-TAGGED-VALUE: not a tagged value" val))))
#+end_src
Jetzt muessen wir neue Konstruktoren fuer unsere Datentypen
definieren, die unsere type tags benutzen.
#+caption: =<<definition-tagged-list>>=
#+name: definition-tagged-list
#+begin_src scheme
(define (get-list-internal xs)
(if (eq? (get-tagged-type xs) 'list)
(get-tagged-value xs)
(error "GET-LIST-INTERNAL: value is not a list" xs)))
(define (tail xs)
(cdr (get-list-internal xs)))
(define (head xs)
(car (get-list-internal xs)))
(define empty-list
(attach-tag 'list #nil))
(define (empty? l)
(null? (get-list-internal l)))
(define (prepend x xs)
(attach-tag 'list
(cons x
xs)))
(define (init xs)
(cond ((empty? xs) (error "INIT: list is empty" xs))
((empty? (tail xs)) empty-list)
(else (prepend (head xs)
(init (tail xs))))))
(define (last xs)
(cond ((empty? xs) (error "LAST: list is empty" xs))
((empty? (tail xs)) (head xs))
(else (last (tail xs)))))
(define (reverse xs)
(define (iter accu rest)
(cond ((empty? rest) accu)
(else (iter (prepend (head rest) accu)
(tail rest)))))
(iter empty-list xs))
(define (tagged-list . xs)
(define (iter ls)
(cond ((null? ls) empty-list)
(else (prepend (car ls) (iter (cdr ls))))))
(iter xs))
#+end_src
#+caption: =<<definition-tagged-tree>>=
#+name: definition-tagged-tree
#+begin_src scheme
(define leaf
(attach-tag 'tree 'leaf))
(define (branch value left right)
(attach-tag 'tree
(list value left right)))
(define (get-tree-internal t)
(if (eq? (get-tagged-type t) 'tree)
(get-tagged-value t)
(error "GET-LEAF-INTERNAL: value is not a tree" t)))
(define (leaf? t)
(eq? t leaf))
(define (branch? t)
(not (leaf? t)))
(define (left t)
(if (branch? t)
(cadr (get-tree-internal t))
(error "LEFT: tree is a leaf" t)))
(define (right t)
(if (branch? t)
(caddr (get-tree-internal t))
(error "RIGHT: tree is a leaf" t)))
(define (branch-value t)
(if (branch? t)
(car (get-tree-internal t))
(error "BRANCH-VALUE: tree is a leaf" t)))
(define (tree-add-elem smaller-or-equal t x)
(if (leaf? t)
(branch x leaf leaf)
(if (smaller-or-equal x (branch-value t))
(branch (branch-value t)
(tree-add-elem smaller-or-equal
(left t)
x)
(right t))
(branch (branch-value t)
(left t)
(tree-add-elem smaller-or-equal
(right t)
x)))))
(define (tree-from-list smaller-or-equal xs)
(define (iter accu rest)
(if (empty? rest)
accu
(iter (tree-add-elem smaller-or-equal accu (head rest))
(tail rest))))
(iter leaf xs))
#+end_src
#+caption: =<<definition-tagged-double-list>>=
#+name: definition-tagged-double-list
#+begin_src scheme
(define empty-dlist
(attach-tag 'dlist
(list empty-list empty-list)))
(define (get-dlist-internal dl)
(if (eq? (get-tagged-type dl) 'dlist)
(get-tagged-value dl)
(error "GET-DLIST-INTERNAL: value is not a double list" dl)))
(define (dprepend val dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(attach-tag 'dlist
(list (prepend val front)
back))))
(define (dappend dl val)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(attach-tag 'dlist
(list front
(prepend val back)))))
(define (dtail dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(cond ((not (empty? front)) (attach-tag 'dlist
(cons (tail front)
back)))
(else (attach-tag 'dlist
(cons (tail (reverse back))
empty-list))))))
(define (dinit dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(cond ((not (empty? back)) (attach-tag 'dlist
(cons front
(tail back))))
(else (attach-tag 'dlist
(cons empty-list
(tail (reverse front))))))))
(define (dhead dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(cond ((not (empty? front)) (head front))
(else (last back)))))
(define (dlast dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(cond ((not (empty? back)) (head back))
(else (last front)))))
#+end_src
Nun koennen wir eine entsprechende =map=-Funktion definieren.
#+begin_src scheme :noweb no-export :tangle examples/map-03.scm
<<definition-type-tags>>
<<definition-tagged-list>>
<<definition-tagged-tree>>
<<definition-tagged-double-list>>
(define (map fun container)
(define (lmap fun l)
(if (empty? l)
empty-list
(prepend (fun (head l))
(lmap fun (tail l)))))
(define (tmap fun t)
(if (leaf? t)
leaf
(branch (fun (branch-value t))
(tmap fun (left t))
(tmap fun (right t)))))
(define (dmap fun dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(attach-tag 'dlist
(cons (lmap fun front)
(lmap fun back)))))
(cond ((eq? (get-tagged-type container) 'list) (lmap fun container))
((eq? (get-tagged-type container) 'tree) (tmap fun container))
((eq? (get-tagged-type container) 'dlist) (dmap fun container))
(else (error "MAP: unknown type" container))))
#+end_src
Diese Definition ermoeglicht nun ein einheitliches "Interface" fuer
alle Datentypen, die die =map=-Operation unterstuetzen. Ein
entscheidender Nachteil ist dabei jedoch, dass wir jedesmal, wenn
wir einen neuen Datentypen hinzufuegen wollen, die =map=-Funktion
selbst editieren muessen. Das fuehrt dazu, dass unser kleines Modul
nicht gut erweitert werden kann.
** Type lookup tables
Ein modularerer Ansatz fuer "ueberladene Funktionen" ist das
Benutzen einer Tabelle, in der hinterlegt ist, welche Funktion fuer
welchen Typen benutzt werden soll. Wir koennen uns eine Tabelle
folgendermassen vorstellen:
| Operation | list | tree | dlist |
|-----------+----------+----------+----------|
| map | =lmap= | =tmap= | =dmap= |
| foldl | =lfoldl= | =tfoldl= | =dfoldl= |
Wenn ein neuer Datentype geschrieben wird, der =map= unterstuetzt,
muss er einfach nur der Tabelle hinzugefuegt werden.
#+caption: =examples/map-04.scm=
#+begin_src scheme :tangle examples/map-04.scm :noweb no-export
<<definition-type-tags>>
<<definition-tagged-list>>
<<definition-tagged-tree>>
<<definition-tagged-double-list>>
(define map-lookup-table (make-hash-table))
(define (map fun container)
(let ((op (hashq-ref map-lookup-table
(get-tagged-type container))))
(if op
(op fun container)
(error "MAP: not defined for this type"
(get-tagged-type container)))))
(define (mmap fun l)
(if (empty? l)
empty-list
(prepend (fun (head l)) (mmap fun (tail l)))))
(define (tmap fun t)
(if (leaf? t)
leaf
(branch (fun (branch-value t))
(tmap fun (left t))
(tmap fun (right t)))))
(define (dmap fun dl)
(let* ((intern (get-dlist-internal dl))
(front (car intern))
(back (cadr intern)))
(attach-tag 'dlist
(list (map fun front)
(map fun back)))))
(hashq-set! map-lookup-table 'list mmap)
(hashq-set! map-lookup-table 'dlist dmap)
(hashq-set! map-lookup-table 'tree tmap)
#+end_src
** Veraenderliche Daten
Im obigen Programmbeispiel wird der Befehl =hashq-set!= verwendet.
Dieser Befehl nimmt eine Hashmap entgegen und nimmt ein Update vor.
Hier sollten wir ein wenig stutzig werden, da bisher einmal
erzeugte Daten nicht mehr veraendert werden konnten, mit anderen
Worten: =hashq-set!= ist eine Zuweisungsanweisung.
Wir sollten uns darueber im Klaren sein, sein, dass wir sehr
vorsichtig sein muessen, wenn wir veraenderliche Daten haben, da
viele nette Eigenschaften funktionaler Programmierung verloren
gehen, wenn wir Zuweisungen erlauben. Darunter faellt unter
anderem, dass wir nicht mehr davon ausgehen koennen, dass eine
Funktion, aufgerufen mit bestimmten Argumenten, immer das selbe
Ergebnis produziert, egal wo sie im Programm aufgerufen wird.
Im weiteren Verlauf dieses Textes werden wir Zuweisungen nur fuer 2
Dingen verwenden: "data directed programming" und "memoization"
(das werden wir noch kennenlernen). Dies wird den imperativen
Einfluss auf unsere Programme gering halten, aber trotzdem flexibel
genug sein, um spannende Konzepte kennenzulernen.
** GOOPS
Guile Scheme stellt bereits eine Infrastruktur fuer genau solche
Anwendungsfaelle bereit, GOOPS. Der Name leitet sich (irgendwie) von
objektorientierter Programmierung ab.
Wenn wir GOOPS benutzen wollen, muessen wir es ersteinmal importieren:
#+name: goops-example-imports
#+caption: =<<goops-example-imports>>=
#+begin_src scheme
(use-modules (oop goops))
#+end_src
Um jetzt unser Funktorinterface zu implementieren, muessen wir eine
generische Funktion definieren, die die "mapping"-Funktion repraesentiert
#+name: goops-example-defs
#+caption: =<<goops-example-defs>>=
#+begin_src scheme
(define-generic fmap)
#+end_src
Nun erstellen wir eine Klasse, wobei Klasse hier sehr nah dran ist, an der
Javadefinition fuer Klasse.
#+name: goops-example-maybe-def
#+caption: =<<goops-example-maybe-def>>=
#+begin_src scheme
(define-class <maybe> ()
(is-set #:getter maybe-is-set
#:init-keyword #:is-set)
(value #:getter maybe-value
#:init-keyword #:value))
#+end_src
Wir wollen nun Konstruktoren definieren. Der primitive
Konstruktor, den jede Klasses von Hause aus besitzt, ist =make=.
Wir wollen aber eigene Konstruktoren definieren.
#+name: goops-example-maybe-methods
#+caption: =<<goops-example-maybe-methods>>=
#+begin_src scheme
(define (make-just val)
(make <maybe> #:is-set #t #:value val))
(define nothing
(make <maybe> #:is-set #f #:value #f))
#+end_src
Wir haben jetzt die Datenfelder und die Konstruktoren von =<maybe>=
definiert, aber noch keine Methoden. Anders als in Java und
Co. werden Methoden nicht als Attribute an Klassen angehaengt,
sonder ueber Funktionssignatur deklariert.
#+name: goops-example-maybe-methods
#+caption: =+<<goops-example-maybe-methods>>=
#+begin_src scheme
(define-method (is-just (x <maybe>))
(maybe-is-set x))
#+end_src
Die =is-nothing=-Funktion ist trivial, da wir einfach nur das
Ergebnis von =is-just= negieren muessen.
#+name: goops-example-maybe-methods
#+caption: =+<<goops-example-maybe-methods>>=
#+begin_src scheme
(define-method (is-nothing (x <maybe>))
(not (is-just x)))
#+end_src
Unsere =fmap=-Funktion wird nun auch als Methode von =<maybe>=
implementiert.
#+name: goops-example-maybe-methods
#+caption: =+<<goops-example-maybe-methods>>=
#+begin_src scheme
(define-method (fmap fun (x <maybe>))
(if (maybe-is-set x)
(make-just (fun (maybe-value x)))
x))
#+end_src
Nun muessen wir nur noch alles zu einem Modul zusammenfassen:
#+begin_src scheme :noweb yes :tangle examples/goops-example.scm
<<goops-example-imports>>
<<goops-example-defs>>
<<goops-example-maybe-def>>
<<goops-example-maybe-methods>>
#+end_src
*** Objektorientierung
Objektorientierung ist eine Spezialform von data directed
programming. Die Methoden, fuer ein Datensatz ein gueltiges
Argument darstellen, werden oft Objektmethoden genannt. In den
meisten objektorientierten Sprachen werden diese Methoden so
definiert, dass ein Parameter der Methode das Objekt ist, "auf das
die Methode angewendet" werden soll. In Python geschieht das
durch das Argument "self", in Java und Javascript ist es "this".
Aus dieser Notation ergeben sich haeufig seltsame Streitigkeiten,
welche Method zu welcher Objektklasse gehoert.
Nehmen wir einmal an, wir haben eine Klasse =Form=, die
geometrische Formen abbildet und eine Klasse =Leinwand=, auf die
diese Formen "gemalt" werden koennen. Ist die Methode "male Form
auf Leinwand" nun eine Methode von =Form=, die als Argument eine
Leinwand entgegen nimmt, oder eine Methode der Klasse =Leinwand=,
die eine Form entgegen nimmt. Man findet Vertreter der eine, als
auch der anderen Interpretation.
Dieses Problem ist ein guter Hinweis darauf, die Implementation
von "OO" durch implizite Argumente und "Klassenmethoden", die
als
* Shared variables
Mit der methode =set!= kann der Wert einer Variable veraendert werden.
#+begin_src scheme
(define n 0)
(set! n 1)
(display n)
;; 1
#+end_src
Um die selbe Variable an verschiedenen stellen in unserem Program
veraendern zu koennen, nutzen wir =set!=.
#+begin_src scheme
;; Dieses Programm soll es ermoeglichen, einen zentralen Kontostand zu
;; verwalten
;; Wir benoetigen zwei funktionen: einzahlen und kontostand.
(define deposit #f) ;; einzahlen
(define get-balance #f) ;; kontostand
(let ((balance 0)) ;; der kontostand wird mit 0 initialisiert
(set! get-balance
(lambda () balance))
(set! desposit
(lambda (amount)
(set! balance (+ balance amount)))))
(get-balance) ;; 0
(deposit 5)
(get-balance) ;; 5
(deposit 10)
(get-balance) ;; 15
#+end_src
* Beispiel: web server
Wie die meisten modernen Sprachen, stellt GNU Guile eine
Webserverimplementation bereit, mit der beliebige Daten zwischen
Client und Server ausgetauscht werden koennen. Der entsprechende Code ist im Modul
=web server= zu finden.
#+caption: =<<server-imports>>=
#+name: server-imports
#+begin_src scheme
(use-modules (web server))
#+end_src
Das Modul stellt eine Funktion =run-server= bereit, die den
Webserver startet. =run-server= nimmt ein Pflichtargument entgegen,
welches eine Funktion ist, wir nennen sie =handler=.
#+caption: =<<server-run>>=
#+name: server-run
#+begin_src scheme
(run-server handler)
#+end_src
Die =handler=-Funktion bearbeitet eingehende Requests und
konstruiert (gueltige) Responses. Sie muss zwei Parameter
verstehen, wobei der erste Parameter den Request Header und der
zweite Parameter den Request body repraesentiert.
#+caption: =<<server-handler-def>>=
#+name: server-handler-def
#+begin_src scheme
(define (handler request-header request-body)
<<server-handler-implementation>>)
#+end_src
Um dem Interface des Webservers zu genuegen, muss die Funktion
ausserdem zwei Argumente zurueck geben, nicht zu verwechseln mit
einem Paar von zwei Werten. Guile Scheme stellt die moeglichkeit
bereit, dass eine Funktion mehrere Rueckgabewerte hat, dies wird
ueber die =values=-Funktion realisiert, die zu den Grundfunktionen
der Sprache gehoert. Der erste Rueckgabewert repraesentiert den
Response Header und der zweite Rueckgabewert den Response Body.
#+caption: =<<server-handler-implementation>>=
#+name: server-handler-implementation
#+begin_src scheme
(values
<<server-response-header-implementation>>
<<server-response-body-implementation>>)
#+end_src
Um den Response Header zu konstruieren, kann die
=build-response=-Funktion aus dem Modul =web response= genutzt
werden.
#+caption: =<<server-imports>>=
#+name: server-imports
#+begin_src scheme
(use-modules (web response))
#+end_src
#+caption: =<<server-response-header-implementation>>=
#+name: server-response-header-implementation
#+begin_src scheme
(build-response #:code 200
#:headers `((content-type . (text/html))))
#+end_src
Die Implementation fuer den Response Body muss eine Funktion mit
einem Argument sein. Dieses Argument ist ein sogenannter Port, auf
den die Ausgabe geschrieben werden muss. Dazu kann zum Beispiel die
=display=-Funktion benutzt werden.
#+caption: =<<server-response-body-implementation>>=
#+name: server-response-body-implementation
#+begin_src scheme
(lambda (port)
(display "hello world" port))
#+end_src
Das vollstaendige Programm besteht dann aus den oben definierten
Bestandteilen.
#+begin_src scheme :noweb no-export :tangle examples/server1.scm :shebang "#!/usr/bin/env guile !#"
<<server-imports>>
<<server-handler-def>>
<<server-run>>
#+end_src
Wenn das Programm =examples/server1.scm= ausgefuehrt wird, wird ein
Webserver auf Port 8080 gestartet. Bei einem Request liefert der
Server ein HTML-Dokumen aus, in dem "hello world" steht.
* Quoting
LISP eignet sich sehr gut fuer sogenanntes "meta programming", damit
bezeichnet man das Schreiben und Manipulieren von Computerprogrammen
durch Programme. Jeder gueltige LISP-Ausdruck laesst sich als Liste
darstellen.
#+begin_src scheme :exports both
(define exp (list '+ 1 2))
(eval exp (interaction-environment))
#+end_src
Zum evaluieren des Codes muss der Interpreter wissen, in welcher
Umgebung er den Code ausfuehren soll. Die Funktion
=interaction-environment= definiert eine Umgebung aehnlich zu der
Umgebung in der REPL.
* GNU Free Documentation License
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
<http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
** PREAMBLE
The purpose of this License is to make a manual, textbook, or other
functional and useful document "free" in the sense of freedom: to
assure everyone the effective freedom to copy and redistribute it,
with or without modifying it, either commercially or noncommercially.
Secondarily, this License preserves for the author and publisher a way
to get credit for their work, while not being considered responsible
for modifications made by others.
This License is a kind of "copyleft", which means that derivative
works of the document must themselves be free in the same sense. It
complements the GNU General Public License, which is a copyleft
license designed for free software.
We have designed this License in order to use it for manuals for free
software, because free software needs free documentation: a free
program should come with manuals providing the same freedoms that the
software does. But this License is not limited to software manuals;
it can be used for any textual work, regardless of subject matter or
whether it is published as a printed book. We recommend this License
principally for works whose purpose is instruction or reference.
** APPLICABILITY AND DEFINITIONS
This License applies to any manual or other work, in any medium, that
contains a notice placed by the copyright holder saying it can be
distributed under the terms of this License. Such a notice grants a
world-wide, royalty-free license, unlimited in duration, to use that
work under the conditions stated herein. The "Document", below,
refers to any such manual or work. Any member of the public is a
licensee, and is addressed as "you". You accept the license if you
copy, modify or distribute the work in a way requiring permission
under copyright law.
A "Modified Version" of the Document means any work containing the
Document or a portion of it, either copied verbatim, or with
modifications and/or translated into another language.
A "Secondary Section" is a named appendix or a front-matter section of
the Document that deals exclusively with the relationship of the
publishers or authors of the Document to the Document's overall
subject (or to related matters) and contains nothing that could fall
directly within that overall subject. (Thus, if the Document is in
part a textbook of mathematics, a Secondary Section may not explain
any mathematics.) The relationship could be a matter of historical
connection with the subject or with related matters, or of legal,
commercial, philosophical, ethical or political position regarding
them.
The "Invariant Sections" are certain Secondary Sections whose titles
are designated, as being those of Invariant Sections, in the notice
that says that the Document is released under this License. If a
section does not fit the above definition of Secondary then it is not
allowed to be designated as Invariant. The Document may contain zero
Invariant Sections. If the Document does not identify any Invariant
Sections then there are none.
The "Cover Texts" are certain short passages of text that are listed,
as Front-Cover Texts or Back-Cover Texts, in the notice that says that
the Document is released under this License. A Front-Cover Text may
be at most 5 words, and a Back-Cover Text may be at most 25 words.
A "Transparent" copy of the Document means a machine-readable copy,
represented in a format whose specification is available to the
general public, that is suitable for revising the document
straightforwardly with generic text editors or (for images composed of
pixels) generic paint programs or (for drawings) some widely available
drawing editor, and that is suitable for input to text formatters or
for automatic translation to a variety of formats suitable for input
to text formatters. A copy made in an otherwise Transparent file
format whose markup, or absence of markup, has been arranged to thwart
or discourage subsequent modification by readers is not Transparent.
An image format is not Transparent if used for any substantial amount
of text. A copy that is not "Transparent" is called "Opaque".
Examples of suitable formats for Transparent copies include plain
ASCII without markup, Texinfo input format, LaTeX input format, SGML
or XML using a publicly available DTD, and standard-conforming simple
HTML, PostScript or PDF designed for human modification. Examples of
transparent image formats include PNG, XCF and JPG. Opaque formats
include proprietary formats that can be read and edited only by
proprietary word processors, SGML or XML for which the DTD and/or
processing tools are not generally available, and the
machine-generated HTML, PostScript or PDF produced by some word
processors for output purposes only.
The "Title Page" means, for a printed book, the title page itself,
plus such following pages as are needed to hold, legibly, the material
this License requires to appear in the title page. For works in
formats which do not have any title page as such, "Title Page" means
the text near the most prominent appearance of the work's title,
preceding the beginning of the body of the text.
The "publisher" means any person or entity that distributes copies of
the Document to the public.
A section "Entitled XYZ" means a named subunit of the Document whose
title either is precisely XYZ or contains XYZ in parentheses following
text that translates XYZ in another language. (Here XYZ stands for a
specific section name mentioned below, such as "Acknowledgements",
"Dedications", "Endorsements", or "History".) To "Preserve the Title"
of such a section when you modify the Document means that it remains a
section "Entitled XYZ" according to this definition.
The Document may include Warranty Disclaimers next to the notice which
states that this License applies to the Document. These Warranty
Disclaimers are considered to be included by reference in this
License, but only as regards disclaiming warranties: any other
implication that these Warranty Disclaimers may have is void and has
no effect on the meaning of this License.
** VERBATIM COPYING
You may copy and distribute the Document in any medium, either
commercially or noncommercially, provided that this License, the
copyright notices, and the license notice saying this License applies
to the Document are reproduced in all copies, and that you add no
other conditions whatsoever to those of this License. You may not use
technical measures to obstruct or control the reading or further
copying of the copies you make or distribute. However, you may accept
compensation in exchange for copies. If you distribute a large enough
number of copies you must also follow the conditions in section 3.
You may also lend copies, under the same conditions stated above, and
you may publicly display copies.
** COPYING IN QUANTITY
If you publish printed copies (or copies in media that commonly have
printed covers) of the Document, numbering more than 100, and the
Document's license notice requires Cover Texts, you must enclose the
copies in covers that carry, clearly and legibly, all these Cover
Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on
the back cover. Both covers must also clearly and legibly identify
you as the publisher of these copies. The front cover must present
the full title with all words of the title equally prominent and
visible. You may add other material on the covers in addition.
Copying with changes limited to the covers, as long as they preserve
the title of the Document and satisfy these conditions, can be treated
as verbatim copying in other respects.
If the required texts for either cover are too voluminous to fit
legibly, you should put the first ones listed (as many as fit
reasonably) on the actual cover, and continue the rest onto adjacent
pages.
If you publish or distribute Opaque copies of the Document numbering
more than 100, you must either include a machine-readable Transparent
copy along with each Opaque copy, or state in or with each Opaque copy
a computer-network location from which the general network-using
public has access to download using public-standard network protocols
a complete Transparent copy of the Document, free of added material.
If you use the latter option, you must take reasonably prudent steps,
when you begin distribution of Opaque copies in quantity, to ensure
that this Transparent copy will remain thus accessible at the stated
location until at least one year after the last time you distribute an
Opaque copy (directly or through your agents or retailers) of that
edition to the public.
It is requested, but not required, that you contact the authors of the
Document well before redistributing any large number of copies, to
give them a chance to provide you with an updated version of the
Document.
** MODIFICATIONS
You may copy and distribute a Modified Version of the Document under
the conditions of sections 2 and 3 above, provided that you release
the Modified Version under precisely this License, with the Modified
Version filling the role of the Document, thus licensing distribution
and modification of the Modified Version to whoever possesses a copy
of it. In addition, you must do these things in the Modified Version:
* A. :: Use in the Title Page (and on the covers, if any) a title
distinct from that of the Document, and from those of
previous versions (which should, if there were any, be
listed in the History section of the Document). You may
use the same title as a previous version if the original
publisher of that version gives permission.
* B. :: List on the Title Page, as authors, one or more persons or
entities responsible for authorship of the modifications in
the Modified Version, together with at least five of the
principal authors of the Document (all of its principal
authors, if it has fewer than five), unless they release
you from this requirement.
* C. :: State on the Title page the name of the publisher of the
Modified Version, as the publisher.
* D. :: Preserve all the copyright notices of the Document.
* E. :: Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.
* F. :: Include, immediately after the copyright notices, a license
notice giving the public permission to use the Modified
Version under the terms of this License, in the form shown
in the Addendum below.
* G. :: Preserve in that license notice the full lists of Invariant
Sections and required Cover Texts given in the Document's
license notice.
* H. :: Include an unaltered copy of this License.
* I. :: Preserve the section Entitled "History", Preserve its
Title, and add to it an item stating at least the title,
year, new authors, and publisher of the Modified Version as
given on the Title Page. If there is no section Entitled
"History" in the Document, create one stating the title,
year, authors, and publisher of the Document as given on
its Title Page, then add an item describing the Modified
Version as stated in the previous sentence.
* J. :: Preserve the network location, if any, given in the
Document for public access to a Transparent copy of the
Document, and likewise the network locations given in the
Document for previous versions it was based on. These may
be placed in the "History" section. You may omit a network
location for a work that was published at least four years
before the Document itself, or if the original publisher of
the version it refers to gives permission.
* K. :: For any section Entitled "Acknowledgements" or
"Dedications", Preserve the Title of the section, and
preserve in the section all the substance and tone of each
of the contributor acknowledgements and/or dedications
given therein.
* L. :: Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section
numbers or the equivalent are not considered part of the
section titles.
* M. :: Delete any section Entitled "Endorsements". Such a section
may not be included in the Modified Version.
* N. :: Do not retitle any existing section to be Entitled
"Endorsements" or to conflict in title with any Invariant
Section.
* O. :: Preserve any Warranty Disclaimers.
If the Modified Version includes new front-matter sections or
appendices that qualify as Secondary Sections and contain no material
copied from the Document, you may at your option designate some or all
of these sections as invariant. To do this, add their titles to the
list of Invariant Sections in the Modified Version's license notice.
These titles must be distinct from any other section titles.
You may add a section Entitled "Endorsements", provided it contains
nothing but endorsements of your Modified Version by various
parties--for example, statements of peer review or that the text has
been approved by an organization as the authoritative definition of a
standard.
You may add a passage of up to five words as a Front-Cover Text, and a
passage of up to 25 words as a Back-Cover Text, to the end of the list
of Cover Texts in the Modified Version. Only one passage of
Front-Cover Text and one of Back-Cover Text may be added by (or
through arrangements made by) any one entity. If the Document already
includes a cover text for the same cover, previously added by you or
by arrangement made by the same entity you are acting on behalf of,
you may not add another; but you may replace the old one, on explicit
permission from the previous publisher that added the old one.
The author(s) and publisher(s) of the Document do not by this License
give permission to use their names for publicity for or to assert or
imply endorsement of any Modified Version.
** COMBINING DOCUMENTS
You may combine the Document with other documents released under this
License, under the terms defined in section 4 above for modified
versions, provided that you include in the combination all of the
Invariant Sections of all of the original documents, unmodified, and
list them all as Invariant Sections of your combined work in its
license notice, and that you preserve all their Warranty Disclaimers.
The combined work need only contain one copy of this License, and
multiple identical Invariant Sections may be replaced with a single
copy. If there are multiple Invariant Sections with the same name but
different contents, make the title of each such section unique by
adding at the end of it, in parentheses, the name of the original
author or publisher of that section if known, or else a unique number.
Make the same adjustment to the section titles in the list of
Invariant Sections in the license notice of the combined work.
In the combination, you must combine any sections Entitled "History"
in the various original documents, forming one section Entitled
"History"; likewise combine any sections Entitled "Acknowledgements",
and any sections Entitled "Dedications". You must delete all sections
Entitled "Endorsements".
** COLLECTIONS OF DOCUMENTS
You may make a collection consisting of the Document and other
documents released under this License, and replace the individual
copies of this License in the various documents with a single copy
that is included in the collection, provided that you follow the rules
of this License for verbatim copying of each of the documents in all
other respects.
You may extract a single document from such a collection, and
distribute it individually under this License, provided you insert a
copy of this License into the extracted document, and follow this
License in all other respects regarding verbatim copying of that
document.
** AGGREGATION WITH INDEPENDENT WORKS
A compilation of the Document or its derivatives with other separate
and independent documents or works, in or on a volume of a storage or
distribution medium, is called an "aggregate" if the copyright
resulting from the compilation is not used to limit the legal rights
of the compilation's users beyond what the individual works permit.
When the Document is included in an aggregate, this License does not
apply to the other works in the aggregate which are not themselves
derivative works of the Document.
If the Cover Text requirement of section 3 is applicable to these
copies of the Document, then if the Document is less than one half of
the entire aggregate, the Document's Cover Texts may be placed on
covers that bracket the Document within the aggregate, or the
electronic equivalent of covers if the Document is in electronic form.
Otherwise they must appear on printed covers that bracket the whole
aggregate.
** TRANSLATION
Translation is considered a kind of modification, so you may
distribute translations of the Document under the terms of section 4.
Replacing Invariant Sections with translations requires special
permission from their copyright holders, but you may include
translations of some or all Invariant Sections in addition to the
original versions of these Invariant Sections. You may include a
translation of this License, and all the license notices in the
Document, and any Warranty Disclaimers, provided that you also include
the original English version of this License and the original versions
of those notices and disclaimers. In case of a disagreement between
the translation and the original version of this License or a notice
or disclaimer, the original version will prevail.
If a section in the Document is Entitled "Acknowledgements",
"Dedications", or "History", the requirement (section 4) to Preserve
its Title (section 1) will typically require changing the actual
title.
** TERMINATION
You may not copy, modify, sublicense, or distribute the Document
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense, or distribute it is void, and
will automatically terminate your rights under this License.
However, if you cease all violation of this License, then your license
from a particular copyright holder is reinstated (a) provisionally,
unless and until the copyright holder explicitly and finally
terminates your license, and (b) permanently, if the copyright holder
fails to notify you of the violation by some reasonable means prior to
60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, receipt of a copy of some or all of the same material does
not give you any rights to use it.
** FUTURE REVISIONS OF THIS LICENSE
The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in
detail to address new problems or concerns. See
[[http://www.gnu.org/copyleft/]].
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
License "or any later version" applies to it, you have the option of
following the terms and conditions either of that specified version or
of any later version that has been published (not as a draft) by the
Free Software Foundation. If the Document does not specify a version
number of this License, you may choose any version ever published (not
as a draft) by the Free Software Foundation. If the Document
specifies that a proxy can decide which future versions of this
License can be used, that proxy's public statement of acceptance of a
version permanently authorizes you to choose that version for the
Document.
** RELICENSING
"Massive Multiauthor Collaboration Site" (or "MMC Site") means any
World Wide Web server that publishes copyrightable works and also
provides prominent facilities for anybody to edit those works. A
public wiki that anybody can edit is an example of such a server. A
"Massive Multiauthor Collaboration" (or "MMC") contained in the site
means any set of copyrightable works thus published on the MMC site.
"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
license published by Creative Commons Corporation, a not-for-profit
corporation with a principal place of business in San Francisco,
California, as well as future copyleft versions of that license
published by that same organization.
"Incorporate" means to publish or republish a Document, in whole or in
part, as part of another Document.
An MMC is "eligible for relicensing" if it is licensed under this
License, and if all works that were first published under this License
somewhere other than this MMC, and subsequently incorporated in whole or
in part into the MMC, (1) had no cover texts or invariant sections, and
(2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site
under CC-BY-SA on the same site at any time before August 1, 2009,
provided the MMC is eligible for relicensing.
** ADDENDUM: How to use this License for your documents
To use this License in a document you have written, include a copy of
the License in the document and put the following copyright and
license notices just after the title page:
#+begin_example
Copyright (c) YEAR YOUR NAME.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3
or any later version published by the Free Software Foundation;
with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
A copy of the license is included in the section entitled "GNU
Free Documentation License".
#+end_example
If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts,
replace the "with...Texts." line with this:
#+begin_example
with the Invariant Sections being LIST THEIR TITLES, with the
Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST.
#+end_example
If you have Invariant Sections without Cover Texts, or some other
combination of the three, merge those two alternatives to suit the
situation.
If your document contains nontrivial examples of program code, we
recommend releasing these examples in parallel under your choice of
free software license, such as the GNU General Public License,
to permit their use in free software.