Haskell hat mich eigentlich schon immer interessiert. Aber zu meinen Windows Zeiten gab es keine wirlich gut funktionierende Umgebung um mit Haskell zu experimentieren. Das hat sich, zu Glück, geändert. Mit WinGHCi hat man, wie ich finde, eine komfortable Plattform um so richtig durch zu starten.
Auf der Seite http://hackage.haskell.org/platform/windows.html kann man sich den aktuellen Installer für Windows runter laden.
Unter Linux lade ich mir den Compiler mittels apt-get install oder der Synaptic-Paketverwaltung auf meinen Rechner.
In der Shell gebe ich „ghci“ ein und es erscheint folgender Ausdruck:
GHCi, version 6.10.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude>
Da mir „Prelude“ ein wenig zu lang ist wechsel ich mit
Prelude> :set prompt "ghci> "
ghci>
Wenn ich ein Script mit dem Namen „meinScript.hs“ ( Haskell Dateinamen müssen immer mit einem kleinen Buchstaben beginnen!) abgespeichert habe kann ich es im Compiler mit
ghci> :load meinScript.hs
oder kürzer
ghci> :l meinScript
aufrufen.
Wenn ich das Script geändert habe muss ich noch einmal
ghci> :l meinScript
oder
ghci> :r (für reload)
eingeben.
Verlassen kann ich den Compiler mit:
ghci> :quit (oder einfach :q)
Neben den ganz normale Rechenoperationen die in Haskell genau wie in anderen Sprachen möglich sind, wie z. B.:
ghci> 4 + 4
8
ghci> (+) 4 4
8
ghci> (*) 4 4
16
ghci> 6 /= 5
True
das Ungleich-Zeichen ist bei Haskell also /=
Hier ein paar umfangreichere Beispiele:
ghci> [x*2 | x <- [1..5]]
[2,4,6,8,10]
ghci>
Ich kann die Funktion wie in der Mathematik üblich lesen und auch so interpretieren.
Wobei dem senkrechten Strich ( | = "pipe"), in Haskell "guard" genannt, die Funktion einer if-Schleife gleichgestellt ist.
ghci> [x*y | x <- [1..5], y <- [1..10], x `mod`2 == 0]
[2,4,6,8,10,12,14,16,18,20,4,8,12,16,20,24,28,32,36,40]
Ich berechne x mal y für die gilt: x ist Element der List [ 1 bis 5 ] und y ist Element der List [ 1 bis 10 ] mit der Nebenbedingung, dass nur x-Werte genommen werden die ohne Rest durch 2 teilbar sind.
(Bei der Deklaration von mod ist darauf zu achten, dass ich diese in back-ticks setze!)
Weitere Beispiele zum Berechnen von Listen.
Hinzuzählen von Elementen zu einer bereits vorhandenen Liste:
ghci> [1,2,3,4] ++ [5,6]
[1,2,3,4,5,6]
Mit "zip" kann man zwei Listenelemente immer paarweise zuordnen:
ghci> zip [1,2,3,4,5] [9,9,9,9,9]
[(1,9),(2,9),(3,9),(4,9),(5,9)]
oder auch:
ghci> zip [1..] ["beef", "tuna", "salmon", "racecars"]
[(1,"beef"),(2,"tuna"),(3,"salmon"),(4,"racecars")]
Als Nächstes möchte ich mir die Seitenlängen eines rechtwinkligen Dreiecks ausgeben lassen, bei dem die Seiten nicht länger als 10 LE sein sollen und die Summe Derselben 24 ergibt.
ghci> let dreiecke = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
ghci> dreiecke
[(6,8,10)]
Ich möchte mir gern die Summe der Listenelemente ausgeben lassen. Dazu stehen mir mehrere Möglichkeiten zur Verfügung.
Ich kann mir ein Script erstellen in dem ich die Summe mittels der "if-then-else" Methode ermitteln lasse. ( Beachte: if steht immer gemeinsam mit then! )
sumInt :: [Int] -> Int (Ich gebe eine Liste des Typs Int ein und erhalte als Rückgabewert ein Int)
sumInt x =
if null x then 0 (wenn eine leere Liste eingegeben wird ist sumInt = 0)
else head x + sumInt (tail x) (hier wird sumInt berechnet)
Oder ich lasse es mit Hilfe von Guards berechnen:
sumInt :: [Int] -> Int
sumInt x
| null x = 0
| otherwise = head x + sumInt (tail x)
Oder ich lasse mir das Ergebniss direkt in der Shell mit:
sum[x]
ausgeben. Wobei x für die einzelnen Listenelemente steht. Z.B. :
ghci > sum [1,2,3]
6
oder auch:
ghci> product [1,2,3]
6
Mehr Beispiele:
Alle Listenelemente werden mit 3 multipliziert:
ghci >map (*3) [1,2,3]
[3,6,9]
Für das gleiche Ergebnis muss ich in OCaml oder F# folgenden Code eingeben:
# let rec mul3 xs = match xs with [] -> [] | y :: ys -> 3*y :: mul3 ys;;
val mul3 : int list -> int list = <fun>
# mul3 [1; 2; 3];;
- : int list = [3; 6; 9]
Haskell mag zwar nicht so schnell sein wie beispielsweise Ocaml oder F#, aber die Zeit spart man definitiv beim Tippen wieder ein!
Die Zahl 3 wird zu den Listenelementen addiert:
ghci >map (+3) [1,2,3]
[4,5,6]
Oder als Exponentialfunktion:
ghci >map (^3) [1,2,3]
[1,8,27]
Anmerkung: Der "head" einer Liste ist immer das erste Element.
ghci > head [1,2,3,4]
1
ghci > head "hello"
h
Der "tail" einer Liste ist dagegen nicht das letzte Element, sondern alle ausser dem "head".
ghci > tail [1,2,3,4]
[2,3,4]
ghci > tail "hello"
"ello"
Als Nächstes erstelle ich mir ein Script in einem Texteditor meiner Wahl,
factorial :: (Integral a) => a -> a
factorial 0 = 1
factorial n = n * factorial (n - 1)
und speicher dieses als z.B. "factorial.hs" in mein Heimatverzeichnis.
Aufruf des Programmes, das die Fakultäten einzelner Zahlen berechnet. Die Fakultät einer Zahl z.B. 3 (geschrieben 3!) bedeutet:
3! = 3*2*1 = 6
4! = 4*3*2*1 = 24 (4*6)
5! = 5*4*3*2*1 = 120 (20*6)
ghci> :l factorial
[1 of 1] Compiling Main ( factorial.hs, interpreted )
Ok, modules loaded: Main.
*Main> factorial 5
120
*Main> :l (bringt mich wieder zurück zu dem prompt)
Ok, modules loaded: none.
ghci>
Oder ich schreibe direkt in die shell:
ghci> let fact n = if n < 2 then 1 else n * fact(n-1)
und erhalte das gleiche Ergebnis:
ghci> fact 5
120
Eine kleine Spielerei von mir um zu zeigen, dass man verschiedene Funktionen gern auch verschachteln kann.
ghci> let fact n = if n < 2 then 1 else n * fact(n-1)
ghci> let add x = x+1
ghci> let verbinde f g x = f(g x)
ghci> verbinde fact add 4
120
Ich habe zuerst die Funktion zur Berechnung von Fakultäten deklariert (hier g). Als zweite Funktion bestimme ich, dass zu dem Eingabewert eins dazu gezählt werden soll ( x= x+1). Die dritte Funktion besagt, dass zwei Funktionen miteinander verbunden werden (f(g),f(x) = f(g, x)).
In diesem Beispiel bedeutet dies, dass hier die "Fakultät" von 4+1 berechnet wird!
Man unterscheidet hier globales "let-binding", z.B.:
let x = 2
und lokales "let-binding", z.B.:
let x = 2 in x*x
Hier ein paar weitere Beispiele für lokales "let-binding" .
ghci >let a = 9 in a + 1
10
ghci >let x = 2 in x + 3
5
ghci >let x = 3 in x * x
9
ghci >let x = 2
ghci >let x = 3 in x * x
9
ghci >x * x
4
(Habt ihr bemerkt, dass x nur den Wert 3 hat wenn "let" in Verbindung mit "in" steht?)
ghci >(let a = 3.0; b = 4.0 in sqrt(a*a+b*b))
5.0
(Hier wir die Quadratwurzel aus- 3.0 mal 3.0 plus 4.0 mal 4.0 -gezogen.)
Ich gebe folgenden Code in meinen Texteditor und speicher das Programm unter einen beliebigen Namen ab.
main = do
putStrLn "Wie ist Ihr Name?"
name <- getLine
putStrLn ("Guten Tag " ++ name ++ ", herzlich willkommen!")
Dann rufe ich mein Script in der Shell auf:
ghci> :l beliebig
[1 of 1] Compiling Main ( beliebig.hs, interpreted )
Ok, modules loaded: Main.
*Main> main
Wie ist Ihr Name?
Horst
GutenTag Horst, herzlich willkommen!
*Main>
Ein I/O Script wird nur dann ausgeführt wenn wir ihm den Namen "main" gegeben haben.
putStrLn = put String Line ( gibt einen String aus und springt in die nächste Zeile.
ghci >putStrLn "Hallo"
Hallo
ghci >putStr "Hallo"
Halloghci >
Ihr bemerkt den Unterschied zwischen putStr und putStrLn ?
name <- getLine liest eine User-Eingabe und speichert diese in der Variablen "name".
Das allseits beliebte Quicksortprogramm unter Haskell:
quicksort :: (Ord a) => [a] -> [a]
quicksort [] = []
quicksort (x:xs) =
let smallerSorted = quicksort [a | a <- xs, a <= x]
biggerSorted = quicksort [a | a <- xs, a > x]
in smallerSorted ++ [x] ++ biggerSorted
Ich starte im Terminal meinen Compiler:
linux% ghci
GHCi, version 6.10.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Lade das Programm:
ghci> :l quicksort
und laß das Programm laufen:
*Main> quicksort [1,6,2,8,3,9,4,5]
[1,2,3,4,5,6,8,9]
*Main>