Skip to content

Haskell λ

Jest to statycznie typowany, głównie deklaratywny, czysto funkcyjny język programowania.

Jego najpopularniejszym kompilatorem jest GHC (Glasgow Haskell Compiler).

Podczas programowania korzystamy z wyrażeń (opisujących coś) a nie z instrukcji (mówiących co ma być zrobione).

Jedną z najważniejszych właściwości tego języka jest to, że funkcje zawsze zwracają tą samą wartość dla tych samych argumentów wejściowych. (nie ma żadnych zmiennych globalnych, statycznych ani innych rzeczy, które mogłyby wpływać na wynik).

Zmienne

Większość przydatnych rzeczy jest w dokumentacji

  • Char - znak
  • isAlpha, isAlphaNum, isDigit, isLower, isUpper :: Char -> Bool
  • toLower, toUpper :: Char -> Char
  • putChar :: Char -> IO ()
  • String - type String = [Char]
  • Num, Int, Float - liczba
  • div, mod
  • () krotka
  • fst - zwraca pierwszy element
  • snd - zwraca drugi element
  • Bool
  • Operatory
  • [] - lista (patrz Operacje na listach)

Do zdefiniowania zmiennej możemy użyć słowa kluczowego let

let x = 4 in x * x

Int <-> Char konwersja pomiędzy tymi typami jest możliwa

ord b
>98
chr 98
>b

Typy

Poza wbudowanymi typami zmiennych możemy definiować własne

Synonymous types - używają już istniejących typów używamy tu słowa kluczowego type

type Name = String
type Position = String
type Age = Int
type Course = Int

type Point = (Float, Float)
type Path = [Point]

Algebraic types - typy algebraiczne są typami złożonymi zdefiniowanymi przez użytkownika. Używamy tu słowa kluczowego data

data Person =   Student Age Name Course |
                Professor Name Position |
                Director Name

namePerson :: Person -> Name
namePerson (Student e n c) = n
namePerson (Professor n c) = n
namePerson (Director n) = n

stud1 = Student 21 "Marian" 12

Tutaj słowa Student, Professor, Director służą do określenia typu danej struktury. (data constructor symbols)

Wykorzystując same symbole konstruktorów możemy tworzyć enumy

data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun

Functional types - budują i opisują funkcje

type MyType = (Int -> Int)
fib :: MyType

Przykłady:

  • not to wartość typu Bool -> Bool
  • (2 +) ma typ Int -> Int
  • map daje (a -> b) -> [a] -> [b] (przyjmuje funkcję robiązą z a b oraz tablicę a z potem zwraca tablicę b)

Operator -> zwija się z prawej strony

Tzn a -> b -> c jest równoznaczne z a -> (b -> c)
a nie (a -> b) -> c

Należy jednocześnie pamiętać, że funkcje zwijają się od prawej

f a b to ekwiwalent (f a) b a nie f (a b)
Czyli
$ not not false da nam error

Operacje

Funkcje są wywoływane poprzez podanie ich nazwy oraz argumentów

sort [42,13,22]
> [13,22,42]:: (Num a, Ord a) => [a]

Listy

Tworzenie i dodawanie

Aby dodać element do listy używamy :

'a' : []
> "a":: [Char]

Tutaj do pustej listy dodaliśmy literę a dzięki czemu dostaliśmy stringa.

Do tworzenia list możemy też używać automatycznego generowania

