Haskell/Zmienne i funkcje

Z Wikibooks, biblioteki wolnych podręczników.

[edytuj] Zmienne

Interpreter GHCi pozwala na wykonywanie operacji arytmetycznych poprzez bezpośrednie wpisanie ich w linii poleceń interpretera. Na przykład można obliczyć pole koła o promieniu 5 w następujący sposób:

Prelude> 3.14159265358979323846264338327950 * 5 * 5
78.53981633974483

Jeśli następnie chcielibyśmy obliczyć obwód tego koła, konieczne było by ponowne wpisanie wartości liczby pi. GHCi pozwala na definiowanie zmiennych za pomocą słowa let. Po jednokrotnym zdefiniowaniu wartości pi można używać jej w dalszych obliczeniach. Nazwy zmiennych (oraz funkcji) muszą rozpoczynać się od małej litery.

Prelude> let pi = 3.14159265358979323846264338327950
Prelude> pi
3.141592653589793

Liczba pi jest „obcinana” do 16 cyfr jedynie podczas wyświetlania.

Prelude> pi * 5 * 5
78.53981633974483

Jeśli chcielibyśmy również promień zastąpić zmienną r możemy napotkać pewien problem.

 Prelude> let r = 5
 Prelude> pi*r*r
 <interactive>:1:5:
    Couldn't match expected type `Double' 
	against `Integer'
      Expected type: Double
      Inferred type: Integer
    In the second argument of `(*)', namely `r'
    In the first argument of `(*)', namely `r', namely `pi * r'

Problem ten spowodowany jest ścisłą kontrolą zgodności typów, a dokładniej tym, że Haskell nie pozwala na pomnożenie liczby typu Double przez liczbę typu Integer. Rozwiązaniem tego problemu może być zdefiniowanie zmiennej r jako liczby rzeczywistej:

Prelude> let r = 5.0

Zmienne nie muszą być definiowane bezpośrednio jako liczby. Mogą być zdefiniowane na przykład jako wyrażenie arytmetyczne:

Prelude> let poleKola = pi * r * r
Prelude> poleKola
78.53981633974483

Należy jednak pamiętać, że wartość zmiennej jest niezmienna. Niemożliwe jest więc obliczenie pola koła o promieniu 2 w następujący sposób:

Prelude> let r = 2.0
Prelude> poleKola
78.53981633974483
Porada Zmienną poleKola (podobnie jak inne zmienne) można potraktować jak funkcję nieprzyjmującą żadnych parametrów. Zgodnie z zasadą referencyjnej przeźroczystości funkcja wywołana z takim samym zestawem parametrów (w tym przypadku lista parametrów jest pusta) zawsze zwróci tą samą wartość. Zmienna r jest zmienną globalną, a działanie funkcji nie może być zależne od żadnej zmiennej globalnej.


[edytuj] Funkcje

Wygodnym rozwiązaniem tego problemu jest zdefiniowanie funkcji przyjmującej jako parametr długość promienia koła.

Prelude> let poleKola r = pi * r * r
Prelude> poleKola 5
78.53981633974483
Prelude> poleKola 1
3.141592653589793

Zmienna r wewnątrz funkcji poleKola jest zmienną lokalną. Wartość zmiennej globalnej o tej samej nazwie nie ma żadnego wpływu na działanie funkcji.

Warto zauważyć, że tym razem możliwe jest obliczenie pola koła o promieniu 5 (nie musi to być 5.0). Typ wartości liczbowej jest ustalany na podstawie kontekstu w jakim występuje. Podczas definicji zmiennej r nie było możliwe wywnioskowanie, że ma to być liczba rzeczywista. Podczas wywołania funkcji poleKola z parametrem jest to możliwe. Parametr przekazany do funkcji jest mnożony przez liczbę rzeczywistą (pi), więc powinien być liczbą tego samego typu. Dzięki temu Haskell traktuje liczbę 5 jako Double.

Oczywiście możliwe jest zdefiniowanie funkcji przyjmującej więcej niż jeden parametr, na przykład obliczającej pole prostokąta:

Prelude> let poleProstokata a b = a * b 
Prelude> poleProstokata 2 4
8

Można również zdefiniować funkcję, która do wyznaczenia wartości wykorzysta inne funkcje. Na przykład funkcja licząca objętość prostopadłościanu

Prelude> let objProstopadloscianu a b h = poleProstokata a b * h
Prelude> objProstopadloscianu 1 2 3
6

[edytuj] Funkcje liczące pola graniastosłupów

Powyżej zostały zdefiniowane funkcje obliczające pole koła i prostokąta. Na ich podstawie w prosty sposób można zdefiniować funkcje wyznaczające objętość różnych graniastosłupów. Objętość dowolnego graniastosłupa obliczamy mnożąc pole podstawy razy wysokość. Na początek napiszmy więc taką funkcję:

Prelude> objGran polePodst h = polePodst * h

Teraz możemy już zdefiniować funkcje liczącą objętość prostopadłościanu, walca lub sześcianu:

Prelude> let objProstopadloscianu a b h = objGran (poleProstokata a b) h
Prelude> let objWalca r h = objGran (poleKola r) h
Prelude> let objSzescianu a = objGran (poleProstokata a a) a
Porada Ćwiczenie
  1. Zdefiniuj funkcję przyjmującą imię i witającą się (np. po podaniu imienia Tomek funkcja zwraca "Witaj Tomek!"). Do łączenia łańcuchów znaków służy operator ++.
  2. Utwórz funkcję obliczającą odległość dwóch punktów na płaszczyźnie (funkcja będzie przyjmować 4 współrzędne x1, y1, x2 i y2). Następnie napisz funkcję, która będzie przyjmować współrzędne wierzchołków trójkąta (6 liczb) oraz zwróci obwód trójkąta. Funkcja obliczająca obwód powinna wykorzystywać funkcję wyznaczającą odległość punktów.