Wykład przeznaczony jest dla studentów I roku I stopnia Informatyki Algorytmicznej.
Odbywa się we wtorki w godz. -
w sali 0.32 w budynku C-13.
Na stronie tej znajdziesz informacje o zasadach zaliczenia, literaturze, realizowanym materiale oraz listę zadań.
Literatura
Podstawowa
Miran Lipovaca, Learn You a Haskell for Great Good! A Beginner's Guide, No Starch Press,
Will Kurt, Get Programming with Haskell, Manning,
Bryan O’Sullivan, John Goerzen, and Don Stewart, Real World Haskell, O’Reilly,
Benjamin C. Pierce, Basic Category Theory for Computer Scientists, The MIT Press,
Pomocnicza:
S. Tompson, Haskell. The craft for functional Programming, Addison Wesley,
B. Milewski, Category Theory for Programmers, Creative Licence,
Na laboratoriach będziecie oceniani za aktywność. Rozwiązania zadań będą oceniane w skali 0-2 pkt (w zależości od stopnia trudności, ustalonego przez prowadzącego). Do zdobycia będzie 20 pkt.
Pod koniec maja będą ogłoszone tematy kilku projektów do wyboru. Będziecie mogli je ralizować samodzielnie lub w dwuosobowych grupach. Za realizację projektu będzie można dostać do 10 pkt.
Ocena dostateczna będzie od 10 punktów.
$
\def\RR{\mathbb{R}}
\def\QQ{\mathbb{Q}}
\def\ZZ{\mathbb{Z}}
\def\CC{\mathbb{C}}
\def\NN{\mathbb{N}}
\def\IFF{\leftrightarrow}
\newcommand{\span}[1]{\mathrm{span}(#1)}
\newcommand{\IS}[2]{\langle\,#1,#2\rangle}
\newcommand{\sgn}[1]{\mathrm{sgn}(#1)}
$
Zagadnienia omówione na wykładzie
W1 (04.03.2025): GHCI i funkcje
Podstawowe polecenie ghci:
:q = wyjście
:t = typ
:i = info
:l = załadowanie modułu
Część kodów, które pojawiły się na tablicy.
module W1 where -- funkcja jednej zmiennej f x = 1 + x*(x+1)
g::Num a => a -> a -> a g x y = 1 + x*y -- Silnia : IF ... ELSE ... fact1 :: Integer -> Integer fact1 n = if n==0 then 1 else n * fact1 (n-1) -- Silnia : pattern matching fact2 :: Integer -> Integer fact2 0 = 1 fact2 n = n * fact2 (n - 1) -- Silnia : case expresion fact3 n = case n of 0 -> 1 n -> n * fact3 (n-1) -- Silnia : with let expression fact4 :: Integer -> Integer fact4 n = let y = fact4 (n-1) in if n==0 then 1 else n * y
Ważna sprawa: zrozumienie pojęcia currying i uncurrying.
W2 (11.03.2025): Podstawowe typy
Podstawowe konstruktory typów
(a,b) :pary elementów typu a i b
[a] : ciągi elementów typu a; podstawowe funkcje (:) oraz (++)
Część kodów, które pojawiły się na tablicy.
{-
modul: W2.hs
info: Wykład z FP 2024/25 lato
author: Jacek Cichoń
date: 11.03.2025
-}
module W02 where
{- PARY -}
fst' (x,_) = x
snd' (_,y) = y
coll n | n == 1 = 1
| even n = coll (div n 2)
| otherwise = coll (3*n+1)
collatz :: (Int,Int) -> (Int,Int)
collatz (n,s) | n==1 = (1,s)
| even n = collatz (div n 2,s+1)
| otherwise = collatz (3*n+1,s+1)
lCollatz n = snd (collatz (n,0))
-- partition
{- komentuje, bo jest w Data.List
partition :: (a->Bool) -> [a] ->([a],[a])
partition _ [] = ([],[])
partition p (x:xs) = if p x then (x:l,r)
else (l,x:r)
where (l,r) = partition p xs
-}
-- liczby pierwsze sieve (p:xs) = p : sieve (filter (\n -> mod n p /= 0) xs) primes = sieve [2..]
better_sieve (p:xs) = p: sieve (filter(\n -> n<p*p ||(mod n p /= 0)) xs)
addGF xs ys = zipWith (+) xs ys
Konstrukcja "type"
module Fizyka(Point,Vector,Time,
move, translate, moveInGF) where
type Point = (Double,Double)
type Vector = (Double,Double)
type Time = Double
constG = 9.80655
-- lokalna funkcje
generalMove :: Point -> Vector -> Time -> Double -> Point
generalMove (x,y) (vx,vy) t acc = (x+vx*t, y + vy*t + acc*t*t/2)
-- upublicznione
move :: Point -> Vector -> Time -> Point
move p v t = generalMove p v t 0
translate :: Point -> Vector -> Point
translate p v = generalMove p v 1 0
moveInGF :: Point -> Vector -> Time -> Point
moveInGF p v t = generalMove p v t (-constG)
Konstrukcja "data" - wstęp
module W04b where
{-- Typy numerowane --}
data DOW = Po|Wt|Sr|Cz|Pi|So|Ni deriving (Eq,Ord,Enum,Bounded)
instance Show DOW where
show Po = "Poniedziałek"
show Wt = "Wtorek"
show Sr = "Środa"
show Cz = "Czwartek"
show Pi = "Piatek"
show So = "Sobota"
show Ni = "Niedziela"
dniPracujace = [Po .. Pi] -- efekt Enum
Proponuję rzucić okiem na następującą stronę:
The Evolution of a Haskell Programmer, na której Fritz Ruehr'er wygłupia się z różnymi sposobami zdefiniowania funkcji silnia.
W5 (01.04.2025): Typy, wstęp do funktorów
Zastosowania foldów i strumienie
module W5b where
-- prototyp
data Osoba' = Osoba' String String String Int Int Int
imie' :: Osoba' -> String
imie' (Osoba' _ x _ _ _ _) = x
-- itd
{-- RECORD SYNTAX --}
data Osoba = Osoba
{
idO :: String
,imie :: String
,nazwisko :: String
,rokUr :: Int
,miesiacUr :: Int
,dzienUr :: Int
} deriving (Show,Eq)
data Date = Date{rok::Int,miesiac::Int,dzien::Int}
deriving(Eq)
instance Show Date where
show (Date r m d) =
(show r) ++"."++(show m)++"."++(show d)
data Person = Person
{
_idO :: String
,_imie :: String
,_nazwisko :: String
, dataUr :: Date
} deriving (Show,Eq)
ccc = Person "001" "Anna" "Balicka" (Date 2005 5 12)
zmienDate :: Person -> Date -> Person
zmienDate osoba nowaData =
osoba{dataUr = nowaData}
zmienRokUr :: Person ->Int -> Person
zmienRokUr osoba rokUr =
let urodziny = dataUr osoba
urodziny' = urodziny{rok = rokUr} in
osoba{dataUr = urodziny'}
-- to samo - inny zapis
zmienRokUr' osoba rokUr =
osoba{dataUr = (dataUr osoba){rok = rokUr}}
--- Pozniej: lenses
{-- TYPY PARAMETRYZOWALNE --}
-- MODEL KATEGORYJNY: PP (X) = X x X
data Para a = Para (a,a) deriving (Show,Eq)
-- MODEL :: jesli f:X->Y to (fmap f):(X x X) -> (Y x Y)
instance Functor Para where
fmap f (Para (x,y)) = Para (f x, f y)
--pmap :: (a->b) -> Para a -> Para b
--pmap f (Para (x,y)) = Para (f x, f y)
f x = 3.5 * x*(1-x)
-- uzycie: fmap f Para(0.25,0.75)
{-- FUNKTOR MAYBE --}
-- data Maybe a = Nothing | Just a
-- MODEL: MB(X) = ({0} x {*}) u ({1} x X)
-- fmap f Nothing = Nothing
-- fmap f (Just x) = Just (f x)
safeHead [] = Nothing
safeHead (x:_) = Just x
safeSqrt x
| x>=0 = Just (sqrt x)
| otherwise = Nothing
safeLog x
| x>0 = Just (log x)
| otherwise = Nothing
safeDiv x 0 = Nothing
safeDiv x y = Just (x/y)
-- zaczynamy partyzantkę;
-- pozniej zrobimy to lepiej
-- sqrt(log x)
composeMB :: (a-> Maybe b) -> (b -> Maybe c) -> (a -> Maybe c)
composeMB f g x =
case f x of
Nothing -> Nothing
Just y -> g y
expr1 = composeMB safeLog safeSqrt
-- (sqrt x)/log(x +1)
composeMB2 :: (a->b->Maybe c) -> Maybe a -> Maybe b -> Maybe c
composeMB2 _ Nothing _ = Nothing
composeMB2 _ _ Nothing = Nothing
composeMB2 op (Just x) (Just y) = op x y
expr2 x = let sn = safeSqrt x
sd = safeLog (x^2-4) in
composeMB2 safeDiv sn sd
Oto skompresowany katalog
W5_BD.zip, z plikami którymi bawiliśmy się na wykładzie.
W6 (08.04.2025): Elementy Teorii Kategorii; wstęp do funktorów
Oto kody omawiane na wykładzie, po uwzględnieniu większości
propozycji programu hlint:
module W6a where
data MAYBE a = NOTHING | JUST a
newtype READER a b = READER{runReader:: a -> b}
newtype WRITER m a = WRITER (a, m)
instance Functor MAYBE where
fmap _ NOTHING = NOTHING
fmap f (JUST x) = JUST (f x)
instance Functor (READER a) where
fmap f (READER phi) = READER (f . phi)
instance Functor (WRITER m) where
fmap f (WRITER (x,m)) = WRITER (f x, m)
{-- Zastosowanie --}
clearChar ch = let lc = toLower ch in
if lc `elem` ['a'..'z'] then lc
else ' '
toWords :: String ->[String]
toWords = words . map clearChar
filtrujSW :: [String] -> String -> [String]
filtrujSW ws stopW =
let stw = words stopW in
filter (`notElem` stw) ws
pogrupuj :: [String] -> [(String,Int)]
pogrupuj = sortBy (\ (_,k) (_,l) -> compare l k) .
map (\ g -> (head g,length g)) . group . sort
topN :: [String] -> Int -> [(String,Int)]
topN xs ile = take ile (pogrupuj xs)
-- Zanurzamy obliczenia w funktor RE
type RE a = READER (String, String, Int) a
---- (book, stopwords, ile)
toWordsRE :: RE String -> RE [String]
toWordsRE = fmap toWords
filtrujRE :: RE [String] -> RE [String]
filtrujRE (READER α) =
READER (\ (b,sw,i) -> filtrujSW (α (b,sw,i)) sw)
topNRE :: RE [String] -> RE [(String,Int)]
topNRE (READER β) =
READER (\ (b,sw,ile) -> topN (β (b,sw,ile)) ile)
-- teraz inicjujemy proces
getBookRE :: RE String
getBookRE = READER (\(book,_,_) -> book)
-- i skladamy wszystkie funkcje
findTopNRE = (topNRE . filtrujRE . toWordsRE) getBookRE
-- Oto użycie
findTopN = runReader findTopNRE
-------------------------
run bookPath ile = do
book <- readFile ("Dane/"++bookPath)
sw <- readFile "Dane/stop_words_english.txt"
print $ findTopN (book, sw, ile)
Spróbujcie sprawdzić działanie powyższego kodu wywołując w ghci
findTopN ("Ala ma kota, Ala ma psa, Ala ma kanarka", "", 4)
W7 (15.04.2025): Wstęp do funktorów
Quick Check
module W7a where
import Data.List
import Test.QuickCheck
{-- Zabawa z liczbami pierwszymi --}
-- niezbyt efentywna funkcja
-- Zadanie: zrób to lepiej
primeQ n =
length ([k | k<- [1..n], n `mod` k == 0]) == 2
-- wielomian Eulera
eulerPoly n = n*n + n + 41
-- własność do przetestowania
checkEulerPoly' :: Int -> Bool
checkEulerPoly' n =
primeQ (eulerPoly (abs n))
checkEuler' = quickCheck checkEulerPoly'
-- trochę lepiej
checkEulerPoly :: Int -> Property
checkEulerPoly n = (n>=0) ==> primeQ (eulerPoly n)
checkEuler = quickCheck checkEulerPoly
{-- SPLIT string
CEL:
split :: Char -> String -> [String]
przykład działania:
split '/' "data/usr/include" = ["data", "usr", "include"]
split '/' "/usr/include/dat" = ["", "usr", "include", "dat"]
--}
split1 :: Char -> String -> [String]
split1 c [] = []
split1 c xs =
let x1 = takeWhile (/= c) xs
x2 = dropWhile (/= c) xs in
x1 : if null x2 then []
else split1 c (tail x2)
{--
wlasnosc do przetestowania :
((unsplit c) . (split c)) xs == xs
kontrola liczby testów:
quickCheck (withMaxSuccess 1000 propertyToCheck)
--}
propSplit1 c xs = unsplit c (split1 c xs) == xs
checkSplit1 = quickCheck (withMaxSuccess 1000 propSplit1)
-- Poprawiona funkcja SPLIT ---------------
split :: Char -> String -> [String]
split c "" = [""]
split c xs =
let x1 = takeWhile (/= c) xs
x2 = dropWhile (/= c) xs in
x1 : if null x2 then []
else split c (tail x2)
-- testowanie ze zbieraniem inforacji o testach:
-- collect
checkSplit c xs = let ys = split c xs in
collect (length ys) $ unsplit c ys == xs
module W7b where
{-- PODSTAWOWE FUNKTORY --}
-- DWA NOWE FUNKTORY
newtype STATE s x = ST (s -> (x,s))
data EITHER a b = LEFT a | RIGHT b
instance Functor (STATE s) where
fmap f (ST σ) = ST( \s -> let (x ,t) = σ s in (f x,t))
instance Functor (EITHER a) where
fmap _ (LEFT x) = LEFT x
fmap f (RIGHT x) = RIGHT (f x)
-- Przykłady typów parametryzowalnych które nie są
-- funktorami
newtype ToInt a = TInt (a->Int)
newtype Predicate a = Pred (a->Bool)
newtype Endo a = Endo (a->a)
-- Typy rekurencynje
data SimpleTree a = Leaf a | Node (SimpleTree a) (SimpleTree a)
deriving (Eq)
Rozważamy odwzorowanie $F(X) = \NN + (X\times X)$ i definiujemy
$F^{0} = F(\emptyset)$, $F^{n+1} = F(F^n)$. Mamy (sprawdźcie to)
$$ F^0 \subseteq F^1 \subseteq F^2 \subseteq F^3 \subseteq \ldots$$
Kładziemy $F^{\omega} = \bigcup_{n\geq 0} F^n$. Mamy $F(F^\omega) = f^\omega$ (sprawdźcie to).
Z tego wynika, że zbiór $F^\omega$ jest najmniejszym punktem stałym odwzorowania $F$.
Ten zbiór jest izomorficzny z SimpleTree $\NN$.
Tak Haskell interpretuje ten typ rekurencyjny.