Przykłady:

  • [2..10] is [2,3,4,5,6,7,8,9,10]
  • [1..] is [1,2,3,4,...
  • [1,3..10] is [1,3,5,7,9]
  • [’a’..’e’] is “abcde”

Lub mechanizmu List comprehension

[wyrażenie przyjmujące zmienne | generator, filtry, generator, filtry, ...]

Na przykład

[ x*x | x<-[1..5], odd x ]
> [1, 9, 25]

repeat nieskończone listy

Prelude> repeat 3
[3,3,3,3,3...]

Operacje

Pobieranie - poniżej

pobieranie z list

Inne - operator ++

-- Łączenie list
Prelude> [3,4]++[5]
[3,4,5]

-- Indeksowane elementy
Prelude> [45,43,12,54] !! 2
12

-- branie x elementów z listy
Prelude> take 3 (repeat 1)
[1,1,1]

-- usuwanie elementu z listy
Prelude>

Funkcje dla list

Funkcja zip łączy dwie listy w listę krotek

zip [1,2] ["a","b"]
> [ (1,"a"), (2,"b")]
> length [1,2,3]
3

filter (a -> Bool) -> [a] -> [a] - przefiltrowuje wybraną listę, otrzymujemy tylko elementy dla których nasza funkcja zwraca True

> filter even [1,2,4,5,32]
[2,4,32]

map :: (a -> b) -> [a] -> [b] - aplikuje wybraną funkcję na wszystkie elementy wybranej listy i zwraca listę wyników

> map square [9,3]
[81,9]
> map (<3) [1,2,3]
[True,True,False]

foldr :: (a -> b -> b) -> b -> [a] -> b - funkcja, która pozwala łączyć wartości w tablicy. Podajemy jej argumenty:

  • funkcja
  • pierwszy element typu docelowego
  • lista
suma = foldr (+) 0
iloczyn = foldr (*) 1

Funkcje

FUnkcje są podstawotą częścią języka. Mogą być przekazywane jako argumenty, lub modyfikowane i częściowo aplikowane (partial application)

partial applications:

(+) :: Int -> Int -> Int
$ (2 +) 5
7
add_2 :: (Int -> Int)
add_2 = (2 +)
$ add_2 5
7

Definiowanie

Funkcja składa się z opcjonalnego opisu typów
nazwaFun :: Typ -> Typ -> Typ...
Oraz samej definicji, tutaj mamy 2 warianty:

addRange :: Int -> Int -> Int
addRange x y
  | x == y = x
  | x < y = x + addRange (x + 1) y
  | otherwise = y + addRange (y + 1) x

oraz

numCbetw2 :: Char -> Char -> Int
numCbetw2 c1 c2 =
  let x = ord c2 - ord c1
   in if abs x == 0 then abs 0 else abs x - 1

TODO Argumenty przekazywane do funkcji mogą mieć formę listy (łączonej strzałkami ->) lub krotek (el1,el2,...).

Łączenie funkcji

Wykorzystując kropkę . możemy łączyć funkcje w ciągi

(sum . map square . filter even) [1 .. 10]

Powyższy kod zwróci nam sumę kwadratów liczb parzystych dla liczb z zakresu 1-10.

Moduły

Moduły są odpowiednikami paczek/bibliotek dla innych języków.

Aby go włączyć należy go zaimportować

import MojModul
-- jest to plik MojModul

Przy tworzeniu nowego modułu od razu zaznaczamy jakie inne moduły improstujemy

module ModuleNameX where
  import ModuleNameY

Przy plikach w folderach używamy kropek. Np jeśli w folderze A/B/C mamy plik EjemImport.hs to importujemy go tak:

import A.B.C.EjemImport

Eksportowanie - przy definiowaniu modułu możemy zaznaczyć co chcemy z niego eksportować.

-- Tutaj widać że eksportujemy tylko 2 funkcje
module Geometry2D (areaSquare, perimeterSquare) where
  areaRectangle :: Float -> Float -> Float
  areaRectangle base height = base * height
  perimeterRectangle :: Float -> Float -> Float
  perimeterRectangle base height = 2 * (base + height)
  areaSquare :: Float -> Float
  areaSquare side = areaRectangle side side
  perimeterSquare :: Float -> Float
  perimeterSquare side = perimeterRectangle side side

W wypadku, gdy występuje kolizja nazw możemy użyć słowa kluczowego qualified

module NormalizeAll where
  import qualified NormalizeSpaces
  import qualified NormalizeCase as NC
  normalizeAll :: String -> String
  normalizeAll =NormalizeSpaces.normalize . NC.normalize