Professional Documents
Culture Documents
PV 04 FuncProgI
PV 04 FuncProgI
Programmēšanas valodas
Funkcionālā programmēšana: ievads
• Funkcionālās programmēšanas sākumi: - rēķini (1935)
• Pamatideja: programma ir funkcija, kas ieejas datus attēlo par izejas datiem;
• Funkcija - tāds pats datu objekts, kā pārējie, var definēt funkcijas, kas funkcijas
attēlo par funkcijām. Augsta modularitāte;
• Citas funkcionālas valodas: ML, MIRANDA, Lazy ML, F#, Clojure, u.c.
Funkcionālā programmēšana:
1) iespējams rakstīt nopietnas lietotnes
2) funkcionālajās valodās ietvertajām idejām liela ietekme uz programmēšanas
valodu attīstību vispār.
- rēķini
- termi: M ::= x |x.M | M N
x - mainīgais
x.M - abstrakcija (funkcija x M, parasti M = M(x))
M N - aplikācija: terms - funkcija M tiek pielietots argumenta termam N
Piemērs: funkciju x x2 apzīmē kā x.x2 [x2, +, - tiks ieviesti vēlāk]
Vairākargumentu funkcijas: x. (y. (z. M)), saīsināti pieraksta: x y z. M
Funkciju pielietošana vairākiem argumentiem: M N1 N2 N3 apzīmē (((M N1 ) N2 ) N3 )
Precedence: x.yx x.(y x), nevis (x.y) x (aplikācija saista ciešāk)
Funkciju izpilde - rēķinos: - redukciju virkne.
- redukcija - funkciju pielieto argumentam: (x.x2) 7 72
(x.x2) (13-z) (13-z)2
Pareiza arī šāda “bezjēdzīga” redukcija: (x.x2) (xy.x+y) (xy.x+y)2
x.M N M[N/x] (x.M N tiek reducēts par reducēts par termu M, kurā visās
mainīgā x brīvās ieiešanas vietās tiek ierakstīts terms N).
Nepieciešamības gadījumā tiek izmantota arī - redukcija: saistīto mainīgo
pārsaukšana: x.x2 var tikt reducēts par y.y2.
Terms ir normālā formā, ja tam nevar pielietot - redukciju.
- rēķini: redukcija
(xy.x+y) 5 y.x+y [5/x] y.5+y - funkcijas pielietošana vienam argumentam
(xy.x+y) 5 7 ( x.(y.x+y) 5) 7 (y.5+y) 7 5+7
- redukcija pielietota terma iekšienē
- terms ir koks, virsotnes: x, , @ (apzīmē aplikāciju):
@ @
@ 7 @ 7 @ @
5 5 7 7 5+7
x x y 5+y y 5+y
y x+y y x+y
- terma normālā forma (vērtība): nav iespējamas tālākas - redukcijas (aprēķini
pabeigti, visas funkcijas pielietotas saviem argumentiem)
Ja - termam ir normālā forma, tad tā ir viena vienīga (redukciju secība var būt
dažāda, iespējami dažādi izpildes scenāriji, iespējams veidot “paralēlismu”).
Uzrakstīt - rēķinu interpretatoru - ? [, - redukcijas]
Funkcionālas valodas realizācija: ērti attēlot - termus, efektīvi realizēt redukciju.
- rēķini: redukcija un normālā forma
Teorēma. Ja - termam ir normālā forma, tad tā ir viena vienīga.
Ilustrācija:
f. x. (f (f x)) y.(y + y) 7 x. ( ( y. (y + y)) ( ( y. (y + y)) x)) 7
x. ( y. (y + y)) ( ( z. (z + z)) x) 7
x. (( z. (z + z)) x) + (( z. (z + z)) x) 7 ...
Programmēšanas valodas
Haskell instalācija un darba organizācija
http://www.haskell.org/
Jebkurš instalācijas veids būs piemērots/pietiekams
Programmas teksta rediģēšana - atsevišķā failā (rediģēšana ar parastu teksta
redaktoru, atsevišķiem redaktoriem var būt Haskell atslēgas vārdu atpazīšanas
serviss), interaktīvā vide – tikai izteiksmju aprēķināšana un rezultāta izdruka.
Teksta sākums: module nnn where
Izpildes vidē
:? piedāvā palīdzību (help)
definīciju ielāde ar :l[oad], (vai arī dbl-click uz faila pēc noklusēšanas to atver ar
ghci)
definīciju izmaiņu ielāde: :r[eload] (piemēram, ja failā kaut kas pamainīts)
: t eee parāda izteiksmes eee tipu
:set +t uzstāda režīmu pēc katras interaktīvajā vidē aprēķinātās
izteiksmes rādīt tās tipu
Vienkārša darba vide: http://replit.com/
Jālieto :load file.hs lai ielādētu funkciju definīcijas (lai varētu izpildīt Run komandu,
vajadzīgas tālākas konstrukcijas).
Valodas Haskell atbalsts ir arī Visual Studio Code.
Daži piemēri
No http://www.haskell.org/haskellwiki/Learn_Haskell_in_10_minutes
Prelude> 3 * 5
15
Prelude> 4 ^ 2 – 1
15
Prelude> (1 - 5)^(3 * 2 - 4)
16
Prelude> "Hello“
"Hello“
Prelude> "Hello" ++ ", Haskell“
"Hello, Haskell“
Prelude> succ 5 succ – standarta bibliotēkā iebūvēta funkcija
6
Prelude> round 6.59
7
Prelude> sqrt 2
1.4142135623730951
Prelude> not (5 < 3)
True
Prelude> gcd 21 14
7
Piemēri, ar tipu informāciju
Pēc izpildītas komandas :set +t
Prelude> 3 * 5
15
it :: Integer
Prelude> (1 - 5)^(3 * 2 - 4)
16
it :: Integer
Prelude> "Hello" ++ ", Haskell“
"Hello, Haskell“
it :: [Char] Simbolu virkne = simbolu saraksts
Prelude> sqrt 2
1.4142135623730951
it :: Double
Prelude> not (5 < 3)
True
it :: Bool
Tipi tiek noteikti automātiski (cik iespējams). Ja izteiksmei tips neeksistē – tipu kļūda.
Var būt situācijas, kad lietotājs tipu “saka priekšā” (arī dokumentācija).
Tālāki piemēri, tipu informācija
>5
5
it :: Integer
>5*5
25
it :: Integer
> 5.0
5.0
it :: Double
> 5 * 5.0
25.0
it :: Double Automātiska tipu konversija (šādi nav, piemēram, ML)
> 5 + ‘a’ Stingra tipu kontrole
No instance for (Num Char)
arising from the literal `5' at <interactive>:1:0
‘a’ ir ar tipu Char, 5 un + prasa, lai arī otrā argumenta tips piederētu tipu klasei Num
> :t (*)
(*) :: (Num a) => a -> a -> a
Arī funkcijām ir tipi, * var tikt pielietots jebkādam skaitliskam tipam
Anonīmās funkcijas (lambda – izteiksmes)
Izpildes vidē:
> (\x -> x*x) 5 Anonīmā funkcija (- izteiksme) un tās pielietojums
25
> (\x -> x*x) 5.1 Pielietojums cita tipa vērtībām
26.009999999999998
it :: Double
insert x [] = [x]
insert x (h:t) = if x<h then x:(h:t) else h:(insert x t)
insert_sort [] = []
insert_sort (h:t) = insert h (insert_sort t)
> :t foldl
foldl :: (a -> b -> a) -> a -> [b] -> a
> :t foldr
foldr :: (a -> b -> b) -> b -> [a] -> b
deriving (Show,Eq) jāraksta, lai šī tipa vērtības būtu iespējams izspīdināt uz ekrāna, kā
arī, lai tās būtu salīdzināmas, izmantojot vienādību.
aaa = Nil
ccc = Node 4 (Node 3 Nil Nil) (Node 6 Nil (Node 7 Nil Nil))
eval (Skip) s = s
eval (Assign x a) s = do_assign x a s
eval (Seq c1 c2) s = eval c2 (eval c1 s)
eval (If_c b c1 c2) s = if (bval b s) then (eval c1 s) else (eval c2 s)
eval (While_c b c) s = if (bval b s) then eval (While_c b c) (eval c s) else s
:t eval
eval :: Com -> (String -> Integer) -> String -> Integer
do_assign2 :: String -> Aexp -> [(String, Integer)] -> [(String, Integer)]
eval (Skip) s = s
eval (Assign x a) s = do_assign2 x a s
eval (Seq c1 c2) s = eval c2 (eval c1 s)
eval (If_c b c1 c2) s = if (bval b s) then (eval c1 s) else (eval c2 s)
eval (While_c b c) s = if (bval b s) then eval (While_c b c) (eval c s) else s
Funkcionālā programmēšana
Aizturētā programmu izpilde
Aizturētā programmu izpilde: rēķināt argumenta vērtību funkcijai tikai tad, ja
arguments patiešām vajadzīgs.
Parametru nodošana apakšprogrammai pēc vārda (analoģiski ar ALGOL 60, šeit daudz
nopietnāki pielietojumi).
let val x = 3 * 2 in (4 * 2) - (x + x) end
Kādu aprēķināšanas secību izvēlēties?
a) aprēķināt x -> 6 un tad ievietot ?
b) ievietot x vietā izteiksmi 3*2 ? (jārēķina būs divreiz …)
c) ievietot norādi uz x, tad aprēķināt, ja vajadzīgs (otrajai reizei vērtība 6 jau būs
gatava) ?
Aizturētā izpilde: lietotājs var domāt, ka rīkojas atbilstoši (b), patiesībā implementācija
atbilst (c) . Memoizācijas semantika.
Argumentu var nevajadzēt (i), var būt tā, ka to arī nevar aprēķināt (ii):
(i) let val x = 3 * 2 in (4 * 2) - (5 + 7) end
(ii) let val x = 5 `div` 0 in (4 * 2) - (5 + 7) end
Aizturētā programmu izpilde (2)
Argumentu var nevajadzēt (i), var būt tā, ka to arī nevar aprēķināt (ii):
(i) let val x = 3 * 2 in (4 * 2) - (5 + 7) end
(ii) let val x = 5 div 0 in (4 * 2) - (5 + 7) end
Izteiksme ne-strikta, ja tai var būt definēta vērtība arī tad, ja argumenta vērtība ir
nedefinēta.
Striktas izteiksmes: GIGO princips (garbage in garbage out). Striktas izteiksmju
aprēķināšana ir vieglāk implementējama.
- termos: vai censties reducēt argumentu pirms ievietošanas ?
Teorēma par normālās formas unikalitāti: aprēķinu secības maiņa - rēķinos nevar
novest pie cita rezultāta (bet var novest pie situācijas, ka risinājums netiek atrasts).
Funkcionālā valodā šī teorēma spēkā, ja nav “blakus efektu” izteiksmēm.
Dažādi izpildes scenāriji funkcionālā valodā:
g ( f (x) ):
- aprēķināt f(x), tad virzīt to kā argumentu funkcijai g
- censties rēķināt g, rēķinot f tik daudz, lai g saņemtu tos datus, kas viņai vajadzīgi
… (var labi strādāt ar bezgalīgām datu struktūrām !)
Ģenerēšanas un filtrēšanas programmas
Iespējams valodā iekļaut potenciāli bezgalīgus objektus.
Plūsmas – saraksti ar slinko elementu aprēķināšanas secību.
Fibonači skaitļi
fib = 0 : 1 : [ a + b | (a,b) <- zip fib (tail fib) ]
fibsn n = take n fib
Alternatīva definīcija:
fibslist1 n = take n fibs1 where fibs1 = 0:1:zipWith (+) fibs1 (tail fibs1)
Eratostena siets:
sieve (p : lis) = p : sieve [ n | n <- lis, mod n p /= 0]
primes = sieve [ 2 .. ]
> take 20 primes
[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71]
Slinkā programmu izpilde: piemēri
Kvadrātsaknes aprēķināšana, Ņūtona - Rapsona algoritms, N:
Iterāciju virkne: a0, a1, …, an, … an+1 = (an + N/ an) / 2
Realizācija:
Definējam nākamo iterāciju:
next n x = (x + n/x)/2
Bezgalīga iterāciju struktūra (ģenerators):
repeat1 f a = a : (repeat1 f (f a))
repeat ( next n ) a0 rezultāts: [a0,a1,a2,…] - bezgalīgs saraksts!
Nosacījums, cik daudz datus no bezgalīgās struktūras ņemsim (filtrs):
within eps (a:b:rest) = if abs(a-b) <= eps then b
else within eps (b:rest)
sqrt a0 eps n = within eps (repeat1 (next n) a0)
Cita veida nosacījums uz šo pašu bezgalīgo virkni (cits filtrs):
relative eps (a:b:rest) = if abs(a/b-1) <= eps then b
else relative eps (b:rest)
sqrt1 a0 eps n = relative eps (repeat1 (next n) a0)
Tēma: Monādes valodā Haskell
Funkcionālā programmēšana
Monādes, aprēķini ar «kontekstu»
Pievieno «čaulu» apkārt vērtībai.
Piemēra «čaula»: pievienojam pāra otro elementu. 3 -> (3,0)
Monāde: jauna Ieguldījums
vērtība kontekstā
1) Funkcija return lai iegūtu sākotnējo monādisko vērtību: 3 -> (3,0)
Piemērs: return x = (x,0)
2) Funkcija >>= monādisko vērtību kompozīcijai (kombinē (x,n) ar \x -> (y,k) lai
iegūtu (y, update_context k n))
Piemērs: (>>=) (x,n) f = let (y,k) = f x in (y, n+k) update_context ir +
Rezultāts atkal ir Konkrētā monāde
Write (x,n) >>= \x -> (y,k) definē, kā konteksts
(vērtība, konteksts)
vērtība ar tiek ietekmēts
kontekstu kompozīcija Attēlo vērtību par
jauno vērtību un
f x = (x+1,2) ieguldījumu kontekstā
Citas definīcijas:
g x = (x*2,3) return x = (x,1)
Tad g.f.f.return 5 -> g.f.f (5,0) -> g.f (6,2) -> g(7,4) -> (14,7) (x,n) >>=
\x -> (y,k) = (y, n*k)
Rakstām (return 5) >>= f >>= f >>= g Rezultāts -?
Tā pati programma pār
Vērtību konteksts (otrā komponente) nav redzama programmā citu monādi.
Monādes piemērs: Maybe
data Maybe a = Nothing | Just a (Maybe datu tips)
halve :: Int -> Maybe Int
halve (even x) = Just (x `div` 2)
halve (odd x) = Nothing
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
Maybe kā monāde:
https://riptutorial.com/haskell/example/6144/the-maybe-monad
instance Monad Maybe where
return = Just x
fail = Nothing
Nothing >>= f = Nothing
return :: a -> Maybe a
(Just x) >>= f = f x return x = Just x
Maybe x: aprēķini, kam var būt (>>=) :: Maybe a -> (a->Maybe b)-> Maybe b
un var nebūt rezultāts (>>=) m g = case m of Nothing -> Nothing
Just x -> g x
«Iespējamības» čaula ap vērtībām.
Vērtības neesamība (Nothing) apstrādāta nevis kodā, bet monādē.
HASKELL: datu ievads un izvads
Speciāla veida vērtības – notikumi (actions)
Izteiksmes, kuru vērtība ir notikums – komanda (command).
Komandai, kuras notikuma rezultāts ir ar tipu T, tips ir IO T .
Komandai, kuras notikumam nav rezultāta, tips ir IO () .
Piemēram: putStrLn :: String -> IO ()
getLine :: IO String
svx = putStrLn "Sveiks!" -- Komandas svx izsaukums izdrukās “Sveiks!”
dlg = do -- do – atļauj veidot virknes kompozīciju no komandām
putStrLn "Please enter your name: "
s <- getLine
putStrLn ("Hello, " ++ s ++ ", how are you?")
Darbības ar failiem: linetofile = do
s <- getLine
writeFile "a.txt" s
filetofile = do
s <- readFile "a.txt"
writeFile "b.txt" s
Monādu klase, IO monāde
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b («then» operācija, kombinē monādisko
vērtību ar tipu m a un funkciju ar monādisko tipu a -> m b)
(>>) :: m a -> m b -> m b (2 monādisko vērtību kombinācija)
return :: a -> m a (no «tīrās» vērtības iegūt atbilstošo monādisko)
fail :: String -> m a (kļūdas ziņojuma funkcija)
Avots: http://www.engr.mun.ca/~theo/Misc/haskell_and_monads.htm