--- FLP studentská sbírka úloh === Přehlednější sbírka oficiálních řešení + zadání z fitušky od studentů. Upravujte, přidávejte dle libosti. Držte pls nějakou strukturu, ať se v tom dá vyznat a nedopadne to jak sdílený dokument. Pro diskuzi se dají použít komentáře u odstavců/kódů. - Haskell časť je za 30b a Prolog časť za 30b - LEN na riadnom termíne je vždy bonus +10b - nie každý termín obsahuje dôkaz či Lambda kalkul # Haskell ## 2022/2023 ### Předtermín 1) Lambda kalkul: zadefinujte jakkoliv True, False a XOR (pripadne cokoliv dalsiho je potreba). Ukazte, ze XOR T F -> T (7b) 2) V haskellu je dano - `suma a [] = a` - `suma a (x:xs) = suma (a + x) xs` Dokazte, ze `suma 0 xs = foldl (+) 0 xs`. Byla tam poznamka, ze mame vhodne zvolit indukcni hypotezu (8b) 3) Zadefinujte datovy typ pro vyrazy s celymi cisly, jejich scitanim a nasobenim (2b) 4) Nadefinujte akci `pp` (pretty-print) prijimajici argument odpovidajici typu z ulohy 3, co na standardni vystup vypise vyraz uzavorkovany tak, aby vyznam zustal zachovan (nesmi ale byt zadne zavorky navic). Mohli jsme pouzit Prelude a navic `putChar`, `putStr`. Na pomocnem papire bylo asi 5 prikladu uzavorkovani, neco jako: (13b) ```https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both# + / \ * 3 -> 1*2+3 / \ 1 2 * / \ + 3 -> (1+2)*3 / \ 1 2 * / \ + + -> (1+2)*(3+4) / \ / \ 1 2 3 4 + / \ + + -> 1+2+3+4 / \ / \ 1 2 3 4 ``` ```haskell data Expr = Val Int | Add Expr Expr | Mul Expr Expr pp' :: Expr -> String pp' (Val val) = show val pp' (Add e1 e2) = pp' e1 ++ "+" ++ pp' e2 pp' (Mul a1@(Add _ _) a2@(Add _ _)) = "(" ++ pp' a1 ++ ")*(" ++ pp' a2 ++ ")" pp' (Mul a1@(Add _ _) v) = "(" ++ pp' a1 ++ ")*" ++ pp' v pp' (Mul v a2@(Add _ _)) = pp' v ++ "*(" ++ pp' a2 ++ ")" pp' (Mul e1 e2) = pp' e1 ++ "*" ++ pp' e2 pp :: Expr -> IO () pp expr = putStr (pp' expr) ``` 5) Premie: Nadefinovat typ pro double linked list (2b). Napsat funkci `l2dll`, co vezme bezny haskellovsky seznam a udela z nej ten nas DLL (8b). ## 2021/2022 ### termin ![](https://media.discordapp.net/attachments/621775580471492638/973595388504317982/unknown_973594542941024347_372131404412354571.jpg?width=854&height=484) 1. Vlastnosť operátoru pevného bodu -- `Y E = E (Y E)` Definícia LT v λ-kalkule s použitím operátora pevného bodu (`Y`), `iszero` a `prev`: ``` LET ?: = λ x y z . x y z LET LTfn = λ f x y . (iszero y) ? False : ((iszero x) ? True : (f (prev x) (prev y))) LET LT = Y LTfn ``` 2. - funkce mid, která pro vstupní seřaditelný seznam vrací takovou hodnotu, která seznam rozdělí na dvě části, kde jedna obsahuje pouze menší prvky, druhá pouze větší prvky a jejichž délka se liší nanejvýš o 1. Nebylo ale v zadání zmíněno, zda musí být prvky v seznamu unikátní. Pokud by nebyly, imo nebude řešení existovat. K dispozici afaik holý Haskell. ```haskell -- ak holy haskell definuj aj head, init, tail head []= errorEmptyList "head" head (x:_) = x tail [] = errorEmptyList "tail" tail (_:xs) = xs init :: [Int] -> [Int] init [] = errorEmptyList "init" init [x] = [] init (x:xs) = x : init xs quicksort :: (Ord a) => [a] -> [a] quicksort [] = [] quicksort (x:xs) = smaller ++ [x] ++ bigger where smaller = quicksort [a | a <- xs, a <= x] bigger = quicksort [a | a <- xs, a > x] -- najde median mida:: [a] -> a mida l@(_:_:_:_) = mida $ tail $ init l mida l = head l mid l = mida $ quicksort l ``` alebo ```haskell quick :: Ord a => [a] -> [a] quick [] = [] quick [x] = [x] quick (x:xs) = smaller ++ [x] ++ larger where smaller = quick [y | y <- xs , y <= x] larger = quick [y | y <- xs, y > x] myInit [] = undefined myInit [x] = [] myInit (x:xs) = x:myInit xs myTail [] = undefined myTail [x] = [] myTail (x:xs) = xs mid :: Ord a => [a] -> a mid [] = undefined mid [x] = x mid [x,y] = x mid xs = mid (myInit $ myTail $ quick xs) ``` - Haskell io Načíst soubor a vypsat jej, přičemž každý řádek je trimmed o whitespaces (funkce trim k dispozici) a před každým řádkem je jeho počet znaků po trimu. Např.: " Hello " -> "5:Hello" Za takto upraveným vypsaným souborem přidat (Počet řádků celkem) ++ "/" ++ (počet po trimu prázdných řádků) Např.: "1/0" K dispozici klasické IO funkce, Prelude (unlines/lines, ...), byly k nim i signatury. ```haskell trim :: String -> String trim str = let dropLastSpace [] = [] dropLastSpace s = if last s == ' ' then dropLastSpace $ init s else s in dropLastSpace $ dropWhile (\x -> x == ' ') str length' :: [a] -> Integer length' [] = 0 length' (_ : xs) = 1 + length' xs map' :: (a -> b) -> [a] -> [b] map' _ [] = [] map' f (x : xs) = f x : map' f xs trim :: String -> String trim l = f (f l) where f = reverse . dropWhile isSpace processLine :: String -> String processLine line = show (length' trimmed) ++ ":" ++ trimmed where trimmed = trim line emptyLines :: [String] -> Integer emptyLines [] = 0 emptyLines ((count : _) : xs) | count == '0' = emptyLines xs + 1 | otherwise = emptyLines xs lenls :: FilePath -> IO () lenls path = do handle <- openFile path ReadMode content <- hGetContents handle let l = map' processLine (lines content) let emptyLinesCount = emptyLines l putStr (unlines l ++ show (length' l) ++ "/" ++ show emptyLinesCount) ``` ### Riadny 1. Definuj štruktrúru zásobníka pre riešenie v konštantnom čase (tj. bez rekurzie) - `empty` - inicializuje prázdny zásobnik - `top` - vráti vrchol zásobnika - `push` - pridá položku na vrchol zásobnika - `pop` - odstráni vrch zásobníka - `len` - vráti počet položiek v zásobníku ```haskell data PD a = PD Int [a] deriving (Show,Eq) empty :: PD a empty = PD 0 [] top (PD _ (x:_)) = x push (PD s l) v = PD (s+1) (v:l) pop (PD s (x:xs)) = (x,PD (s-1) xs) len (PD s _) = s ``` 2. definovať funkcie - pushStr, ktorá vloží reťazec na zásobník - popStr, ktorá popne reťazec zo zásobníka ak sa zhoduje ```haskell pushStr pda str = foldr (\x pda -> push pda x) pda str -- popStr pda str | lstr > len pda = Nothing | not res = Nothing | True = Just newPda where lstr = length str (res,newPda) = checkPop pda str checkPop pda [] = (True,pda) checkPop pda (s:ss) = let (v,pdaN) = pop pda in if v==s then checkPop pdaN ss else (False,pda) ``` alternatívna implementácia ```haskell -- 1. data Stack a = Stack [a] Int empty :: Stack a empty = Stack [] 0 top :: Stack a -> a top (Stack [] _) = error "empty stack" top (Stack (x : _) _) = x push :: Stack a -> a -> Stack a push (Stack s i) x = Stack (x : s) (i + 1) pop :: Stack a -> (a, Stack a) pop (Stack [] _) = error "empty stack" pop (Stack (x : s) i) = (x, Stack s (i - 1)) len :: Stack a -> Int len (Stack _ i) = i -- 2. pushStr :: Stack a -> [a] -> Stack a pushStr = foldr (\x s -> push s x) popStr :: (Eq a) => Stack a -> [a] -> Maybe (Stack a) popStr stack xs = if len stack < length xs then Nothing else popStr' stack xs where popStr' stack [] = Just stack popStr' stack (x : xs) = let (y, newStack) = pop stack in if x == y then popStr' newStack xs else Nothing ``` 3. definovať dátovú štruktúru pre reprezentáciu pravidiel deterministického konečného automatu ```haskell data Rule a = R a [a] [a] Int Int deriving (Show,Eq) ``` 4. IO úloha - spracovat súbor do vnútornej reprezentacie pravidiel (štruktúry z predchádzajúcej úlohy) ```haskell -- read_symbol:pop_string:push_string:from_state:next_state -- read symbol je 1 char -- pop_string, push_string sú reťazce, môžu byť prázdne -- from_state, next_state - 1 číslo -- oddelené vždy dvojbodkou a:ab:ab:1:2 b:::2:3 ``` ```haskell readRules fname = do h <- openFile fname ReadMode c <- hGetContents h let lns = lines c let rules = loadLines lns (\r -> (return $! length r) >>= (\_ -> hClose h >> return r) ) $! rules loadLines [] = [] loadLines (l:ls) = R c popS puS ((read fromS)::Int) ((read l4)::Int) : loadLines ls where (c:':':l1) = l (popS,(_:l2)) = span (/=':') l1 (puS,(_:l3)) = span (/=':') l2 (fromS,(_:l4)) = span (/=':') l3 ``` Bonus: isRuleApplicable - najst pravidlo ktore je aplikovatelné pre determinitický konecný automat + napísať štruktúru ### 1. opravný Implementovať IO funkciu, ktorá spojí nasledujúce riadky ak ich dĺžka dokopy je menej, rovná 120. Tieto riadky očísluje na zaciatku, prida dvojbodku a pridá obsah spojených riadkov. Spojené riadky zapíse do súboru fileinput s koncovkou .out ```haskell fx fname = do h <- openFile fname ReadMode o <- openFile (fname ++ ".out") WriteMode c <- hGetContents h hPutStr o $ unlines $ wrap 1 $ lines c hClose o hClose h wrap n (a:b:ls) | (length a + length b) <= 120 = wrap n ((a++b):ls) | True = (show n ++ ":" ++ a) : wrap (n+1) (b:ls) wrap n [l] = [show n ++ ":" ++ l] wrap _ [] = [] ``` <!-- ### 2. opravný --> ## 2020/2021 ### Riadny - zadefinovať datovy typ pre aritmeticke operacie +/- ```haskell data Expr = Val Integer | Add Expr Expr | Sub Expr Expr deriving (Show,Eq) ``` - spraviť funkciu eval pre vyhodnocovanie arit. operacii + a - ```haskell eval :: Expr -> Integer eval (Val v) = v eval (Add e1 e2) = eval e1 + eval e2 eval (Sub e1 e2) = eval e1 - eval e2 ``` - funkcia load, načítať zo subora v tvare prefix operacii do datoveho typu napr. `+ 4 2` ~~alebo to bolo možno so zatvorkami `+(4 2)`~~ (malo by to byť jedno v podstate) ```haskell load :: FilePath -> IO Expr load f = do h <- openFile f ReadMode c <- hGetContents h -- let (res,_) = parse c -- (\ ret -> hClose h >> return ret) $! res parse :: String -> (Expr,String) parse ('+':_:xs) = (Add l r, cont) where (l,rest) = parse xs (r,cont) = parse rest parse ('-':_:xs) = (Sub l r, cont) where (l,rest) = parse xs (r,cont) = parse rest parse is = (Val ((read val) :: Integer), st rest) where (val, rest) = span (\x -> elem x ['0'..'9']) is st [] = [] st (_:t) = t ``` ### 1. opravný 1. LE datova struktura [iné riesenie - 19/20 1. op](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both#1-opravn%C3%BD-term%C3%ADn) ```haskell data LE a = App (LE a) (LE a) | Abs a (LE a) | Var a deriving (Show,Eq) ``` Konkrétny priklad `\x.xy` = `Abs (x) (App (Var x) (Var y))` `\xy.x(xy) = (\x.(\y.x(xy)))` = `Abs (x) (Abs (y) (App (Var x) (App (Var x) (Var y))))` 3. union, intersection, delete ```haskell delete _ [] = [] delete x (y:ys) = if x == y then ys else y : delete x ys -- alebo delete d ys = [ x | x <- ys, x /= d ] union [] ys = ys union (x:xs) ys = if elem x ys then union xs ys else x : union xs ys -- pomocou generatoru intrsect xs ys = [ z | z <- xs, z `elem` ys] -- alebo intrsect [] _ = [] intrsect (x:xs) ys = if elem x ys then x: intrsect xs ys else intrsect xs ys ``` 4. fv - Vytvorenie zoznamu voľných premenných v lambda výraze [riesenie](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both#1-opravn%C3%BD-term%C3%ADn) ```haskell fv (Var v) = [v] fv (App e1 e2) = union (fv e1) (fv e2) fv (Abs x e) = delete x (fv e) ``` 5. `isApp E X F` - ci je beta redukcia `F` za `X` aplikovatelna vo vyraze `E`, tak True ```haskell isApp (App (Abs x e) sub) = isValid e x sub isApp _ = False ``` 6. isValid [riesenie](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both#1-opravn%C3%BD-term%C3%ADn) alebo ```haskell isValid wh var new = vv wh [] where fvn = fv new vv (Var v) bnd | elem v bnd = True | v==var = intrsect bnd fvn == [] | True = True vv (App e1 e2) bnd = vv e1 bnd && vv e2 bnd vv (Abs x e) bnd = vv e (if elem x bnd then bnd else x:bnd) ``` 7. print suboru s tym ze sa na zaciatku pridaju cisla zarovnane vpravo napr.: ``` bla blaa bla bla bla blaa blaaaa bla bla bla bla balalaa banana banana bla ``` výstup: ``` 1 bla 2 blaa 3 bla 4 5 bla bla 6 blaa 7 blaaaa 8 bla bla bla 9 bla 10 balalaa 11 banana 12 banana bla ``` ```haskell numlines f = do h <- openFile f ReadMode c <- hGetContents h let lns = lines c let numl = length lns let maxn = length $ show numl writeln maxn 1 lns hClose h writeln _ _ [] = return () writeln w n (l:ls) = do putStrLn (align n w ++ " " ++ l) writeln w (n+1) ls align n w = [' ' | _ <- [1..(w-length sn)]] ++ sn where sn = show n ``` ### 2. opravný termín /6b/ Definovať funkciu `suf`, ktorá prijíma ako argument zoznam a vracia zoznam všetkých sufixov tohto zoznamu. Definovať funkciu `pre`, ktorá prijíma ako argument zoznam a vracia zoznam všetkých prefixov tohto zoznamu. Funkcia `pre` musí korektne fungovať aj pre nekonečné zoznamy, t.j. `take 10 $ pre [1..]` vráti zoznam desiatich zoznamov. ```haskell suf [] = [[]] suf l@(_:xs) = l : suf xs pre [] = [] pre l = mkpr [] l where mkpr rp (a:as) = reverse (a:rp) : mkpr (a:rp) as mkpr _ [] = [] -- alebo pre _ [] = [[]] pre c (x:xs) = (c++[x]) : (pre (c++[x]) xs) ``` /4b/ Definovať funkciu `substrs`, ktorá prijíma ako argument reťazec a vracia zoznam všetkých podreťazcov tohto reťazca. Definovať funkciu `subsets`, ktorá prijíma ako argument reťazec a vracia zoznam všetky kombinácie tohto reťazca. Môžte použiť aj funkcie `suf` a `pref` z predošlej úlohy. ```haskell substrs xs = concat $ map pre $ suf xs subsets [] = [[]] subsets (a:as) = ss ++ map (a:) ss where ss = subsets as ``` /6b/ Definovať funkciu `ff`, prvý argument je meno súboru v ktorom je každý riadok vo formáte `<key>:<value>` a druhý argument je hľadaný klúč. Funkcia `ff` vráti hodnotu tohto klúča. Ak sa daný klúč v súbore nenachádza, funkcia vráti, že zadaný klúč nenašiel. ```haskell ff f key = do h <- openFile f ReadMode c <- hGetContents h let lns = lines c (\res -> hClose h >> return res) $! find (show key) lns find _ [] = "Not found" find keystr (l:ls) = if take (length keystr) l == keystr then let rest = drop (length keystr) l in if head rest == ':' then tail rest else find keystr ls else find keystr ls ``` ## 2019/2020 ### Předtermín Definovat datovou strukturu, která reprezentuje neorientovaný graf (prostě množina vrcholů a množina hran, kde hrana je dvojice - vrchol, vrchol). ```haskell data Graph v = UG {vertices::[v], edges :: [(v,v)]} deriving (Eq,Show) ``` Vytvořit funkci, co maže izolované uzly v neorientovaném grafu: ```haskell delIso :: Eq v => Graph v -> Graph v delIso (UG vs es) = UG (del vs es) es where del [] _ = [] del (v:vs) es = if elemVEs v es then v : del vs es else del vs es elemVEs :: Eq v => v -> [(v, v)] -> Bool elemVEs v ((v1,v2):es) = v==v1 || v==v2 || elemVEs v es elemVEs _ [] = False ``` Dán typ pro neorientovaný graf, soubor, v něm po řádcích jména uzlů, volný řádek, dvojice jmen oddělených dvojtečkou dává hrany. Zkontrolovat funkcí checkUG, zda-li je to jinak korektní graf. Vrátit graf. ```haskell loadG :: FilePath -> IO (Maybe (GraphString)) loadG file = do h <- openFile file ReadMode c <- hGetContents h let (vs,es) = parse $ lines c let g = UG vs es let gok = check g if gok then hClose h >> (return $ Just g) else hClose h >> return Nothing parse :: [String] -> ([String], [(String, String)]) parse ("":es) = ([], pe es) parse (v:vs) = let (vxs,es) = parse vs in (v:vxs,es) pe :: [String] -> [(String, String)] pe (e:es) =let (v1,x) = span (/=':') e in (v1,tail x) : pe es pe [] = [] ``` Alebo ```haskell loadG file = do h <- openFile file ReadMode c <- hGetContents h let lns = lines c let (vs, es) = parse lns let g = UG vs es let gok = checkUG g if gok then hClose h >> (return $ Just g) else hClose h >> return Nothing parse lns = (vs, es) where (vs, (_:rest)) = span (/="") lns es = pe rest pe ((v1:':':v2):re) = (v1,v2) : pe re pe [] = [] ``` ### Řádný termín Dáno (CS - context sensitive grammar): ```haskell data CSgrammar nt t = CSG nt [Rule nt t] deriving (Show, Eq) data Rule nt t = Rule [Either nt t] [Either nt t] deriving (Show, Eq) ``` Úkoly: ```haskell appRule :: (Eq nt, Eq t) => [Either nt t] -> Rule nt t -> [[Either nt t]] appRule sf (Rule lft rgt) = proc [] sf where -- test every suffix of the sf whether its prefix is not a LHS of the rule, -- if yes then do a derivation else continue proc _ [] = [] proc pref sf@(s:ss) = if repl then (reverse pref ++ rgt ++ rest) : proc (s:pref) ss else proc (s:pref) ss where (repl,rest) = replT lft sf -- if it is a prefix, return rest behind the prefix and True -- otherwise False (and something) replT [] rest = (True,rest) replT (x:xs) (r:rs) = if x==r && res then w else (False,rs) where w@(res,xxs) = replT xs rs replT _ [] = (False,[]) ---------- appAll :: (Eq nt, Eq t) => CSgrammar nt t -> [Either nt t] -> [[Either nt t]] appAll (CSG _ rules) sf = -- apply or rules, if possible if null applicable then [] else concatMap (appRule sf) applicable where -- select only applicable rules applicable = filter (isApp sf) rules ---------- genNew :: (Eq nt, Eq t2) => CSgrammar nt t2 -> [[Either nt t2]] -> [[Either nt t2]] genNew gram inSFs = cleanRep newSFs where newSFs = concatMap (appAll gram) inSFs cleanRep [] = [] cleanRep (sf:sfs) = if elem sf inSFs || elem sf sfs then cleanRep sfs else sf : cleanRep sfs ---------- testInL :: (Eq a, Eq t) => CSgrammar a t -> [Either a t] -> Bool testInL gram@(CSG st _) sentence = if not $ isTerminal sentence then error "Not a sentence on input in testInL" else proc [[Left st]] [] where sentLen = length sentence proc sfs done | elem sentence terms = True | null nterms = False | True = proc notdone (notdone ++ done) where step = genNew gram sfs leqsfs = filter (\s -> length s <= sentLen) step terms = filter isTerminal leqsfs nterms = filter (not . isTerminal) leqsfs notdone = filter (\sf -> not (elem sf done)) nterms ``` ### 1. opravný termín **/3b/** Funkcie na prienik a zjednotenie množín v čistom Haskelli. Jedna musela byť napísaná s využitím generátorov zoznamov a druhá iným spôsobom. ```haskell uni [] l = l uni l [] = l uni (x:xs) l = if x `elem` l then uni xs l else x : uni xs l -- int xs ys = [ z | z <- xs, z `elem` ys] ``` **/2b/** Datová štruktúra pre lambda výrazy. [iné riesenie - 20/21 1. op](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?view#1-opravn%C3%BD) ```haskell data LE a = App (LE a) (LE a) | Abs a (LE a) | Var a deriving (Show,Eq) ``` **/4b/** Vytvorenie zoznamu voľných premenných v lambda výraze ```haskell fv (Var v) = [v] fv (App e1 e2) = uni (fv e1) $ fv e2 fv (Abs v e) = [x | x <- fv e, x /= v] ``` **/8b/** ..... ```haskell isValid (Var _) _ _ = True isValid (App e1 e2) ne v = isValid e1 ne v && isValid e2 ne v isValid (Abs w ee) ne v = proc [w] ee where fvne = fv ne proc bound (Var x) | x == v && x `elem` bound = True | x == v = null $ bound `int` fvne | otherwise = True proc bound (App e1 e2) = proc bound e1 && proc bound e2 proc bound (Abs x ex) = proc (x:bound) ex ``` **/6b/** Niečo s leftmost outermost deriváciou/aplikáciou ```haskell lmom w@(Var _) = w lmom (App e1 e2) = case e1 of (Abs v e) -> if isValid e e2 v then subst e e2 v else (App e1 e2) _ -> App (lmom e1) e2 lmom (Abs w ee) = Abs w (lmom ee) ``` ## 2018/2019 ### Řádný termín - Nadefinovat strukturu pre DirTree. - Urobit funkciu, ktora spocita velkost obsahu složky. - Urobit funkciu ktora vrati zoznam suborov a složek, ktory obsahuju zadany prefix. - Funkcia na rekurzivny vypis obsahu priecinka, nieco ako: ``` (tohle je soucast zadani jeste) -* Root + +-* SubDir + + + file2 + file1 + ``` ```haskell data DirTree a = File String Integer a | Dir String Integer a [DirTree a] deriving (Show,Eq) dirSize (File _ s _) = s dirSize (Dir _ s _ dir) = foldr (+) s $ map dirSize dir findAllPref file dir = findPath "" dir where cmpp [] _ = True cmpp _ [] = False cmpp (p:ps) (f:fs) = p==f && cmpp ps fs findPath p (File name _ _) = if cmpp file name then [p++name] else [] findPath p (Dir name _ _ dir) = let cur = if cmpp file name then [p++name++"/"] else [] other = concatMap (findPath $ p++name++"/") dir in cur++other printDir (File name size _) = putStrLn $ "+ " ++name++" "++show size printDir d@(Dir _ _ _ _) = prd "" d where prd pref (File name size _) = putStrLn $ pref++" "++name++" "++show size prd pref (Dir name _ _ dir) = do putStrLn $ pref++"-* "++name mapM_ (prd $ pref++" +") dir putStrLn $ pref++" ." ``` - BONUS: Red–black tree: ```haskell data Color = Black | Red deriving (Show,Eq,Ord,Bounded,Read,Enum) data RBT a = Nil | Nd Color a (RBT a) (RBT a) deriving (Show,Eq) countBlack Nil = Just 1 countBlack (Nd Red _ l r) = cmpLR 0 (countBlack l) (countBlack r) countBlack (Nd _ _ l r) = cmpLR 1 (countBlack l) (countBlack r) cmpLR x (Just lv) (Just rv) = if lv == rv then Just $ x+lv else Nothing cmpLR _ _ _ = Nothing ``` ### 1. Opravný termín 2-6) Pomocí haskell implementovat lambda výrazy a nějakou práci nad nima, myslím, že tam byl substr ```haskell data LE = Var String | App LE LE | Abs String LE deriving (Eq,Show) remove _ [] = [] remove x (y:ys) = if x == y then ys else y : remove x ys union [] ys = ys union (x:xs) ys = if elem x ys then union xs ys else x : union xs ys fv (Var v) = [v] fv (App e1 e2) = let f1 = fv e1 f2 = fv e2 in union f1 f2 fv (Abs v e) = remove v (fv e) ---------------------------------------------- -- subst what var where subst w var ins = run ins where fvs = fv w comb (Just e1) (Just e2) = Just $ App e1 e2 comb _ _ = Nothing run vv@(Var v) = if v == var then Just w else Just vv run vv@(Abs v e) = if elem var (fv e) && elem v fvs then Nothing else if v == var then Just vv else case run e of Nothing -> Nothing Just e -> Just $ Abs v e run (App e1 e2) = comb re1 re2 where re1 = run e1 re2 = run e2 ---------------------------------------------- -- varianta bez bonusu pp' x = ppp x >> putStrLn "" ppp (Var v) = putStr v ppp (App e1 e2) = do putStr "(" ppp e1 putStr " " ppp e2 putStr ")" ppp (Abs v e) = do putStr "(\\ " putStr v putStr " -> " ppp e putStr")" ---------------------------------------------- -- varianta na bonus pp (Var v) = putStrLn v pp (App e1 e2) = do apply e1 mkp True e2 putStrLn "" pp e = do mkp False e putStrLn "" mkp f (Var v) = do if f then putStr " " else return () putStr v mkp f (Abs v e) = do if f then putStr " ( " else return () putStr $ "\\ "++v nest e if f then putStr " )" else return () mkpf (App e1 e2) = do if f then putStr " (" else return () if f then mkp f e1 else apply e1 mkp True e2 if f then putStr " )" else return () nest (Abs v e) = do putStr $ " "++v nest e neste = do putStr " -> " mkp False e apply (Var v) = do putStr v apply (App e1 e2) = do apply e1 mkp True e2 apply (Abs v e) = do putStr "( " putStr $ "\\ "++v nest e putStr" )" ``` ### 2. Opravný termín 1. Nadefinovat typ BTree, který má předem daný počet potomků a předem zadanou hloubku stromu (zadáno při tvorbě). Listy mají jako hodnotu seznam dvojic (klíč, hodnota), které jsou předem neznámého typu. Uzly mají pak jako hodnotu seznam, který odpovídá potomkům a v každá hodnota v seznamu značí maximální klič, který může daný potomek obsahovat. 2. Vytvoření funkce `create :: Integer -> Integer -> Int -> Int -> BTree`. Kde parametry jsou po sobě: Max klíč, min klíč, hloubka, počet potomků. **BONUS:** za řešení, které se vypořádá s tím, když je počet potomků/pater nesoudělný s celkovým počtem klíčů. 3. Vytvoření funkce `ins :: (typ neuveden)`, která dostává argumenty: klíč, data, strom a která podle zadaného klíče buď upraví hodnotu ve stromu, nebo ji přidá. 4. Vytvoření funkce `allList :: (typ neuveden)`, která ze zadaného stromu vybere z listů všechny hodnoty a vrátí je jako jediný konkatenovaný seznam hodnot. **BONUS:** pokud tato funkce bude pracovat s lineární časovou složitostí ## 2017/2018 ### Řádný termín /4b/ Funkce sort v holem Haskellu, bere seznam hodnot nad tridou Ord a vraci serazene od nejmensiho po nejvetsi. K dispozici fold*, map a operace nad tridou Ord. ```haskell ins x [] = [x] ins x l@(y:ys) = if x < y then x : l else y : ins x ys sort l = foldr ins [] l ``` /13b/ Nadefinovat funkciu pt, ktora berie nazov suboru ako argument. Z tohoto suboru nacita zaznamy ve formatu Cislo_typu_Integer#String, pripadne prazdny riadok. Zaznam reprezentovat datovym typom DLog. Vypsat zaznamy s cisly, ktere jsou nasobkem 5 (cislo mod 5 == 0). Odelene budu tentoraz dvojbodkou (:). Je potrebne uviest typove defincie pre kazdu pouzitu funkciu. Zadane byly typove definicie nekterych funkci pro praci s IO (openFile, hGetContents, lines, unlines, ReadMode, WriteMode, atp.). ```haskell data DLog = Empty | IT Integer String deriving (Show,Eq) pr :: [DLog] -> IO () pr [] = return () pr (Empty:xs) = pr xs pr ((IT i s):xs) = if i `mod` 5 == 0 then putStrLn (show i ++ ":" ++ s) >> pr xs else pr xs pl :: String -> IO () pl fn = do h <- openFile fn ReadMode c <- hGetContents h pr $ proc $ lines c hClose h proc :: [String] -> [DLog] proc [] = [] proc (l:ls) = if null l then Empty : proc ls else (mk l) : proc ls mk :: String -> DLog mk l = IT ((read (takeWhile (/='#') l))::Integer) (tail $ dropWhile (/='#') l) -- Varianta SPAN pl' :: String -> IO() pl' fn = do h <- openFile fn ReadMode c <- hGetContents h pr $ map mkDL $ lines c hClose h mkDL :: String -> DLog mkDL [] = Empty mkDL l = let (n,s) = span (/='#') l in IT ((read n)::Integer) (tail s) ``` /6b/ BONUS otazka - Vytvorit datovou strukturu pro reprezentaci stromu. Vytvorit funkci initTree, ktera dostane jako parametr hodnotu a vytvori nekonecny strom, kde vsechny uzly obsahuji tuto hodnotu. Vytvorit funkci takeLev, ktera vrati strom az po urcitou uroven danou parametrem. ```haskell data Tree a = Nd a (Tree a) (Tree a) | Lf deriving (Show,Eq) takeLev :: Int -> Tree a -> Tree a takeLev 0 _ = Lf takeLev _ Lf = Lf takeLev n (Nd v l r) = Nd v (takeLev (n-1) l) (takeLev (n-1) r) initTree :: a -> Tree a initTree val = Nd val (initTree val) (initTree val) ``` ### 1. opravný termín Vytvořit nekonečný seznam prvočísel. ```haskell primes = 2:[x | x <- [3,5..], isPrime x primes] where isPrime x (p:ps) = (p*p > x) || (x `mod` p /= 0 && isPrime x ps) -- alebo primes = isPrime [2..] where isPrime (p:xs) = p : isPrime [x|x <- xs, x `mod` p > 0] ``` Haskell s dostupnými IO funkcemi. Napsat funkci `mkR`, která dostane jméno souboru. V řádcích souboru se nachází buď FIT login, nebo prázdný řádek, nebo nějaký jiný text. Funkce má vypsat nejdříve počet řádků s validními loginy, pak počet textových řádků, pak počet prázdných řádku a nakonec vypsat všechny tyto validní loginy v náhodném pořadí. Pro generování náhodných čísel je k dispozici funkce `randomRIO :: Random a => (a, a) -> IO a`. ```haskell mkR :: String -> IO () mkR file = do h <- openFile file ReadMode c <- hGetContents h let alllogs = lines c let empty = length $ filter (=="") alllogs let wempty = filter (/="") alllogs let notLogs = length $ filter (not . isLogin) wempty let correct = filter isLogin wempty putStrLn $ show (length correct) ++ "/" ++ show notLogs ++ "/" ++ show empty randLog <- genRand correct putStrLn $ unlines randLog hClose h isLogin :: String -> Bool isLogin x = length x == 8 && head x == 'x' && (all (\x -> elem x ['a'..'z']) $ take 5 $ tail x) && (all (\x -> elem x (['0'..'9']++['a'..'z'])) $ drop 6 x) genRand :: [a] -> IO [a] genRand [] = return [] genRand l = do ir <- randomRIO (0,length l - 1) :: IO Int let (h,t) = splitAt ir l let v = head t let r = tail t mkr <- genRand (h++r) return (v:mkr) ``` ## 2016/2017 ### Řádný termín Holý Haskell. Definovat datový typ pro asociativní pole. Dále napsat funkci test, která ověří, že klíče v asociativním poli jsou unikátní. (7b) ```haskell data AL k d = Val k d (AL k d) | Nil deriving (Show,Eq) test Nil = True test (Val k _ rest) = noKey k rest && test rest noKey _ Nil = True noKey k (Val k' _ rest) = k/=k' && noKey k rest ``` Haskell + prelude (kromě readFile) + nějaké IO funkce jako (hGetContents, hClose, openFile, hPutStr). Napsat funkci fdup, která načte soubor jehož nazev je v jejím parametru a potom tento soubor vypíše na výstup, ale s tím, že pokud jsou na začátku řádku dva znaky +, tak se tento řádek vypíše 2x, ale už bez těchto dvou plusových znaků. Nesmí se změnit pořadí řádků. (6b) ```haskell import System.IO fdup :: FilePath -> IO () fdup file = do h <- openFile file ReadMode c <- hGetContents h putStr $ unlines $ dl $ lines c hClose h dl :: [String] -> [String] dl (('+':'+':l):ls) = l:l:dl ls dl (l:ls) = l : dl ls dl [] = [] ``` Bonus: Definujte strukturu pro termy v prologu a následně funkci unify s dvěma parametry, která vratí nejobecnější unifikátor dvou termu (8b) ```haskell data Term = Var String | ValI Integer | Term String [Term] deriving (Show,Eq) unify (ValI a) (ValI b) = if a==b then Just [] else Nothing unify (Term a as) (Term b bs) = if a==b && length as==length bs then comb [] as bs else Nothing where comb res [] [] = Just res comb res (a:as) (b:bs) = unify a b >>= (\s -> comb (res++s) (map (lapp s) as) (map (lapp s) bs)) unify w@(Var a) t = if w==t then Just [] else Just [(a,t)] unify t w@(Var a) = unify w t lapp ss t = foldl (flip app) t ss app (v,t) w@(Var v') = if v==v' then t else w app s (Term t ts) = Term t (map (app s) ts) app _ x = x ``` # Prolog ## 2022/2023 ### Řádný Implementace funkce prime/1, která ověří, že X je prvočíslo a při zadání prime(X) generuje nekonečkou posloupnost prvočísel. ```prolog allNums(1, 1) :- !. allNums(N, X) :- X is N - 1. allNums(N, X) :- N2 is N - 1, allNums(N2, X). generateinfity(1). generateinfity(X) :- generateinfity(X2), X is X2 + 1. generateinfity(X) :- generateinfity(X). not_prime(X) :- allNums(X, X1), allNums(X, X2), X is X1 * X2. prime2(X) :- not_prime(X), !, fail. prime2(_). prime(1). prime(X) :- generateinfity(X), prime2(X). ``` ### Předtermín Implementace deterministickeho Turingova stroje. Je dano `:- dynamic tol/1 tor/1 state/1 head/1.`, kde state je predikat reprezentujici aktualni stav Turingova stroje. `head` obsahuje symbol pod hlavou. `tol` obsahuje seznam reprezentujici pasku vlevo od hlavy. `tor` stejne tak, ale vpravo od hlavy. V obou pripadech je prvni prvek seznamu policko pasky nejblize hlave. Semantika TM je klasicka, tj. neni mozne se posunout doleva, pokud seznam `tol` je prazdny. Doprava je mozne se posunout vzdy (blank pod hlavou). Krok TS je reprezentovany termem `move(State, Symbol, Action)`, kde `State` je soucasny stav, `Symbol` symbol pod hlavou a `Action` akce, co se ma provest (viz 4). V zadani zminoval, ze mame vzdy co nejvic vyuzivat predikaty vzdy z predchozich ukolu. 1) Implementujte predikat `shl/0`, co pokud je to mozne, pak provede posun hlavy doleva a uspeje. (7b) V dalsich prikladech muzeme predpokladat existenci predikatu shr/0, co posouva doprava, wh/1, co zapisuje pod hlavu a rh/1, co cte symbol pod hlavou. ```prolog shl :- tol([NewHead|NewLTape]), head(OldHead), retract(head(_)), assertz(head(NewHead)), retract(tol(_)), assertz(tol(NewLTape)), tor(OldRTape), retract(tor(_)), assertz(tor([OldHead|OldRTape])). ``` 2) Implementujte predikat `ttol/1`, co do prvniho argumentu vrati obsah cele pasky (5b za "oneliner", jinak 3b) ```prolog ttol(FullTape) :- tol(LTape), tor(RTape), head(Head), reverse(LTape, RevLTape), append(RevLTape, [Head|RTape], FullTape). ``` 3) Implementujte predikat findmove(Moves, Action), co ve vstupnim seznamu `Moves` (jeho obsah jsou termy move) najde proveditelnou akci a zunifikuje ji do argumentu `Action`. Pak tam byla asi 4radkova poznamka o tom, ze v seznamu Moves muzou byt i kroky, ktery jdou provest s jakymkoliv symbolem apod, ale mame brat v potaz jen validni. Zaroven mame umoznit backtracking v pripade, ze tech validnich pohybu je vic (ze to v realu nenastane, ale mame to podporovat) (3b) ```prolog findmove(Moves, Action) :- member(move(State, Symbol, Action), Moves), head(Symbol), state(State). ``` 4) Implementujte predikat action/1, ktery dostane jako argument akci a provede ji. Akce muze byt nasledujiciho tvaru (5b): a) `act(w, Sym, State)` - zapise Sym pod hlavu a presune se do stavu State b) `act(r, State)` - posune hlavu doprava a presune se do stavu State c) `act(l, State)` - posune hlavu doleva a presune se do stavu State ```prolog action(act(r, State)) :- shr, retract(state(_)), assertz(state(State)). action(act(l, State)) :- shl, retract(state(_)), assertz(state(State)). action(act(w, Sym, State)) :- retract(head(_)), assertz(head(Sym)), retract(state(_)), assertz(state(State)). ``` 5) Implementujte predikat work(Moves, InitState, Tape, FinalStates), ktery dostane mozne prechody TS `Moves` (viz format `move`), pocatecni stav `InitState`, seznam koncovych stavu `FinalStates` a pocatecni obsah pasky `Tape` a uspeje, pokud pro tohle zadani existuje reseni. Tim, ze je to deterministicky TM, mame uvazovat, ze je mozne provest vzdy maximalne jeden krok z `Moves`, tj. neuvazovat backtracking. (10b) ```prolog work(Moves, InitState, [InitHead, RestOfTape], FinalStates) :- assertz(state(InitState)), assertz(tol([])), assertz(tor(RestOfTape)), assertz(head(InitHead)), doMoves(Moves, FinalStates). isInFinal(Finals) :- state(CurrentState), member(CurrentState, Finals), !. doMoves(Moves, FinalStates) :- isInFinal(FinalStates), !. doMoves(Moves, FinalStates) :- findmove(Moves, NextAction), action(NextAction), doMoves(Moves, FinalStates). ``` ## 2021/2022 ### 1. termín 1) [12b.] Např.: [[1, 2], [2,3,4], []], [[3,4], [1], [1,2,3,4]] Vstupem seznam množin, Sjednocení všech množin tvoří univerzum, Pro každou vstupní množinu vypsat doplněk vůči tomuto univerzu. K dispozici holý prolog. ```prolog complements(I,O) :- getUniverse(I,U), getComplements(I,U,O) . getComplements([],_,[]). getComplements([HI|TI],U,[HO|TO]) :- getDifference(HI,U,HO), getComplements(TI,U,TO) . getDifference(U,U,[]). getDifference(_,[],[]). getDifference(S,[HU|SU],[HU|R]) :- notMember(HU,S), getDifference(S,SU,R) . getDifference(S,[_|SU],R) :- getDifference(S,SU,R) . getUniverse([],[]). getUniverse([H|T],U) :- getUniverse(T,TU), append(H,TU,U) . notMember(_,[]). notMember(X,[H|T]) :- notMember(X,T), X \= H . member(_,[]) :- false. member(X,[X|_]). member(X,[_|T]) :- member(X,T) . append([],X,X). append(X,[],X). append(L1,L2,R) :- appendInner(L1,L2,T), removeDuplicates(T,R) . appendInner([],X,X). appendInner(X,[],X). appendInner([H1|T1],L,[H1|R]) :- appendInner(T1,L,R) . removeDuplicates([],[]). removeDuplicates([H|T],[H|R]) :- notMember(H,T), removeDuplicates(T,R) . removeDuplicates([_|T],R) :- removeDuplicates(T,R) . ``` 2) [4b.] splt chová se jako span v Haskellu. splt (P, A, AT, AF) vstupem predikát P a seznam A. Výstupem dva seznamy: AT = souvislý prefix A, kde pro všechny prvky platí predikát P. AF = zbytek seznamu A(od prvního prvku, kde P neplatí, do konce). K dispozici holý prolog. ```prolog % Pozn.: call a not nie sú v holom prológu, ale bez toho to nejde implementovať splt(_, [], _, []). splt(P, [H|T], N, AF) :- call(P, H), N = [H|Rest], splt(P, T, Rest, AF). splt(P, [H|T], [], [H|T]) :- not(call(P, H)). % alebo ine riesenie splt(P,[HA|TA],[HA|ATA],AF) :- call(P, HA), splt(P,TA,ATA,AF) . splt(_,L,[],L). ``` 3) [asi 6b.] Vyhledání všech klíčů ve stromě, které se vážou k dané hodnotě. Klíče ve stromě byly unikátní, hodnoty byly neznámého typu ```prolog // test příslušnosti do seznamu elem(H, [H|_]) :- !. elem(H, [_|T]) :- elem(H, T), !. // sjednocení množin union([], R, R) :- !. union(R, [], R) :- !. union([H|T], U, R) :- elem(H, U), union(T, U, R), !. union([H|T], U, [H|R]) :- union(T, U, R), !. // definice stromu // strom může být prázdný, to jsem vyjádřil predikátem myTree(stop). myTree(stop). // strom může mít tři položky // -> dvojici (klíč, hodnota), to v prologu vyjádřím zápisem -(_K, _V), spojovník a závorky vyjadřuji, že je to dvojice, podtržítko na začátku vyjadřuje anonymitu. // -> levý podstrom // -> pravý podstrom // musí nutně platit, že Left i Right jsou stromy. myTree(tree(-(_Key, _Value), Left, Right)) :- myTree(Left), myTree(Right). // hledání klíče dle libovolné hodnoty v prázdném stromě vrátí prázdný seznam jako odpověď getKeysByValue(_, myTree(stop), []) :- !. // při hledání klíčů dle hodnoty Val naunifikuji do A, B klíče odpovídající hodnotě Val z podstromů Left, Right (po řadě), sjednotím je do R a přidám k nim Key, pokud hodnota aktuálního uzlu odpovídá hledané hodnotě getKeysByValue(Val, myTree(tree(-(Key, Value), Left, Right)), [Key|R]) :- Val == Value, getKeysByValue(Val, myTree(Left), A), getKeysByValue(Val, myTree(Right), B), union(A, B, R), !. Zkuste si třeba: myTree(stop). myTree(tree(-(1, 2), stop, tree(-(2, 3), stop, stop))). getKeysByValue(2, myTree(tree(-(10,2), tree(-(1, 2), stop, stop), tree(-(100, 0), stop, tree(-(1000, 2), stop, stop)))), R). ``` 4) [asi 8b.] ~jsme v nekonečném stavovém prostoru, prakticky všechno je neznámé nebo dané parametrem a úkolem je udělat krok/cestu v rámci stavového prostoru ... no idea ### 1. opravný 1. vytvoriť funkciu notelem, ktorá failne ak sa polozka nachádza v zadanom liste notelem(+Val,+List). - moze sa poouzit !, fail ```prolog notmem(_,[]). notmem(X,[X|_]) :- !, fail. notmem(X,[_|XS]) :- notmem(X,XS). ``` 2. implementovat `keyval(Key, Val)` (cca 15b) - pouzite assert, nonvar, var, ! - ak sú zadané key aj value skúsi vložiť do pamati ```prolog keyval(1, jeden) keyval(2, dva) keyval(3, tri) ``` - ak sú zadany len key, nájde value z pamäti ```prolog keyval(1, V) V = jeden keyval(5, V) false. ``` - len value - nájde keys ```prolog keyval(K, jeden) K = [1,11] ``` - keyval(K, V) - vypíše z pamati ```prolog K = 1 V = jeden K = 2 V = dva ``` ```prolog :-dynamic kvpair/2. keyval(Key,Val) :- var(Key), var(Val), !, kvpair(Key,Val). keyval(Key,Val) :- var(Val), !, kvpair(Key,Val). keyval(Key,Val) :- var(Key), !, collect(Key,Val). keyval(Key,Val) :- testOrInsert(Key,Val). collect(Key,Val) :- kvpair(K,Val), !, take([K],Val,Key). take(Keys,Val,Res) :- kvpair(K,Val), notmem(K,Keys), !, take([K|Keys],Val,Res). take(Keys,_,Keys). testOrInsert(Key,Val) :- kvpair(Key,V), !, Val=V. testOrInsert(Key,Val) :- assertz(kvpair(Key,Val)). ``` 3. remKey(Key) - odstráni Key z pamati, neuspeje ak nenájde, remAll - uspeje vždy raz, odstráni predikáty z pamäti ```prolog remKey(Key) :- kvpair(Key,Val), retract(kvpair(Key,Val)). remAll :- kvpair(Key,Val), !, retract(kvpair(Key,Val)), remAll. remAll. ``` 4. vytvoriť zjednocovanie zanorenych listov do obycajného listu - zostanú len najvonkajšejšie zátvorky (najľavejšia+najpravejšia) `destr([[1,[]],[2,[3],4],[[], 5]],[1,2,3,4,5])` ```prolog destr([],[]). destr([[]|X],Y) :- destr(X,Y),!. destr([[H|T]|X],Y) :- destr([H|[T|X]],Y),!. destr([H|T],[H|TT]) :- destr(T,TT). ``` ### Riadny Lambda calcul v prologu - viz [riadny 19/20](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?view#riadny) ```prolog /* 1 var(V) variable abs(f,abs(x,var(x))) ... 0 app(E1,E2) application abs(f,abs(x,app(var(f),var(x)))) ... 1 abs(V,E) abstraction abs(f,abs(x,app(var(f),app(var(f),var(x))))) V - variable E,E1,E2 - lambda-expression */ /* 2 */ fv(var(V),[V]). fv(app(E1,E2),Res) :- fv(E1,R1), fv(E2,R2), union(R1,R2,Res). fv(abs(V,E),Res) :- fv(E,FV), del(FV,V,Res). union([],L,L) :- !. union(L,[],L) :- !. union([X|XS],YS,Res) :- member(X,YS), !, union(XS,YS,Res). union([X|XS],YS,[X|Res]) :- union(XS,YS,Res). del([],_,[]). del([X|XS],X,XS) :- !. del([X|XS],V,[X|Res]) :- del(XS,V,Res). /* 3 */ valid(Where,What,Var) :- fv(What,FV), chk(Where,Var,[],FV). chk(var(V),V,BV,FV) :- intersect(BV,FV,I), I==[]. chk(app(E1,E2),V,BV,FV) :- chk(E1,V,BV,FV), chk(E2,V,BV,FV). chk(abs(V,_),V,_,_) :- !. chk(abs(W,E),V,BV,FV) :- chk(E,V,[W|BV],FV). intersect([],_,[]) :- !. intersect(_,[],[]) :- !. intersect([X|XS],YS,[X|RS]) :- member(X,YS),!, intersect(XS,YS,RS). intersect([_|XS],YS,RS) :- intersect(XS,YS,RS). /* 4 */ isEta( abs( V, app(E,var(V)) ) ) :- fv(E,FV), not(member(V,FV)). isEta(app(E1,E2)) :- isEta(E1) ; isEta(E2). isEta(abs(_,E)) :- isEta(E). ``` <!-- ### 1. opravný --> <!-- ### 2. opravný --> ## 2020/2021 ### Řádný termín Magicke štvorce - NxN matica s hodnotami 1,...,N^2 hodnotami - info: 1x1 je jednoduche spravit; 2x2 nejde; 3x3 ... - suma v riadku, stlpci a uhlopriecke je 15 (pri 3x3) - viz http://www.mathematische-basteleien.de/magsquare.htm 8....1....6 3....5....7 4....9....2 1. spraviť reprezentaciu matice NxN `cms(+N, -M)` - `cms(N,M)` skontroluje, že N je číslo, M je pre navrat konečnej matice Magic sqaure - `cel(N,R)` vytvorí list riadku - `car(N,N,M)` vytvorí výslednú maticu - list listov (list riadkov pomocou `cel`) ```Prolog cms(N,M) :- integer(N), car(N,N,M). cel(N,[_|T]) :- N>0, NN is N-1, cel(NN,T). cel(N,[]) :- N =< 0. car(N,A,[L|T]) :- A>0, cel(N,L), AA is A-1, car(N,AA,T). car(_,A,[]) :- A =< 0. ``` 2. na poziciu X, Y v matici doplnit hodnotu V a vrátiť výslednú maticu - `set(X,Y,M,V,MM)` ```Prolog set(X,Y,[H|T],V,[H|TT]) :- X > 1, XX is X-1, set(XX,Y,T,V,TT). set(X,Y,[H|T],V,[HH|T]) :- X == 1, yset(Y,H,V,HH). yset(Y,[H|T],V,[H|TT]) :- Y > 1, YY is Y-1, yset(YY,T,V,TT). yset(Y,[_|T],V,[V|T]) :- Y == 1. ``` 3. doplniť zvyšnú maticu (pomocne funkcie - f. co ti da nevyuzite hodnoty, ...) ```Prolog solve(N,M,M) :- getFree(M,[]),!, chk(N,M). solve(N,M,Res) :- getFree(M,Free), concat(M,L), assertz(size_of(N)), step(Free,[],L,[],0,ResL), toMS(N,ResL,Res). solve(_,_,_) :- retractall(size_of(_)), !,fail. step([],[],[],Rev,_,Res) :- reverse(Rev,Res), size_of(N), toMS(N,Res,M), chk(N,M). step(Free,[],[X|XS],Rev,Ctr,Res) :- integer(X), CC is Ctr+1, try(Free,[],XS,[X|Rev],CC,Res). step([F|FS],WasFree,[X|XS],Rev,Ctr,Res) :- var(X), % Try F CC is Ctr+1, append(FS,WasFree,Xfree), try(Xfree,[],XS,[F|Rev],CC,Res). step([F|FS],WasFree,[X|XS],Rev,Ctr,Res) :- var(X), % F failed step(FS,[F|WasFree],[X|XS],Rev,Ctr,Res). /* rozsireni prubezny test */ try(Xfree,[],XS,Rev,Ctr,Res) :- size_of(N), D is Ctr div N, M is Ctr mod N, D > 1, M == 0,!, reverse(Rev,O), check(N,D,O), /* prubezny test */ step(Xfree,[],XS,Rev,Ctr,Res). try(Xfree,[],XS,Rev,Ctr,Res) :- step(Xfree,[],XS,Rev,Ctr,Res). /* .............. */ /* rozsireni o prubezny test - test */ sumupto(N,Rest,0,Rest) :- N =< 0. sumupto(N,[X|XS],S,Rest) :- N > 0, N1 is N-1, sumupto(N1,XS,Sub,Rest), S is X+Sub. check(N,Rows,L) :- Rows>1, sumupto(N,L,Val,Rest), RR is Rows-1, chcl(N,RR,Val,Rest). chcl(_,Rows,_,_) :- Rows == 0. chcl(N,Rows,Val,L) :- Rows > 0, sumupto(N,L,Val,Rest), RR is Rows-1, chcl(N,RR,Val,Rest). /* -------------- */ /* -------------- */ /* -------------- */ /* co bylo hotove a k uziti, aby to byly komplet, tak zde */ toMS(_,[],[]). toMS(N,L,[HH|Rest]) :- toArray(N,L,HH,TT), toMS(N,TT,Rest). toArray(0,L,[],L). toArray(N,[H|T],[H|TT],Rest) :- N > 0, N1 is N-1, toArray(N1,T,TT,Rest). /* .............. */ getFree(MS,ListN) :- getUsed(MS,Used), length(MS,N), genAll(N,All), sd(All,Used,ListN). genAll(N,L) :- NN is N*N, mkl(1,NN,L). mkl(N,M,[N|T]) :- N < M, NN is N+1, mkl(NN,M,T). mkl(N,M,[M]) :- N == M. getUsed([],[]). getUsed([[]|T],R) :- getUsed(T,R). getUsed([[H|T]|TT],R) :- var(H),!, getUsed([T|TT],R). getUsed([[H|T]|TT],[H|R]) :- getUsed([T|TT],R). sd([],_,[]). sd([X|XS],S,R) :- member(X,S),!, sd(XS,S,R). sd([X|XS],S,[X|R]) :- sd(XS,S,R). /* .............. */ concat(L1,L2) :- append(L1,L2). /* .............. */ chk(N,M) :- integer(N), N>0, length(M,N), maplist(length,M,LN), all(N,LN), append(M,AllVals), genAll(N,AllPos), vals(AllPos,AllVals), csum(M). all(_,[]). all(N,[N|T]) :- all(N,T). vals([],[]). vals(Pos,[X|XS]) :- integer(X), member(X,Pos), delete(Pos,X,NewPos), vals(NewPos,XS). csum(M) :- maplist(sum_list,M,[H|T]), transp(M,MT), maplist(sum_list,MT,TT), all(H,T), all(H,TT), cdiag(H,0,M), maplist(reverse,M,MR), cdiag(H,0,MR). cdiag(H,S,[[V]]) :- SS is S+V, H == SS. cdiag(H,S,[[V|_]|T]) :- SS is S+V, maplist(tail,T,TT), cdiag(H,SS,TT). tail([_|T],T). head([H|_],H). transp([],[]). transp([[]|_],[]). transp(XSS,[HHS|RT]) :- maplist(head,XSS,HHS), maplist(tail,XSS,TSS), transp(TSS,RT). ``` - BONUS: definovat datovy typ obojsmerne viazany zoznam + funkciu ktora zisti dlzku zoznamu - prvky v zozname sa neopakuju ### 1. opravný 1. `genV(+Start,+End, -V)` vygenerovat hodnoty od Start do End vratane (ocakavany vstup je zadany dobre); Start, End su kladné čisla ```prolog genV(1,3,V). V=1 V=2 V=3 ``` ```prolog genL(E,E,[E]). genL(S,E,[S|T]) :- S<E, SS is S+1, genL(SS,E,T). genV(S,E,V) :- genL(S,E,L), append(_,[V|_],L). % alebo ina verzia: genV(S,E,S):- S < (E + 1). genV(S, E, V):- S < (E + 1), NS is S + 1, genV(NS, E, V). % alebo este trochu ina verzia genV(S, E, S) :- S =< E. genV(S, E, V) :- S < E, NS is S + 1, genV(NS, E, V). ``` 2. `solve(+Xm, +Ym, -X, -Y, -Z)` vyriesit prehladavanim pre `1, ...Xm` a `1...Ym` rovnicu `x²+y²=z²` ```prolog solve(5,5,X,Y,Z) x=3 y=4 z=5 x=4 y=3 z=5 atd... ``` ```prolog solve(Xm,Ym,X,Y,Z) :- genV(1,Xm,X), genV(1,Ym,Y), ZZ is X*X+Y*Y, genZ(X,Y,ZZ,Z). genZ(M1,M2,ZZ,Z) :- max(M1,M2,S), Zm is 2*S+1, genV(S,Zm,Z), Z2 is Z*Z, Z2==ZZ. max(X,Y,X) :- X >= Y. max(X,Y,Y) :- X < Y. % alebo menej komplikovane solve(Xm, Ym, X, Y, Z) :- genV(1, Xm, X), genV(1, Ym, Y), L is X * X + Y * Y, genV(1, L, Z), P is Z * Z, L == P. ``` 3. `resolve` ako `solve` ale zadava da lava a prava strana rovnice ```prolog resolve(5,5,X*X*X+Y*Y*Y,Z*Z,X,Y,Z) X = 1, Y = 2, Z = 3 X = 2, Y = 1, Z = 3 X = 2, Y = 2, Z = 4 ``` ```prolog resolve(Xm,Ym,Xe,Ze,X,Y,Z) :- genV(1,Xm,X), genV(1,Ym,Y), ZZ is Xe, genF(ZZ,Ze,Z). genF(ZZ,Ze,Z) :- Zm is ZZ+1, genV(1,Zm,Z), Z2 is Ze, test(Z2,ZZ). % skor tu malo byť Z2 = ZZ % alebo menej komplikovane resolve(Xm, Ym, Le, Pe, X, Y, Z) :- genV(1, Xm, X), genV(1, Ym, Y), L is Le, genV(1, L, Z), P is Pe, L == P. ``` 4. `zip(L1,L2,L12)` ```prolog zip([1,2,3,4],[a,b,c],R) R = [(1,a),(2,b),(3,c)] ``` ```prolog zip([],_,[]):-!. zip(_,[],[]):-!. zip([X|XS],[Y|YS],[(X,Y)|T]) :- zip(XS,YS,T). ``` ### 2. opravný termín Definované: ```prolog :- dynamic p/1. node(t,c,92). node(t,d,45). node(t,g,71). node(t,h,40). node(t,p,67). node(d,c,50). node(d,g,42). node(d,h,20). node(d,p,54). node(c,g,36). node(c,h,54). node(c,p,58). node(g,h,32). node(g,p,22). node(h,p,36). ``` /8b/ Holý prolog + `not`, `!`, `member`, `length`. Napísať predikát `getAllNodes(+Num)`, ktorý do jediného parametru unifikuje počet jedinečných uzlov. ```prolog getNodes(L,Res) :- node(N,_,_), not(member(N,L)),!, getNodes([N|L],Res). getNodes(L,Res) :- node(_,N,_), not(member(N,L)),!, getNodes([N|L],Res). getNodes(L,L). getAllNodes(Len) :- getNodes([],L), length(L,Len). ``` /22b/ Travelling salesman problem. Napísať predikát `tsp`, ktorý nájde najlacnejšiu trasu medzi dvoma uzlami. ```prolog getlen(X,Y,L) :- node(X,Y,L). getlen(X,Y,L) :- node(Y,X,L). tsp(From,How,Price) :- setof(J,solve(From,J),LL), best(LL,How,Price). best([j(H,P)],H,P) :- !. best([j(_,P)|R],RH,RP) :- best(R,RH,RP), RP<P,!. best([j(H,P)|_],H,P). solve(From,j(How,Price)) :- getAllNodes(LAll), CL is LAll+1, getlen(From,Nxt,L), go(Nxt,From,Way,P), Price is P+L, How = [From|Way], length(How,CL). go(T,T,[T],0) :- !. go(F,T,[F|R],PP) :- assertz(p(F)), getlen(F,N,P), not(p(N)), go(N,T,R,PR), PP is P+PR. go(F,_,_,_) :- p(F), retract(p(F)), !, fail. ``` ## 2019/2020 ### Predtermin Klika - viz [popis](https://fituska.eu/download/file.php?id=13594) ```prolog :- dynamic edge/2, node/2. /* 1 */ /* Dána reprezentace pro neorientovaný graf na vstupu ug(vertices,edges), uložit jako node(uzel,-1) a edge(uzel,uzel) */ insUG(ug(VS,ES)) :- inV(VS), inE(ES). inV([]). inV([V|VS]) :- assertz(node(V,-1)), inV(VS). inE([]). inE([p(V1,V2)|ES]) :- assertz(edge(V1,V2)), inE(ES). /* 2 */ /* Otestovat, zda zadaná množina uzlů je klika - graf je v DB */ isC([_]) :- !. isC([N|NS]) :- checkE(N,NS),isC(NS). isC([]). checkE(_,[]). checkE(N,[X|XS]) :- (edge(N,X);edge(X,N)),!, checkE(N,XS). /* 3 */ /* Doplnit stupeň uzlu ne každému, kde to ještě není nastaveno */ deg(Node,Deg) :- findall(N, edge(Node,N), L1), findall(N, edge(N,Node), L2), length(L1,N1), length(L2, N2), Deg is N1+N2. mkDegs :- node(V,-1),!, deg(V,D), retract(node(V,-1)), assertz(node(V,D)), mkDegs. mkDegs. /* 4 */ /* Zkusit pro uzel N a jeho okolí Nbr najít mezi nimi co kliku velikosti D-1. Sousedů je dostatek, nebo více, na to je spoleh. K dipozici je take jako z Haskellu a perm pro permutace. */ tryC(D,N,Nbrs, [N|Nbrs]) :- length(Nbrs,Len), Len == D, !, isC([N|Nbrs]). tryC(D,N,Nbrs, [N|L]) :- perm(Nbrs,NX), take(D,NX,L), isC([N|L]). /* 5 */ /* Zkusit najít kliku v rozsahu (CD,D>, kde je hlavní uzel N a jeho okolí Nbrs. Klika bude Clique, když se najde. */ repTry(CD,D,N,Nbrs,Clique) :- D > CD, tryC(D,N,Nbrs,Clique),!. repTry(CD,D,N,Nbrs,Clique) :- DD is D-1, DD > CD, repTry(CD,DD,N,Nbrs,Clique). /* 5 */ /* Udělat predikát, co zkusít najít větší kliku než zadanou, ideálně tu největší. Dostane aktuální velikost kliky - 1 (AcD), tu kliku a nějakou vrátí (ResClique). K dispozici má getNbr, co vrací sousedy pro uzel a fltr, co zjistí, jestli to má k aktuální klice smysl v daném okolí kliku hledat a když ano, tak vrátí to okolí, kde se to ověří. */ findC(ActD,_,ResClique) :- node(N,D), D>ActD, getNbr(N,AllNbrs), fltr(D,AllNbrs,NewD,NewNbrs), NewD>ActD, repTry(ActD,NewD,N,NewNbrs,Clique), length(Clique,LenC), DC is LenC - 1, DC > ActD, !, findC(DC,Clique,ResClique). findC(_,C,C). /* A když to vyjde, bude toto fungovat. */ getClique(ug(VS,ES),Clique) :- retractall(node(_,_)), retractall(edge(_,_)), insUG(ug(VS,ES)), mkDegs, node(N,_), findC(0,[N],Clique),!. ``` ### riadny Lambda Kalkul v prologu: - viz [riadny 21/22](https://hackmd.io/JB5CYyAZRoS55_WFMbdUkQ?both#Riadny2) ```prolog /* 1 definicie + ako reprezentovat LE v prologu lambda expression: variable - var(atom) lambda-application - app(E1,E2) lambda-abstraction - abs(atom,E) where E, E1, E2 are lambda expressions atom is a name of variable */ /* 2 */ fv(E,Vs) :- fv3(E,[],Vs). fv3(var(V),Bs,[]) :- member(V,Bs), !. fv3(var(V),_,[V]). fv3(app(E1,E2),Bs,Vs) :- fv3(E1,Bs,V1), fv3(E2,Bs,V2), uni(V1,V2,Vs). fv3(abs(V,E),Bs,Vs) :- fv3(E,[V|Bs],Vs). uni([],L, L). uni([X|XS],L, YS) :- member(X,L), !, uni(XS,L, YS). uni([X|XS],L, [X|YS]) :- uni(XS,L, YS). /* 3 */ subst(Where,What,Var,Res) :- fv(What,FW), sb(Where,What,FW,Var,[],Res). sb(var(X),_,_,Var,_,var(X)) :- X \= Var, !. sb(var(X),_,_,X,Bs,var(X)) :- member(X,Bs). sb(var(X),What,FW,X,Bs,What) :- notIn(FW,Bs), !. sb(app(E1,E2),What,FW,Var,Bs,app(R1,R2)) :- sb(E1,What,FW,Var,Bs,R1), sb(E2,What,FW,Var,Bs,R2). sb(abs(V,E),What,FW,Var,Bs,abs(V,Res)) :- sb(E,What,FW,Var,[V|Bs],Res). notIn([],_) :- !. notIn(_,[]) :- !. notIn([X|_],YS) :- member(X,YS), !, fail. notIn([_|XS],YS) :- notIn(XS,YS). /* 4 */ doBR(app(abs(Var,Where),What), Res) :- subst(Where, What, Var, Res). doBR(app(E1,E2), app(Res,E2)) :- doBR(E1,Res). doBR(app(E1,E2), app(E1,Res)) :- doBR(E2,Res). doBR(abs(V,E), abs(V,Res)) :- doBR(E,Res). /* 5 */ findR(S,S,_,[S]) :- !. findR(_,_,N,_) :- N > 5, !, fail. findR(S,E,N,[S|Rest]) :- assertz(la(S)), doBR(S,NS), not(la(NS)), NN is N+1, findR(NS,E,NN,Rest). findR(S,_,_,_) :- la(S), retract(la(S)), !, fail. ``` ### 1. opravný 1. ```prolog % kontrola ze list Y obsahuje prefix list X isPref([], _) :-!. isPref([X|XS],[X|YS]) :- isPref(XS,YS). % dropuje list L, kym list X obsahuje elementy dropL([],L,L) :- !. dropL([_|XS],[_|YS],R) :- dropL(XS,YS,R). ``` 2. ```prolog replaceAll(Where,What,By,Res) :- length(Where,WL), length(What,AL), AL =< WL, !, doRepl(Where,What,AL,By,Res). replaceAll(Where,_,_,Where). doRepl([W|WS], What, AL, By, [R1|Res]) :- isPref(What, [W|WS]), !, dropL(What, [W|WS], Rest), append(By, Rest, R1), doRepl(WS, What, AL, By, SR), prep(W, SR, Res). doRepl(WS, _, AL, _, []) :- length(WS, WL), WL < AL, !. doRepl([W|WS], What, AL, By, Res) :- doRepl(WS, What, AL, By, SR), prep(W, SR, Res). prep(X, XS, [X|XS]). ``` 3. ```prolog replLines([], _,_, []). replLines([L|LS], What,By, [RL|RLS]) :- replaceAll(L,What,By, RL), replLines(LS, What,By, RLS). ``` 4. ```prolog mkLines([[]|LS],RS) :- !, mkLines(LS,RS). mkLines([LX|_],_) :- length(LX,L), L>1, !, fail. mkLines([[L]|LS],[L|RS]) :- mkLines(LS,RS). mkLines([],[]). ``` 5. ```prolog find(Lines1,SR,Lines2,Res) :- assertz(done(Lines1,[])), findP(Lines1,SR,Lines2,Res). find(Lines1,_,_,_) :- retract(done(Lines1,[])), !,fail findP(Lines1,_,Lines1,[]) :- !. findP(Lines1,SR,Lines2,[T|TS]) :- length(Lines2,LL2), append(_,[T|_],SR), not(done(_,T)), T =.. [sr,What,By], replLines(Lines1,What,By,LRep), mkLines(LRep,LinesNew), length(LinesNew,LN), LN >= LL2, not(done(LinesNew,_)), asr_ret(LinesNew,T), findP(LinesNew,SR,Lines2,TS). asr_ret(L,T) :- assertz(done(L,T)). asr_ret(L,T) :- retract(done(L,T)), !,fail. ``` ## 2018/2019 ### Řádný termín 1. Napsat predikaty pro zjisteni, jestli je prvek v seznamu a pro odstraneni hodnoty ze seznamu. Dat si bacha na to, ze seznam muze obsahovat nenavazane promenne a prvek se s nima nesmi zunifikovat. 2. Transpozice matice. 3. Predikat, ktery bere sudoku reprezentovane matici 9x9 a vytvori seznam bloku 3x3. 4. Predikat, ktery pro danou pozici v sudoku zjisti seznam moznych hodnot, ktere se na to policko daji doplnit. Mame k dispozici predikat getIth(X,M,L), ktery z matice M vytahne radek X do seznamu L. A meli jsme napsane, jak se z pozice [X,Y] v matici urci cislo bloku. 5. Predikat solves, ktery s vyuzitim definovanych predikatu vyresi sudoku, ktere ma na vstupu zadane matici 9x9. Neobsazene policka jsou reprezentovana volnou promennou. Muzem pouzit getIth, getRC, ktery pro zadanou pozici a matici vrati prvek na tech souradnicich a setRC, ktery nastavi prvek v matici na nejakou hodnotu. ```prolog /* -- vv -- */ elemNV(X,[V|_]) :- nonvar(V), V=X, !. elemNV(X,[_|VS]) :- elemNV(X,VS). remVals([],_,[]). remVals([X|XS],L,R) :- elemNV(X,L), !, remVals(XS,L,R). remVals([X|XS],L,[X|R]) :- remVals(XS,L,R). /* -- vv -- */ trp([[]|_],[]) :- !. trp(XSS,[L|LS]) :- heads(XSS,L,YSS), trp(YSS,LS). heads([[H|TS]|XSS],[H|T],[TS|YSS]) :- heads(XSS,T,YSS). heads([],[],[]). /* -- vv -- */ blocks([[A,B,C|CS],[D,E,F|FS],[G,H,I|IS]|XSS],[[A,B,C,D,E,F,G,H,I]|LS]) :- blocks([CS,FS,IS|XSS],LS). blocks([[],[],[]|XSS],LS) :- blocks(XSS,LS). blocks([],[]). /* -- vv -- */ valsFor(X,Y,M,[V|VS]) :- getIth(X,M,L), remVals([1,2,3,4,5,6,7,8,9],L,[H|T]), trp(M,TM), getIth(Y,TM,R), remVals([H|T],R,[HH|TT]), blocks(M,BM), P is (3*((X-1)//3)) + (1+((Y-1)//3)), getIth(P,BM,B), remVals([HH|TT],B,[V|VS]). /* -- vv -- */ solves(M) :- search(1,1,M). search(R,C,M) :- getRC(R,C,M,V), nonvar(V), !, goNonvar(R,C,M). search(R,C,M) :- valsFor(R,C,M,VS), testVals(R,C,M,VS). goNonvar(9,9,_) :- !. goNonvar(R,C,M) :- newRC(R,C,RR,CC), search(RR,CC,M). testVals(9,9,M,[X]) :- getRC(9,9,M,X), !. testVals(R,C,M,[X|_]) :- getRC(R,C,M,X), newRC(R,C,RR,CC), search(RR,CC,M). testVals(R,C,M,[_|XS]) :- testVals(R,C,M,XS). /* co bylo k dispozici jako hotove, pro uplnost */ newRC(R,C,R,CC) :- C<9, !, CC is C+1. newRC(R,9,RR,1) :- R<9, RR is R+1. getIth(1,[X|_],X) :- !. getIth(N,[_|XS],X) :- N1 is N-1, getIth(N1,XS,X). getRC(R,C,M,V) :- getIth(R,M,L), getIth(C,L,V). ``` ### 1. opravny termín Dámy ```prolog /* -------------------------------------------------------------------- */ genAll(0,_,[]). genAll(Size,Col,[pos(Col,Size)|Res]) :- Size > 0, SS is Size - 1, genAll(SS,Col,Res). /* -------------------------------------------------------------------- */ inConf([pos(X,_)|_], pos(X,_)) :- !. inConf([pos(_,Y)|_], pos(_,Y)) :- !. inConf([pos(XX,YY)|_], pos(X,Y)) :- tst(XX,X,YY,Y), !. inConf([_|PS], P) :- inConf(PS,P). tst(XX,X,YY,Y) :- XX<X, !, tst(X,XX,YY,Y). tst(XX,X,YY,Y) :- YY<Y, !, tst(XX,X,Y,YY). tst(XX,X,YY,Y) :- XR is XX-X, YR is YY-Y, XR == YR. /* -------------------------------------------------------------------- */ filterNot(_, [], []). filterNot(Pred, [X|XS], YS) :- call(Pred, X), !, filterNot(Pred, XS, YS). filterNot(Pred, [X|XS], [X|YS]) :- filterNot(Pred, XS, YS). /* -------------------------------------------------------------------- */ tryNext(PS,Size,Possible) :- length(PS,LPS), NewCol is LPS+1, genAll(Size,NewCol,NewPs), filterNot(inConfPS),NewPs,Possible). /* -------------------------------------------------------------------- */ findQ(Size,PS,PS) :- length(PS,Size), !. findQ(Size,PS,[]) :- tryNext(PS,Size,[]), !, fail. findQ(Size,PS,Res) :- tryNext(PS,Size,NewPS), tryAll(Size,PS,NewPS,Res). tryAll(_,_,[],_) :- !, fail. tryAll(Size,PS,[N|_],Res) :- findQ(Size,[N|PS],Res). tryAll(Size,PS,[_|NS],Res) :- tryAll(Size,PS,NS,Res). /* -------------------------------------------------------------------- */ queens(Size,Ress) :- setof(P,findQ(Size,[],P),Ress). /* -------------------------------------------------------------------- */ ``` ### 2. opravný termín **Obchodní cestující:** 1. Definovat predikát `search (From, To, L)`, který vyhledá nejkratší cestu z `From` do `To` a vrátí její délku v `L`. Přičemž využívá `get_distance(From,To,L)`, což je predikát pro navrácení délky cesty mezi dvěma místy. 2. Definovat predikát `fsf (+From, Where, Distances)`, který vhodně navrací nejkratší vzdálenosti od `From` do všech míst z množiny `Where` v argumentu `Distances`. 3. Definovat predikát `gc (+Distances, Closest, L)`, který vybere a vrátí nejbližší bod od `From` z předchozího bodu v argumentu `Closest` a vzdálenost k němu v `L`. 4. Definovat predikát `ts (+From, +Where, -Path, -L)`, jež dostává výchozí bod a množnu dalších, které je nutné navštívit. Musí pak vybrat nejkratší cestu a vrátit ji i její délku. ## 2017/2018 ### Řádný termín 1) /6b/ Flatten seznamu - vytvorit predikat e, ktery bere 2 argumenty. Prvni je seznam libovolne zanorenych seznamu (i prazdnych), napr. [[], [1, 2], [[[[]]],[atom, atom]]]. Druhy argument je vysledny seznam bez zanoreni. 2) /7b/ Funkce XOR, ktera vraci symterickou diferenci dvou mnozin (sjednoceni mnozin bez jejich pruseciku). Bere prvni a druhy parametr mnozinu reprezentovanou seznamem, treti parametr je vysledna mnozina reprezentovana seznamem. 3) /9b/ Napisat predikat search(PocatecniPozice, SeznamCest), ktory najde vsechny cesty z dane pozice zpet do teto pozice, delky 20 az 22 kroku (netrapit se tim, jestli vracet prvni/posledni prvek ci ne). Kazdy prvok je mozne nastivit len jeden krat vyjma prveho (== posledneho). Definicia pozicie je neznama, napiste funkci nextStep(Pos, NewPos) nad neznamym a NEKONECNYM stavovym priestorom. Mozno pouzit retract*, assert*, bagof, setof, length. 4) /8b/ Napisat predikat lookup. Prvy arguement vhodne reprezentovana tabulka symbolov, 2-hy argument kluc, 3-ty argument hodnota. A posledny a vysledny argument je modifikovana, pripadne vstupna tabulka symbolov. Predikat pracuje v dvoch rezimoch. Ak je zadana hodnota, tak sa modifikuje pripadne modifikuje zaznam (klic -> hodnota?) v tabulke symbolov. Ak nie je zadana hodnota, tak vyhladavame v tabulku hodnotu so zadanym klucom. Ak sa nemylim, tak bolo mozne pouzit vsetko zo zakladnej kniznice Prologu. Ja som pouzil var(), nonvar() na zistenie, ci (nie) je zadana hodnota a nemyslim si, ze by to bolo v zadani spomenute. -- priklad byl mozna lehce modifikovany? ```prolog :- dynamic pos/1. /* ---------------------------------- */ e([],[]). e([[]|R],Res) :- !, e(R,Res). e([[X|XS]|YS],Res) :- !, e([X,XS|YS],Res). e([V|XS],[V|Res]) :- e(XS,Res). /* ---------------------------------- */ xor([],L,L) :- !. xor(L,[],L) :- !. xor(L,R,Res) :- sub(L,R,L1),sub(R,L,R1),app(L1,R1,Res). sub([],_,[]). sub([X|XS],YS,RS) :- elem(X,YS),!,sub(XS,YS,RS). sub([X|XS],YS,[X|RS]) :- sub(XS,YS,RS). elem(X,[X|_]) :- !. elem(X,[_|XS]) :- elem(X,XS). app([],L,L). app([X|XS],L,[X|RS]) :- app(XS,L,RS). /* ---------------------------------- */ search(P,Res) :- setof(Path,s(P,P,0,Path),Res). s(P,P,N,[P]) :- N =< 22, N >= 20, !. s(_,_,N,_) :- N > 22, !, fail. s(P,P,N,_) :- N \= 0, !, fail. s(A,P,N,[A|R]) :- assertz(pos(A)), NN is N+1, nextStep(A,AA), ( not(pos(AA)) ; AA=P ) , s(AA,P,NN,R). s(A,_,_,_) :- pos(A), retract(pos(A)), !,fail. nextStep(p(X,Y),p(XX,Y)) :- XX is X+1. nextStep(p(X,Y),p(XX,Y)) :- XX is X-1. nextStep(p(X,Y),p(X,YY)) :- YY is Y+1. nextStep(p(X,Y),p(X,YY)) :- YY is Y-1. /* ---------------------------------- */ emptyT([]). lookup(T,_,_,_) :- var(T), !, fail. lookup(T,Var,Val,NT) :- nonvar(Var),nonvar(Val), !, iT(T,Var,Val,NT). lookup(T,Var,Val,T) :- nonvar(Var), lT(T,Var,Val). iT([], R, L, [p(R,L)]). iT([p(R,_)|PS], R, L, [p(R,L)|PS]) :- !. iT([p(RR,LL)|PS], R, L, [p(RR,LL)|PPS]) :- iT(PS,R,L,PPS). lT([p(R,L)|_],R,L) :- !. lT([_|PS],R,L) :- lT(PS,R,L). /* EOF */ ``` ### 1. opravny termín 1. Holý Prolog. Napsat predikát `mkTrans(ListOfLists,ListOfLists)`, která dostane v 1. argumentu matici, kterou transponovanou unifikuje do 2. argumentu. 2. Holý Prolog. Napsat predikát `subseq`, který v 1. argumentu dostane seznam a do 2. argumentu unifikuje seznam všech jeho podseznamů, tedy jde tam o prefix a suffix matching. 3. Prolog s `bagof, setof, assert, retract` atp. Prohledávání stavového prostoru. Napsat predikát `search(Start,Cíl, Nejkratší_cesta)`, který dostane nějakou startovní a koncovou pozici a unifikuje do 3. argumentu nejkratší cestu mezi nimi. Napsat také predikát `nextStep`, který bude vracet další novou pozici. úkolem není napsat optimální řešení, ale využít elegantnosti Prologu. 4. Prolog s celočíselným dělením, dělením a is. Napsat Prologovou reprezentaci racionálních čísel a operaci násobení a sčítání nad nimi. ```prolog :- dynamic pos/1. /* ---------------------------------- */ mkTrans([],[]). mkTrans([[]|_],[]). mkTrans(LS,[HS|HHS]) :- trans(LS,HS,TS), mkTrans(TS,HHS). trans([],[],[]). trans([[H|T]|LS],[H|HS],[T|TS]) :- trans(LS,HS,TS). /* ---------------------------------- */ suff([],[[]]). suff([H|T],[[H|T]|R]) :- suff(T,R). subseq(S,[[]|SS]) :- suff(S,SUFS), proc(SUFS,SS). proc([],[]). proc([S|SS],RES) :- pref(S,[_|PS]), proc(SS,PSS), append(PS,PSS,RES). pref([H|T],[[]|R]) :- pref(T,PS), prepAll(H,PS,R). pref([],[[]]). prepAll(_,[],[]). prepAll(X,[L|LS],[[X|L]|XS]) :- prepAll(X,LS,XS). /* ---------------------------------- */ search(S,E,Res) :- retractall(pos(_)), steptry(S,E,0,Res). steptry(S,E,N,Res) :- s(S,E,N,Res), !. steptry(S,E,N,Res) :- NN is N+1, steptry(S,E,NN,Res). s(E,E,0,[E]) :- !. s(_,_,N,_) :- N < 0, !, fail. s(A,E,N,[A|R]) :- assertz(pos(A)), NN is N-1, nextStep(A,AA), not(pos(AA)) , s(AA,E,NN,R). s(A,_,_,_) :- pos(A), retract(pos(A)), !,fail. nextStep(p(X,Y),p(XX,Y)) :- XX is X+1. nextStep(p(X,Y),p(XX,Y)) :- XX is X-1. nextStep(p(X,Y),p(X,YY)) :- YY is Y+1. nextStep(p(X,Y),p(X,YY)) :- YY is Y-1. /* ---------------------------------- */ /* rac(numerartor,denominator) */ /* op('+',L,R). * op('*',L,R). * rac(N,D). */ gcd(N,N,N) :- !. gcd(N,M,M) :- N > M, NN is mod(N,M), NN==0, !. gcd(N,M,D) :- N > M, !, NN is mod(N,M), gcd(M,NN,D). gcd(N,M,N) :- M > N, MM is mod(M,N), MM is 0, !. gcd(N,M,D) :- MM is mod(M,N), gcd(N,MM,D). ev(op('+',L,R),rac(NN,DD)) :- ev(L,rac(LN,LD)), ev(R,rac(RN,RD)), N1 is LN*RD + RN*LD, D1 is LD*RD, norm(N1,D1,NN,DD). ev(op('*',L,R),rac(NN,DD)) :- ev(L,rac(LN,LD)), ev(R,rac(RN,RD)), N1 is LN*RN, D1 is LD*RD, norm(N1,D1,NN,DD). ev(rac(X,Y),rac(X,Y)). norm(N,D,NN,DD) :- gcd(N,D,G), G>1,!, NN is div(N,G), DD is div(D,G). norm(N,D,N,D). /* EOF */ ``` ## 2016/2017 ### Riadny termín 1. Prohledávání stavového prostoru. Napsat predikát getPath, který má jako první parametr startovní bod a jako druhý cílový bod a do třetího unifikuje nejkratší (s nejmenší cenou) cestu mezi těmito body. Předpokládejte, že stavový prostor je konečný. Máte k dispozici predikát nextStep(X, XX, P), který jako první parametr dostane nějaký stav stavového prostoru a v druhém vrátí další stav a ve třetím bude výsledná cena tohoto kroku. (14b) 2. Holý prolog (unifikace, základní práce se seznamy, + řez tuším). Navrhnout strukturu pro ukládání boolovských výrazů (pro and, or, not) nad proměnnými a literály true/false. Dále strukturu pro ukládání hodnot proměnných. Dále napsat predikát eval(Table, Expr, Res), která vyhodnotí daný bool výraz. V table je tabulka hodnot proměnných. Expr je vyhodnocovaný výraz. Do Res se unifikuje výsledek. (6b) 3. Holý prolog (unifikace, základní práce se seznamy). Implementujte predikát msort, který provádí merge sort řazení seznamu hodnot. Musí to být merge sort s jeho klasickou složitostí (ne kvadratickou...). (7b) 4. Cely prolog, ale nesmi se pouzit vestaveny append a jeste 2 veci co si nepamatuju. Mame predikat, kde prvni parametr je predikat (dvouparametrovy - tedy jeden vstup a výsledek) a druhy je seznam seznamu. Z druheho parametru se postupně berou seznamy a ma se provest aplikace predikatu na vsechny hodnoty v seznamu a dat dohromady vysledky do jednoducheho seznamu (tedy ne seznam seznamu). (6b) ```prolog /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ :- dynamic pom/1, price/1, ppos/1. /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ getPath(S,E,Path) :- retractall(pom(_)), retractall(price(_)), retractall(ppos(_)), assert(price(none)), mks(S,E,Path). mks(S,E,_) :- search(S,E,0,[]). mks(S,_,[S|Path]) :- ppos(Path). checkP(NP,Path) :- price(none), retract(price(none)), assert(price(NP)), assert(ppos(Path)), !,fail. checkP(NP,Path) :- price(P), NP<P, retract(price(P)), retract(ppos(_)), assert(price(NP)), assert(ppos(Path)), !,fail. search(E,E,Price,Path) :- !, checkP(Price,Path). search(S,E,P,TP) :- assertz(pom(S)), nextStep(S,Nxt,SP), not(pom(Nxt)), NP is P+SP, append(TP,[Nxt],NTP), search(Nxt,E,NP,NTP). search(S,_,_,_) :- pom(S), retract(pom(S)), !, fail. /*or(L,R) and(L,R) not(E) true false var(V)*/ /*[w(Var,Value)]*/ eval(_,true,true). eval(_,false,false). eval(T,var(V),R) :- getVal(T,V,R),!. eval(T,not(E),R) :- eval(T,E,EE), (EE=true,R=false ; R=true ),!. eval(T,and(E1,E2),R) :- eval(T,E1,EE1), (EE1=false,R=false ; eval(T,E2,R) ),!. eval(T,or(E1,E2),R) :- eval(T,E1,EE1), (EE1=true,R=true ; eval(T,E2,R) ),!. getVal([w(V,Value)|_],V,Value) :- !. getVal([_|WS],V,Value) :- getVal(WS,V,Value). /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ merge([],L,L). merge(L,[],L). merge([H1|T1],[H2|T2],[H1|TT]) :- H1 =< H2, merge(T1,[H2|T2],TT). merge([H1|T1],[H2|T2],[H2|TT]) :- H2 < H1, merge([H1|T1],T2,TT). msort([],[]). msort([V],[V]). msort([A,B|T],R) :- divide(T,L1,L2), msort([A|L1],S1), msort([B|L2],S2), merge(S1,S2,R). divide([],[],[]). divide([V],[V],[]). divide([A,B|T],[A|TA],[B|TB]) :- divide(T,TA,TB). /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ mapC(_,[],[]) :- !. mapC(F,[HL|TL],RES) :- mapC(F,TL,RT), mapA(F,HL,RT,RES). mapA(_,[],R,R) :- !. mapA(F,[H|T],R,[X|RES]) :- C =.. [F,H,X], call(C), mapA(F,T,R,RES). ``` # Lambda kalkul ## Pevny bod - prezentacia lambda calcul 30-33 slide - pomocka aby si mohol duplikovat/replikovat/rekurzivne pracovat s funkciou - napr. klasicky mas sucet 2 cisel, ale s vyuzitim pevneho bodu mozes scitat hocikolko cisel cize mozes spravit `SUM 1 2 3 4 5 6 7 8 9` miesto len `SUM 1 2` ``` k - pevny bod E - vyraz Y - operator pevneho bodu Y E = k E k = k E (Y E) = Y E ``` ![](https://media.discordapp.net/attachments/621775580471492638/973626236033179739/unknown.png) ## Cheatsheet ``` LET True = λxy . xy LET False = λxy . yx LET NOT = λa . a (λs.False) (λt.True) -- negace LET AND = λab . a (λs.b) (λt.False) -- konjunkce LET OR = λab . a (λs.True) (λt.b) -- disjunkce LET EQ = λab . a (λs.b) (λt.NOT b) -- ekvivalence LET XOR = λab . a (λs.NOT b) (λt.b) -- negace ekvivalence LET IMP = λab . a (λs.b) (λt.True) -- implikace LET NAND = λab . a (λs.NOT b) (λt. True) -- negace konjunkce LET NOR = λab . a (λs. False) (λt.NOT b) -- negace disjunkce LET NIMP = λab . a (λs. NOT b) (λt. False) -- negace implikace LET OIMP = λab . a (λs. True) (λt. NOT b) -- obrácená implikace LET NOIMP = λab . a (λs. False) (λt. b) -- negace obrácené implikace LET A = λab . a -- identita prvniho argumentu LET B = λab . b -- identita druheho argumentu LET NA = λab . NOT a -- negace identity prvniho argumentu LET NB = λab . NOT b -- negace identity druheho argumentu LET TAUT = λab . True -- tautologie LET CONT = λab . False -- kontradikce LET TERNARY = λabc . a (λs.b) (λt.c) -- ternarni operator LET EQU = \x y . x (\s . y) (\r . (NOT y)) -- Peanova aritmetika LET GTE = λab . iszero(sub b a) LET LTE = λab . iszero(sub a b) LET EQ = λab . iszero(sub a b) ? iszero(sub b a) : false LET GT = λab . TERNARY (EQ a b) (False) (GTE a b) LET LT = λab . TERNARY (EQ a b) (False) (LTE a b) ``` ![](https://media.discordapp.net/attachments/621775580471492638/841748540211462204/unknown.png) ## 2021/2022 ### termin - operátor pevného bodu pro LT k dispozici je: iszero, prev, reprezentace celých čísel (prev 0 = 0), zbytek si musíme navrhnout sami (ternární operátor třeba k dispozici nebyl) ``` LET LT = Y ( \ f x y . iszero x ? (iszero y ? False : True) : (iszero y ? False : f (prev x) (prev y)) ) ``` ### 1. opravný - POW x n - x ^ n, zadané True, dolplniť false, ternarny operator a ``` E k = k vlastnost pevného bodu Y E = k funkce operátoru pevného bodu E (Y E) = Y E vlastnost operátoru LET True = \ x y . x y LET False = \ x y . y LET (?:) = \ c t f . c (\ x . t) f LET POW = Y (\ f x n . iszero n ? 1 : mul x (f x (prev n)) ) ``` <!-- ### 2. opravný --> ## 2020/2021 ### riadny definovať xor, true false podla seba ``` LET True = \ x y . x LET False = \ x y . y LET XOR = \ a b . a (b False True) b ``` ``` -- konkretny priklad (nebol treba) XOR True False = (\ a b . a (b False True) b) True False = (\ b . True (b False True) b) False = True (False False True) False = (\ x y . x )(False False True) False = (\y . (False False True)) False = False False True = (\ x y . y ) False True = (\ y . y ) True = True ``` ### 2. opravný termín /2b/ Napísať výraz, kde 2 premenné sú volné a zároveň aj viazané - vrámci jedného výrazu (NIE 2 rôznych výrazov). ``` -- napr.: (\x . (\b a . a b) a b) -- Pozn.: vrámci (\b a . a b) sú a b viazané ``` /4b/ Napísať príklad pre platnú a neplatnú substitúciu - vrámci jedného výrazu (NIE 2 rôznych výrazov). ``` -- napr.: (\x y z. x y) a z -- Pozn.: subtitucia a je platná ale substitúcia z nie ``` ## 2019/2020 ### předtermín Bylo definovaný `True` jako `\xy.xy`. Muselo se definovat `False` libovolným způsobem. Dál bylo potřeba definovat ternární operátor `? :`. A v poslední řadě definovat funkci `eq`, která porovnává dvě celá čísla. U toho bylo možné využít funkce `iszero` a `prev`. Tohle bylo potřeba implementovat pomocí operátoru pevného bodu. ``` máme: celá čísla, iszero, prev LET True = \ x y . x y LET False = \ x y . y LET (?:) = \ c t f . c (\ z. t) f LET eq = Y (\ f x y . iszero x ? (iszero y ? True : False) : (iszero y ? False : f (prev x) (prev y)) ) ``` ## 2018/2019 LT bude asi funkcia less < ``` E k = k Y E = k Y E = E (Y E) LET True = \ x y. x LET False = \ x y. y LET LT = Y(\f x y. iszero y False (iszero x True (f (prev x) (prev y)))) ``` ## 2017/2018 /6b/ Op. pevneho bodu - MINUS x y. Nadefinovat True + False. K dispozici isZero, prev. Pripadne si dodefinovat dalsi funkce. ``` LET True = \ x y . x LET False = \ x y . y LET (?:) = \ c t f . c t f Y E = k E k = k Y E = E k = E (Y E) LET minus = Y (\ f x y . iszero x ? 0 : (iszero y ? x : f (prev x) (prev y) )) ``` ## 2016/2017 Ukázat vlastnost operátoru pevného bodu. V lamda kalkulu definovat násobení (MUL) s dvěma parametry pomocí prev, add, iszero a ternárního operátoru (6b) ``` k - pevny bod E - vyraz Y - operator pevneho bodu Y E = k E k = k E (Y E) = Y E LET mul = \ a b . (iszero a ? 0 : (iszero b ? 0 : mf a b 0)) LET mf = Y (\ f a b r . iszero a ? r : f (pred a) b (add r b)) ``` ## 2015/2016 Popsat operátor pevného bodu, jeho vlastnost. Pomocí něj a předdefinovaných funkcí isZero, add a ternárního operátoru vytvořit výraz SUM (funkce fungují tak jak čekáme, ale mají nám neznámou implementaci, taky čísla mají neznámou implementaci). Výraz SUM vezme dvě čísla, x a y, a pokud je y=0, vrátí x, jinak vrátí SUM (x+y) (tedy částečně aplikovaného sama sebe). Ukázat vyhodnocení SUM 2 3 0. ``` Pro vyraz E je pevny bod k: E k = k Operator pevneho bodu Y: Y E = k = E (Y E) LET G = \ f x y. iszero y ? x : f (add x y) LET SUM = Y G SUM 2 3 0 = = (Y G) 2 3 0 = (G (Y G)) 2 3 0 = (G SUM) 2 3 0 = ((\ f x y. iszero y ? x : f (add x y )) SUM) 2 3 0 = (iszero 3 ? 2 : SUM (add 2 3)) 0 = (SUM 5) 0 = SUM 5 0 = (Y G) 5 0 = (G (Y G)) 5 0 = (G SUM) 5 0 = ((\ f x y. iszero y ? x : f (add x y )) SUM) 5 0 = iszero 0 ? 5 : SUM (add 5 0) = 5 ``` ### 1. opravný Mějme definováno True jako: ``True = \ x y . x y``. Definujte False standardního významu dle libosti. S jejích pomoci definujte IMP (implikace zleva doprava) standardního významu. Redukujte po krocích```IMP True False = False``` ``` LET True = \ x y . x y LET False = \ x y . y LET IMP = \ x y . x (\ z . y) True IMP True False = = (\ x y . x (\ z . y) True) True False = (\ y . True (\ z . y) True) False = True (\ z . False) True = (\ x y . x y) (\ z . False) True = (\ y . (\ z . False) y) True = (\ z . False) True = False ``` ## 2014/2015 ### Řádny Definicia vlastnosti operatoru pevneho bodu. Potom pomocou tohoto operatoru, iszero a prev nadefinovat minus, ktore berie 2 cisla a odcita ich a - b, pricom vysledok je nezaporny (tj. ==0 ak je b vacsie ako a). Mali sme zohladnit, ze iszero, prev a cisla maju neznamu definiciu, ale znamy vyznam. True a False je mozne si nadefinovat podla seba. ``` let T = \ x y . x let F = \ x y . y nechť Y je operátor pevného bodu, E je lambda-výraz a k je pevný bod pro E, potom: Y E = k = E k Y E = E (Y E) ~ k = E k LET minus = Y (\ f x y. iszero y ? x : (f (prev x) (prev y))) ``` ### 1. opravný ``` LET True = \ x y. x y LET False = \ x y. y LET NOR = \ a b. a (\i.False) (b (\k.False) True) NOR True False = (\ a b. a (\i.False) (b (\k.False) True)) True False ->Beta (\ b. True (\i.False) (b (\k.False) True)) False ->Beta True (\i.False) (False (\k.False) True) = (\ x y. x y) (\i.False) (False (\k.False) True) ->Beta (\ y. (\i.False) y) (False (\k.False) True) ->Beta (\i.False) (False (\k.False) True) ->Beta False ``` ## 2013/2014 pevny bod, nadefinovat GE ``` Y E = E (Y E) LET gef = \ f x y . (iszero x ? iszero y : (iszero y ? True : f (prev x) (f prev y))) let GE = Y gef ``` # Důkazy ## Návod Ne všechno je definované v zadání. Například foldr, foldl a podobné definice je potřeba znát z paměti. - vždy číslujte ake pravidla (rovnice) použivate - všetko krokujte postupne nespajajte kroky - zatvorkovanie spravte ako samostatny krok - https://haskellhero.grifart.cz/index.php?page=lessons&lesson=56 Pamatať si: - foldr(hlavne) ```haskell foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z foldr f z (x:xs) = f x (foldr f z xs) ``` ```haskell foldl :: (a -> b -> a) -> a -> [b] -> a foldl f z [] = z foldl f z (x:xs) = foldl f (f z x) xs ``` ```haskell map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs ``` ```haskell len [] = 0 len (x:xs) = 1 + len xs ``` ```haskell rev [] = [] rev (x:xs) = rev xs ++ [x] ``` ```haskell concat :: [[a]] -> [a] concat [] = [] concat (x:xs) = x ++ concat xs ``` ```haskell -- Pozn.: nefunguje ako filter, ale and pre Bool zoznam -- [True, True] vrati True -- [True False] vrati False all [] = True all (x:xs) = x && all xs ``` ```haskell any [] = False any (x:xs) = x || any xs ``` ```haskell sum [] = 0 sum (x:xs) = x + sum xs ``` ```haskell [] ++ ys = ys (x:xs) ++ ys = x:(xs ++ ys) (xs ++ ys) ++ zs = xs ++ (ys ++ zs) ``` - úprava prefix na infix ```haskell (++) a (foldr (++) [] as) = a ++ (foldr (++) [] as) ``` - aplikacia beta-redukcie ```haskell -- _ sa nahradi a (\_ n -> 1+n) a (foldr (\_ n -> 1+n) 0 as) = |beta-reduction -- n sa nahradi (foldr (\_ n -> 1+n) 0 as) (\n-> 1+n) (foldr (\_ n -> 1+n) 0 as) = |beta-reduction 1 + (foldr (\_ n -> 1+n) 0 as) ``` ```haskell take n _ | n <= 0 = [] take _ [] = [] take n (x:xs) = x : take (n-1) xs ``` ```haskell drop n xs | n <= 0 = xs drop _ [] = [] drop n (x:xs) = drop (n-1) xs ``` ------ je treba dokazať L=P pre - ak je v zadaní len xs: · xs = [] · xs = (a:as) - ak je v zadaní xs aj ys: · xs = [] (ľubovolné ys - pozn. zostáva také isté) · ys = [] (ľubovolné xs - pozn. zostáva také isté) · xs = (a:as), ys = (b:bs) I.P./I.H. je same shit indukčný predpoklad/hypotéza (je to v podstate odpisane len dokazovane zadanie) ## 2022/2023 ### Předtermín V haskellu je dano - `suma a [] = a` - `suma a (x:xs) = suma (a + x) xs` Dokazte, ze `suma 0 xs = foldl (+) 0 xs`. Byla tam poznamka, ze mame vhodne zvolit indukcni hypotezu (8b) Pozor na fold**l** a ne fold**r** ## 2021/2022 ### termin - concat = foldr (:) pro konečné xs a ys `concat xs ++ ys = foldr (:) ys xs` ```haskell concat [] ++ ys = ys concat (x:xs) ++ ys = x : (concat xs ++ ys) ``` ![](https://media.discordapp.net/attachments/621775580471492638/973301244372353064/IMG_20220509_211115.jpg?width=810&height=608) ### 1. opravný (14b alebo 16b) Axiomy: ```haskell uncurry f (x,y) = f x y (1) zip [] _ = [] (3) zip _ [] = [] (4) zip (x:xs) (y:ys) = (x,y) : zip xs ys (5) map f [] = [] (6) map f (x:xs) f x : map f xs (7) zipWith f [] _ = [] (8) zipWith f _ [] = [] (9) zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys (10) ``` Dokazujeme pomocí strukturální indukce s indukční proměnnou xs: `map (uncurry f) (zip xs ys) = zipWith f xs ys` ```haskell 1) xs = [], ys je libovolné L = map (uncurry f) (zip [] ys) =|3 = map (uncurry f) ([]) =|závorky = map (uncurry f) [] =|6 = [] P = zipWith f [] ys =|8 = [] L=P 2) xs je libovolné, ys = [] L = map (uncurry f) (zip xs []) =|4 = map (uncurry f) ([]) =|závorky = map (uncurry f) [] =|6 = [] P = zipWith f xs [] =|9 = [] L=P 3) I.H.: map (uncurry f) (zip xs ys) = zipWith f xs ys xs = (a:as), ys=(b:bs) L = map (uncurry f) (zip (a:as) (b:bs)) =|5 = map (uncurry f) ((a,b) : zip as bs) =|7 = (uncurry f) (a,b) : map (uncurry f) (zip as bs) =|závorky = uncurry f (a,b) : map (uncurry f) (zip as bs) =|1 = f a b : map (uncurry f) (zip as bs) P = zipWith f (a:as) (b:bs) =|10 = f a b : zipWith f as bs =|I.H. = f a b : map (uncurry f) (zip as bs) L = P Q.E.D ``` <!-- ### 2. opravný --> ## 2020/2021 ### riadny Zadanie: `all xs = foldr (&&) True xs` ```haskell all' [] = True -- 1 all' (x:xs) = x && all' xs -- 2 ``` Dôkaz: ```haskell foldr' f a [] = a -- 3 foldr' f a (x:xs) = f x (foldr' f a xs) -- 4 foldr (&&) True xs = all xs -- dokazat 1) xs = [] L = foldr (&&) True [] =|3 = True P = all [] =|1 = True L = P 2) I.H.: foldr (&&) True as = all as xs = (a:as) L = foldr (&&) True (a:as) =|4 = (&&) a (foldr (&&) True as) =|IH = (&&) a (all as) =|prefix->infix = a && (all as) =|priorita = a && all as P = all (a:as) =|2 = a && all as L = P Q.E.D. ``` ### 2. opravný termín Zadané: ```haskell df [] ys = ys -- 1 df xs [] = xs -- 2 df (x:xs) (y:ys) = x:y:df xs ys -- 3 zp _ [] ys = ys -- 4 zp _ xs [] = xs -- 5 zp f (x:xs) (y:ys) = f x y (zp f xs ys) -- 6 ``` Dodefinujte funkciu `f` tak, aby platilo `zp f xs ys = df xs ys` a dokážte to pre všetky konečné `xs` a `ys`. ```haskell f a b l = a:b:l -- 7 zp f xs ys = df xs ys -- dokazat 1) xs = [] L = zp f [] ys =|4 = ys P = df [] ys =|1 = ys L = P 2) ys = [] L = zp f xs p[] =|5 = xs P = df xs p[] =|2 = xs L = P 3) xs=(a:as) ys=(b:bs) I.P. zp f as bs = df as bs L = zp f (a:as) (b:bs) =|6 = f a b (zp f as bs) =|7 = a:b:(zp f as bs) =|zbytecne zavorky = a:b:zp f as bs =|I.P. = a:b:df as bs P = df (a:as) (b:bs) =|3 = a:b:df as bs L=P Q.E.D. ``` ## 2019/2020 ### předtermín Dokázat, že `concat xs = foldr (++) [] xs`, když: ```haskell concat [] = [] -- 1 concat (x:xs) = x ++ concat xs -- 2 ``` Definiční rovnice pro foldr bylo potřeba si nadefinovat. ```haskell foldr f z [] = z -- 3 foldr f z (x:xs) = f x (foldr f z xs) -- 4 ------- 1: xs = [] L = concat [] =|1 = [] P = foldr (++) [] [] =|3 = [] L=P ------- 2: I.P.: concat xs = foldr (++) [] xs xs = (a:as) L = concat (a:as) =|2 = a ++ concat as =|I.P. = a ++ foldr (++) [] as P = foldr (++) [] (a:as) =|4 = (++) a (foldr (++) [] as) =|prefix -> infix = a ++ (foldr (++) [] as) =|zavorky zbytecne, priorita = a ++ foldr (++) [] as L=P Q.E.D. ``` ### riadny Pre `len xs = foldr (\_ n-> 1+n) 0 xs` ```haskell len [] = 0 -- 1 len (x:s) = 1 + len xs -- 2 ``` Postup: ```haskell foldr f z [] = z -- 3 foldr f z (x:xs) = f x (foldr f z xs) -- 4 1) xs = [] L = len [] =|1 = 0 P = foldr (\_ n-> 1+n) 0 [] =|3 = 0 L = P 2) I.H.: len as = foldr (\_ n-> 1+n) 0 as xs = (a:as) L = len (a:as) =|2 = 1 + len as =|I.H. = 1 + foldr (\_ n -> 1+n) 0 as P = foldr (\_ n -> 1+n) 0 (a:as) =|4 = (\_ n -> 1+n) a (foldr (\_ n -> 1+n) 0 as) =|beta-reduction = (\n-> 1+n) (foldr (\_ n -> 1+n) 0 as) =|beta-reduction = 1 + (foldr (\_ n -> 1+n) 0 as) =|remove-brackets-unnecessary = 1 + foldr (\_ n -> 1+n) 0 as L = P ``` ### 1. opravny Pre `map (+1) xs = inc xs` ```haskell inc [] = [] -- 1 inc (x:xs) = x+1 : inc xs -- 2 ``` Postup: ```haskell map f [] = [] -- 3 map f (x:xs) = f x : map f xs -- 4 1) xs = [] L = map (+1) [] =|3 = [] P = inc [] =|1 = [] L = P 2) I.H.: map (+1) as = inc as xs = (a:as) L = map (+1) (a:as) =|4 = (+1) a : map f as =|I.H. = (+1) a : inc as =|Haskell notation = (a+1) : inc as =| priority = a+1 : inc as P = inc (a:as) =|2 = a+1 : inc as L = P Q,E.D. ``` ## 2018/2019 ### riadny Dokážte, že platí `any xs = foldr (||) False xs` když: ```haskell any [] = False -- 1 any (x:xs) = x || any xs -- 2 ``` ```haskell foldr :: (a -> b -> b) -> b -> [a] -> b foldr f z [] = z -- 3 foldr f z (x:xs) = f x (foldr f z xs) -- 4 any xs = foldr (||) False xs 1) xs = [] L = any [] =|1 = False P = foldr (||) False [] =|3 = False L = P I.H.: any xs = foldr (||) False xs 2) xs = (a:as) L = any (a:as) =|2 = a || any as P = foldr (||) False (a:as) =| 4 = (||) a (foldr (||) False as) =|I.H. = (||) a (any as) =|prefix->infi = a || (any as) =|priorita = a || any as L=P Q.E.D. ``` ### 1. opravny Dokážte, že platí `[] ++ as = as ++ [] = as` když: ```haskell [] ++ ys = ys -- 1 (x:xs) ++ ys = x:(xs++ys) -- 2 ``` Postup: ```haskell [] ++ as = as ++ [] = as 1) as = [] L = [] ++ [] =|1 = [] M = [] ++ [] =|2 = [] P = [] L=P=M 2) I.P. : []++as = as++[] = [] as = (x:xs) L = [] ++ (x:xs) =|1 = (x:xs) M = (x:xs) ++ [] =|2 = x:(xs++[]) =|I.P. = x:([]++xs) =|1 = x:(xs) =|zbytecne zavorky pryc = x:xs =|zavorky kolem pridat = (x:xs) P = (x:xs) L=P=M Q.E.D. ``` ## 2017/2018 ### Radny ```haskell all' [] = True -- 1 all' (x:xs) = x && all' xs -- 2 foldr' f a [] = a -- 3 foldr' f a (x:xs) = f x (foldr' f a xs) -- 4 Ukazat: foldr (&&) True xs = all xs 1) xs = [] L = foldr (&&) True [] =|3 = True P = all [] =|1 = True L = P 2) I.H.: foldr (&&) True as = all as xs = (a:as) L = foldr (&&) True (a:as) =|4 = (&&) a (foldr (&&) True as) =|IH = (&&) a (all as) =|prefix->infix = a && (all as) =|priorita = a && all as P = all (a:as) =|2 = a && all as L = P Q.E.D. ``` ### Opravny ```haskell length' a [] = a -- 1 length' a (_:xs) = length' (a+1) xs -- 2 foldl' f a [] = a -- 3 foldl' f a (x:xs) = foldl' f (f a x) xs -- 4 length 0 xs = foldl (\ a _ -> a+1) 0 xs 1) xs = [] L = length 0 [] =|1 = 0 P = foldl (\ a -> a+1) 0 [] =|3 = 0 L = P 2) I.P. forall k in N: length k as = foldl (\ a _ -> a+1) k as xs = (a:as) L = length 0 (a:as) =|2 = length (0+1) as =|soucet = length (1) as =|prebytecne zavorky = length 1 as =|I.P. = foldl (\ a _ -> a+1) 1 as P = foldl (\ a _ -> a+1) 0 (a:as) =|4 = foldl (\ a _ -> a+1) ((\ a _ -> a+1) 0 a) as =|beta_redukce = foldl (\ a _ -> a+1) ((\ _ -> 0+1) a) as =|beta_redukce = foldl (\ a _ -> a+1) (0+1) as =|soucet = foldl (\ a _ -> a+1) (1) as =|prebytecne zavorky = foldl (\ a _ -> a+1) 1 as L = P Q.E.D. ``` ## 2016/2017 ```haskell -- apostrofy pouze pro akceptaci v ghci sum' [] = 0 -- 1 sum' (x:xs) = x + sum' xs -- 2 foldr' f a [] = a -- 3 foldr' f a (x:xs) = f x (foldr' f a xs) -- 4 sum xs = foldr (+) 0 xs (1) xs = [] L = sum [] =|1 = 0 P = foldr (+) 0 [] =|3 = 0 L=P 2) xs = (a:as) I.H. sum as = foldr (+) 0 as L = sum (a:as) =|2 = a + sum as P = foldr (+) 0 (a:as) =|4 = (+) a (foldr (+) 0 as) =|I.H. = (+) a (sum as) =|prefix->infix = a + (sum as) =|priorita aplikace nejvyssi -> eliminace zavorek = a + sum as L = P Q.E.D. ``` ## staršie 1. Zadanie: `take n as ++ drop n as = as` riešenie: ```haskell take :: Int -> [a] -> [a] take n _ | n <= 0 = [] (1.) take _ [] = [] (2.) take n (x:xs) = x : take (n-1) xs (3.) drop :: Int -> [a] -> [a] drop n xs | n <= 0 = xs (4.) drop _ [] = [] (5.) drop n (x:xs) = drop (n-1) xs (6.) (++) :: [a] -> [a] -> [a] [] ++ ys = ys (7.) (x:xs) ++ ys = x : (xs ++ ys) (8.) Pro xs = []: dokazuji: take n [] ++ drop n [] = [] L = take n [] ++ drop n [] = [] ++ drop n [] // použito 2. zleva doprava = [] ++ [] // použito 5. zleva doprava = [] // použito 7. zleva doprava L = P Předpoklad: take n as ++ drop n as = as Pro xs = (a:as): dokazuji: take n (a:as) ++ drop n (a:as) = (a:as) L = take n (a:as) ++ drop n (a:as) = a:(take (n-1) as) ++ drop n (a:as) // použito 3. zleva doprava = a:(take (n-1) as ++ drop n (a:as) ) // použito 8. zleva doprava = a:(take (n-1) as ++ drop (n-1) as) // použito 6. zleva doprava = (a:as) // použit předpoklad L = P ``` 2. Zadanie `len(xs ++ ys) = len xs + len ys` [fituska link](https://fituska.eu/download/file.php?id=11612) ![](https://i.imgur.com/6UyTCj8.jpg) 3. Zadanie `map f (xs ++ ys) = map f xs ++ map f ys` [fituska link](https://fituska.eu/download/file.php?id=11651) 4. Zadanie `rev xs = reverse xs` ```haskell rev [ ] = [ ] (1) rev (x:xs) = rev xs ++ [x] (2) reverse xs = rev’ xs [] (3) rev’ [ ] ys = ys (4) rev’ (x:xs) ys = rev’ xs (x:ys) (5) 1) xs = [] L = rev xs = rev [] // 1 = [] P = reverse xs = reverse [] // 3 = rev’ [] [] // 4 = [] L == P 2) xs = (a:as) IP : rev as = reverse as L = rev xs = rev (a:as) // 2 = rev as ++ [a] // IP = reverse as ++ [a] // 3 = rev’ as [] ++ [a] = rev’ as [a] = rev’ as (a:[]) = rev’ (a:as) [] = reverse (a:as) = reverse xs = P ``` 5. Zadanie `foldr (++) [] xs = ccat xs` ```haskell (1) foldr f zs [] = zs (2) foldr f zs (xs:xss) = f xs (foldr f zs xss) (3) ccat [] = [] (4) ccat (xs:xss) = (++) xs (ccat xss) ---- 1. xs = [] L = foldr (++) [] [] = [] P = ccat [] = [] L = P 2. xs = (a:as) IP/IH: foldr (++) [] xs = ccat xs L = foldr (++) [] (a:as) = (++) a (foldr (++) [] (as)) = a:(foldr (++) [] (as)) P = ccat (a:as) -- TODO L = P ``` 6. Zadanie `map f (xs ++ ys) = (map f xs) ++ (map f ys)` ![](https://i.imgur.com/mhIqALy.jpg) 7. Zadanie `sum (xs ++ ys) == sum xs + sum ys` ```haskell 1: sum [] = 0 2: sum (x:xs) = x + sum xs 3: (++) a b = a ++ b 4: (++) [] ys = ys 5: (++) (x:xs) ys = x:((++) xs ys) 6: 0 + x = x ``` ```haskell 1.: xs = [] L = sum ([] ++ ys) // axiom 3 zprava doleva = sum ((++) [] ys) // axiom 4 zleva doprava = sum (ys) // odstranění závorek kolem samostatného členu = sum ys P = sum [] + sum ys // axiom 1 zleva doprava = 0 + sum ys // axiom 6 zleva doprava = sum ys 2.: xs = (a:as) Indukční předpoklad: sum (as ++ ys) == sum as + sum ys Dokazujeme: sum ((a:as) ++ ys) == sum (a:as) + sum ys L = sum ((a:as) ++ ys) // axiom 3 zprava doleva = sum ((++) (a:as) ys) // axiom 5 zleva doprava = sum (a:((++) as ys)) // axiom 2 zleva doprava = a + sum((++) as ys) // axiom 3 zleva doprava = a + sum(as ++ ys) // indukční předpoklad zleva doprava = a + sum as + sum ys P = sum (a:as) + sum ys // axiom 2 zleva doprava = a + sum as + sum ys L = P Q.E.D. ``` 8. Zadanie `length (xs ++ ys) == length xs + length ys` ```haskell 1: length [] = 0 2: length (x:xs) = 1 + length xs 3: (++) a b = a ++ b 4. (++) [] ys = ys 5. (++) (x:xs) ys = x:((++) xs ys) 6. 0 + x = x ``` Dokazujeme pomocí strukturální indukce nad proměnnou xs. První krok: `xs = []` ```haskell L = length ([] ++ ys) // axiom 3 zprava doleva = length ((++) [] ys) // axiom 4 zleva doprava = length (ys) // odstranění závorek kolem samostatného členu = length ys P = length [] + length ys // axiom 1 zleva doprava = 0 + length ys // axiom 6 zleva doprava = length ys L = P ``` Druhý krok: `xs = (a:as)` Indukční předpoklad: `length (as ++ ys) == length as + length ys` Dokazujeme: `length ((a:as) ++ ys) == length (a:as) + length ys` ```haskell L = length ((a:as) ++ ys) // axiom 3 zprava doleva = length ((++) (a:as) ys) // axiom 5 zleva doprava = length (a:((++) as ys)) // axiom 2 zleva doprava = 1 + length ((++) as ys) // axiom 3 zleva doprava = 1 + length (as ++ ys) // indukční předpoklad zleva doprava = 1 + length as + length ys P = length (a:as) + length ys // axiom 2 zleva doprava = 1 + length as + length ys L = P Q.E.D. ``` 9. V jazyku Haskell, necht je operátor ``` (++) : [a]-> [a]-> [a] [] ++ ys = ys (x:xs) ++ ys = x : (xs++ys) ``` Ukažte, že `xs ++ ys = foldr (:) ys xs` pro danou definici ++ a všechna konečná xs a ys. Axiomy: ```haskell 1 - [] ++ ys = ys 2 - (x:xs) ++ ys = x : ( xs ++ ys) 3 - foldr f a [] = a 4 - foldr f a (x:xs) = f x (foldr f a xs) ``` Postup: ```haskell 1, ak xs = [] L: [] ++ ys = ys –axiom1 zlava do prava P: foldr (:) ys [] = ys –axiom 3 zlava do prava L=P 2, ak xs = (a:as) IP: as ++ ys = foldr (:) ys as L: (a:as) ++ ys = a : (as ++ ys) –axiom 2 zlava do prava P: foldr (:) ys (a:as) = – axiom 4 zlava do prava = (:) a (foldr (:) ys as) = – IP zprava do lava = (:) a (as ++ ys) = – prepis prefix na infix = a : (as ++ ys) L = P Q.E.D ```