Difference between revisions of "FP Laboratory 11/cs"

From Marek Běhálek Wiki
Jump to navigation Jump to search
(Updating to match new version of source page)
 
(20 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Binární stromy ==
+
== Složitá datová struktura ==  
* Implementujte datový typ <code>Tree</code>, který definuje binární strom, ....
+
Uvažujme následující datovou strukturu reprezentující nějaký druh grafického uživatelského rozhraní.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
data Point = Point {column::Int,row::Int} deriving (Show)
 +
 
 +
data Position = Position {leftTopCorner :: Point, width :: Int, height :: Int}
 +
 
 +
data Component
 +
  = TextBox {name :: String, position :: Position, text :: String}
 +
  | Button {name :: String, position :: Position, text :: String}
 +
  | Container {name :: String, children :: [Component]}
 +
</syntaxhighlight>
 +
 
 +
Jako příklad můžeme použít následující datovou strukturu.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
gui :: Component
 +
gui =
 +
  Container "My App"
 +
    [ Container "Menu"
 +
        [ Button "btn_new" (Position (Point 0 0) 100 20) "New",
 +
          Button "btn_open" (Position (Point 100 0) 100 20) "Open",
 +
          Button "btn_close" (Position (Point 200 0) 100 20) "Close"
 +
        ],
 +
      Container "Body" [TextBox "textbox_1" (Position (Point 0 20) 300 500) "Some text goes here"],
 +
      Container "Footer" []
 +
    ]
 +
</syntaxhighlight>
 +
 
 +
* Přidejte typ <code>Component</code> do typove třídy <code>Show</code>.
 +
 
 +
Výsledek pro naše data z předchozího příkladu by měl vypadat takto.
 +
 
 +
<syntaxhighlight lang="Haskell" class="myDark">
 +
ghci> gui
 +
Container - My App
 +
        Container - Menu
 +
                (0,0)[100,20] Button[btn_new]: New
 +
                (100,0)[100,20] Button[btn_open]: Open
 +
                (200,0)[100,20] Button[btn_close]: Close
 +
        Container - Body
 +
                (0,20)[300,500] TextBox[textbox_1]: Some text goes here
 +
        Container - Footer
 +
</syntaxhighlight>
  
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
data Tree a = Leaf a
+
instance Show Position where
            | Branch a (Tree a) (Tree a) deriving (Show)
+
    show (Position (Point col row) width height) = "(" ++ show col ++ "," ++ show row ++ ")["++ show width++","++ show height++"]"
 +
 
 +
instance Show Component where
 +
    show :: Component -> String
 +
    show gui = showIndent "" gui where
 +
        showIndent ind (TextBox name position text) = ind ++ show position ++ " TextBox[" ++ name ++ "]: " ++ text ++"\n"
 +
        showIndent ind (Button name position text) = ind ++ show position ++ " Button[" ++ name ++ "]: " ++ text ++"\n"
 +
        showIndent ind (Container name children) = let
 +
            inner = concat[showIndent (ind++"\t") c |c<-children]
 +
            in ind ++ "Container - " ++ name ++ "\n" ++ inner
 
</syntaxhighlight>
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
* Připravte si přiklad binárního stromu.
+
* Vytvořte funkci <code>insertInto</code>, která vloží prvek do existujícího kontejneru z GUI. Parametry funkce budou:
 +
** prvním parametrem bude GUI, do kterého budeme vkládat nový prvek;
 +
** druhým parametrem bude název kontejneru, do kterého vkládáme nový prvek, lze bezpečně předpokládat, že bude vždy existovat. Prvek bude v kontejneru umístěn jako poslední;
 +
** posledním parametrem je vložený prvek.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
insertInto :: Component -> String -> Component -> Component
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="Haskell" class="myDark">
 +
ghci> insertInto gui "Footer" (TextBox "Done" (Position (Point 0 500) 300 10) "We are done!")
 +
Container - My App
 +
        Container - Menu
 +
                (0,0)[100,20] Button[btn_new]: New
 +
                (100,0)[100,20] Button[btn_open]: Open
 +
                (200,0)[100,20] Button[btn_close]: Close
 +
        Container - Body
 +
                (0,20)[300,500] TextBox[textbox_1]: Some text goes here
 +
        Container - Footer
 +
                (0,500)[300,10] TextBox[Done]: We are done!
 +
</syntaxhighlight>
  
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
testTree1 :: Tree Int           
+
insertInto :: Component -> String -> Component -> Component
testTree1 = Branch 12 (Branch 23 (Leaf 34) (Leaf 45)) (Leaf 55)
+
insertInto (Container cName children ) toName element
 
+
    | cName == toName = Container cName (children++[element])  
testTree2 :: Tree Char           
+
    | otherwise = Container cName [insertInto c toName element  |c<-children]
testTree2 = Branch 'a' (Branch 'b' (Leaf 'c') (Leaf 'd')) (Leaf 'e')
+
insertInto x toName element = x     
 
</syntaxhighlight>
 
</syntaxhighlight>
 
</div>
 
</div>
Line 24: Line 96:
  
 
<div class="mw-translate-fuzzy">
 
<div class="mw-translate-fuzzy">
* Implementujte funkci, která sečte všechny hodnoty uložené ve stromu.
+
* Vytvořte funkci <code>deleteFrom</code>, která odstraní prvek ze stávajícího kontejneru z GUI. Parametry funkcí budou:
 +
** prvním parametrem bude GUI, kde odstraňujeme prvek;
 +
** druhý parametr je název komponenty, která bude odstraněna, můžete bezpečně předpokládat, že bude vždy existovat;
 
</div>
 
</div>
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
sum' :: Tree Int -> Int
+
deleteFrom :: Component -> String -> Component
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="Haskell" class="myDark">
 +
ghci> deleteFrom gui "btn_close"           
 +
Container - My App
 +
  Container - Menu
 +
    (0,0)[100,20] Button[btn_new]: New
 +
    (100,0)[100,20] Button[btn_open]: Open
 +
  Container - Body
 +
    (0,20)[300,500] TextBox[textbox_1]: Some text goes here
 +
  Container - Footer
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
sum' :: Tree Int -> Int
+
deleteFrom :: Component -> String -> Component
sum' (Leaf x) = x
+
deleteFrom (Container x child) target = Container x [deleteFrom c target |c<-child, name c /=target ]
sum' (Branch x l r) = sum' l + x + sum' r
+
deleteFrom c _ = c
 
</syntaxhighlight>
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
* Implementujte funkci, která extrahuje všechny hodnoty ze stromu do seznamu.
+
* Rozšiřte definici tlačítka v našem GUI takto.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
data Event = MouseEvent Point
 +
          | KeyEvent {keyPressed::Char} deriving (Show)
 +
...
 +
  | Button {name :: String, position :: Position, text :: String, onClick :: Maybe (Event -> String)}
 +
...
 +
</syntaxhighlight>
 +
Naše '''onClick''' je funkce, která se spustí po kliknutí na tlačítko. Parametrem této funkce jsou data popisující událost, která se osluhu spustila.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
toList :: Tree a -> [a]
+
...
 +
[ Button "btn_new" (Position (Point 0 0) 100 20) "New" (Just (\event -> "Clicked on new button.")),
 +
  Button "btn_open" (Position (Point 100 0) 100 20) "Open" Nothing,
 +
  Button "btn_close" (Position (Point 200 0) 100 20) "Close" (Just (\event -> "Clicked on close button.")) ]
 +
...
 +
</syntaxhighlight>
 +
* Vytvořte funkci <code>clickOnButton</code>, která dostane naše GUI a nějakou událost. Pokud je parametrem instance <code>MouseEvent</code> a pozice, na kterou jsme klikli, je uvnitř některého z tlačítek GUI, pak vyhodnotí odpovídající funkci <code>onClick</code> a výsledkem bude získáný řetězec. Ve všech ostatních případech bude výsledkem <code>Nothing</code>.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
clickOnButton :: Component -> Event -> Maybe String
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="Haskell" class="myDark">
 +
ghci> clickOnButton gui (MouseEvent (Point 5 5))
 +
Just "Clicked on new button."
 +
ghci> clickOnButton gui (MouseEvent (Point 205 5))
 +
Just "Clicked on close button."
 +
ghci> clickOnButton gui (MouseEvent (Point 205 50))
 +
Nothing
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
toList :: Tree a -> [a]
+
isInside :: Point -> Position -> Bool
toList (Leaf x) = [x]
+
isInside (Point pCol pRow) (Position (Point cornerCol cornerRow) width height) =
toList (Branch x l r) = toList l ++ [x] ++ toList r
+
    cornerCol <= pCol && pCol <= cornerCol + width && cornerRow <= pRow && pRow <= cornerRow + height
 +
 
 +
getFirstOrNothing :: [Maybe a] -> Maybe a
 +
getFirstOrNothing [] = Nothing
 +
getFirstOrNothing (Nothing:xs) = getFirstOrNothing xs
 +
getFirstOrNothing (Just x: xs) = Just x
 +
 
 +
clickOnButton :: Component -> Event -> Maybe String
 +
clickOnButton (Button {position = pos, onClick = (Just func)}) (MouseEvent point) | isInside point pos = Just (func (MouseEvent point))
 +
clickOnButton (Container {children=inner}) event = getFirstOrNothing [clickOnButton c event |c<-inner]
 +
clickOnButton _ _ = Nothing
 
</syntaxhighlight>
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
* Jedna z možností, jak zapsat strom v textové formě je <code>a(b(d,e),c(e,f(g,h)))</code>. Implementujte funkci, která umožní strom načíst a ukládat v takové notaci.  
+
== Doplňková cvičení ==
 +
 
 +
* Uvažujme následující definici a příklad m-árního stromu.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
data MTree a = MTree a [MTree a]
 +
</syntaxhighlight>
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
testTree1 :: MTree Int           
 +
testTree1 = MTree 1 [(MTree 2 [(MTree 3 []),(MTree 4 [(MTree 5 []),(MTree 6 [])]), (MTree 7 []),(MTree 8 [])]), (MTree 9 [])]
 +
</syntaxhighlight>
 +
 
 +
* Vytvořte funkci, která sečte všechny hodnoty uložené ve stromu m-ary.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
msum :: MTree Int -> Int
 +
</syntaxhighlight>
 +
 
 +
* Vytvořte funkci, která extrahuje všechny hodnoty z m-arního strimu do seznamu.
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
mToList :: MTree a -> [a]
 +
</syntaxhighlight>
 +
 
 +
* Vytvořte funkci, která spočítá všechny listy v m-arním stromu.
 +
<syntaxhighlight lang="Haskell">
 +
mLeafCount :: MTree a -> Int
 +
</syntaxhighlight>
 +
 
 +
* Vytvořte funkci, která najde maximální hodnotu uloženou v m-árním stromu.
  
<div style="float: right"> [[File:Video logo.png|80px|link=https://youtu.be/b0EF9WQw-uw]]</div>
 
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
toString :: Show a => Tree a -> String
+
mMaxTree :: Ord a => MTree a -> a
fromString :: Read a => String -> Tree a
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
+
* Vytvoření funkce, která zkontroluje, zda je daný prvek uložen v m-árním
 +
stromu.
 +
 
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
toString :: Show a => Tree a -> String
+
mContains :: Eq a => MTree a -> a -> Bool
toString (Leaf x) = show x
+
</syntaxhighlight>
toString (Branch x l r) = show x ++ "(" ++ (toString l) ++ "," ++ (toString r) ++ ")"
 
  
fromString :: Read a => String -> Tree a
+
* Vytvoření funkce, která vrátí počet prvků větších než zadaná hodnota.
fromString inp = fst (fromString' inp) where
+
<syntaxhighlight lang="Haskell">
  fromString' :: Read a => String -> (Tree a,String)
+
mGreaterThan :: Ord a => MTree a -> a -> Int
  fromString' inp =
 
    let
 
      before = takeWhile (\x -> x /='(' &&  x /=',' &&  x/=')') inp
 
      after = dropWhile (\x ->  x /='(' &&  x /=',' && x/=')') inp
 
      value = read before
 
    in if null after || head after /= '(' then (Leaf value, after) else
 
        let
 
          (l,after') = fromString' (tail after)
 
          (r,after'') = fromString' (tail after')
 
        in (Branch value l r, tail after'')
 
 
</syntaxhighlight>
 
</syntaxhighlight>
</div>
 
<div style="clear:both"></div>
 

Latest revision as of 11:41, 19 November 2024

Složitá datová struktura

Uvažujme následující datovou strukturu reprezentující nějaký druh grafického uživatelského rozhraní.

data Point = Point {column::Int,row::Int} deriving (Show)

data Position = Position {leftTopCorner :: Point, width :: Int, height :: Int} 

data Component
  = TextBox {name :: String, position :: Position, text :: String}
  | Button {name :: String, position :: Position, text :: String}
  | Container {name :: String, children :: [Component]}

Jako příklad můžeme použít následující datovou strukturu.

gui :: Component
gui =
  Container "My App"
    [ Container "Menu"
        [ Button "btn_new" (Position (Point 0 0) 100 20) "New",
          Button "btn_open" (Position (Point 100 0) 100 20) "Open",
          Button "btn_close" (Position (Point 200 0) 100 20) "Close"
        ],
      Container "Body" [TextBox "textbox_1" (Position (Point 0 20) 300 500) "Some text goes here"],
      Container "Footer" []
    ]
  • Přidejte typ Component do typove třídy Show.

Výsledek pro naše data z předchozího příkladu by měl vypadat takto.

ghci> gui
Container - My App
        Container - Menu
                (0,0)[100,20] Button[btn_new]: New
                (100,0)[100,20] Button[btn_open]: Open
                (200,0)[100,20] Button[btn_close]: Close
        Container - Body
                (0,20)[300,500] TextBox[textbox_1]: Some text goes here
        Container - Footer
instance Show Position where
    show (Position (Point col row) width height) = "(" ++ show col ++ "," ++ show row ++ ")["++ show width++","++ show height++"]"

instance Show Component where
    show :: Component -> String
    show gui = showIndent "" gui where
        showIndent ind (TextBox name position text) = ind ++ show position ++ " TextBox[" ++ name ++ "]: " ++ text ++"\n" 
        showIndent ind (Button name position text) = ind ++ show position ++ " Button[" ++ name ++ "]: " ++ text ++"\n"
        showIndent ind (Container name children) = let 
            inner = concat[showIndent (ind++"\t") c |c<-children]
            in ind ++ "Container - " ++ name ++ "\n" ++ inner
  • Vytvořte funkci insertInto, která vloží prvek do existujícího kontejneru z GUI. Parametry funkce budou:
    • prvním parametrem bude GUI, do kterého budeme vkládat nový prvek;
    • druhým parametrem bude název kontejneru, do kterého vkládáme nový prvek, lze bezpečně předpokládat, že bude vždy existovat. Prvek bude v kontejneru umístěn jako poslední;
    • posledním parametrem je vložený prvek.
insertInto :: Component -> String -> Component -> Component
ghci> insertInto gui "Footer" (TextBox "Done" (Position (Point 0 500) 300 10) "We are done!")
Container - My App
        Container - Menu
                (0,0)[100,20] Button[btn_new]: New
                (100,0)[100,20] Button[btn_open]: Open
                (200,0)[100,20] Button[btn_close]: Close
        Container - Body
                (0,20)[300,500] TextBox[textbox_1]: Some text goes here
        Container - Footer
                (0,500)[300,10] TextBox[Done]: We are done!
insertInto :: Component -> String -> Component -> Component
insertInto (Container cName children ) toName element 
    | cName == toName = Container cName (children++[element]) 
    | otherwise = Container cName [insertInto c toName element  |c<-children]
insertInto x toName element = x
  • Vytvořte funkci deleteFrom, která odstraní prvek ze stávajícího kontejneru z GUI. Parametry funkcí budou:
    • prvním parametrem bude GUI, kde odstraňujeme prvek;
    • druhý parametr je název komponenty, která bude odstraněna, můžete bezpečně předpokládat, že bude vždy existovat;
deleteFrom :: Component -> String -> Component
ghci> deleteFrom gui "btn_close"            
Container - My App
  Container - Menu
    (0,0)[100,20] Button[btn_new]: New
    (100,0)[100,20] Button[btn_open]: Open
  Container - Body
    (0,20)[300,500] TextBox[textbox_1]: Some text goes here
  Container - Footer
deleteFrom :: Component -> String -> Component
deleteFrom (Container x child) target = Container x [deleteFrom c target |c<-child, name c /=target ]
deleteFrom c _ = c
  • Rozšiřte definici tlačítka v našem GUI takto.
data Event = MouseEvent Point
           | KeyEvent {keyPressed::Char} deriving (Show)
...
  | Button {name :: String, position :: Position, text :: String, onClick :: Maybe (Event -> String)}
...

Naše onClick je funkce, která se spustí po kliknutí na tlačítko. Parametrem této funkce jsou data popisující událost, která se osluhu spustila.

...
[ Button "btn_new" (Position (Point 0 0) 100 20) "New" (Just (\event -> "Clicked on new button.")),
  Button "btn_open" (Position (Point 100 0) 100 20) "Open" Nothing,
  Button "btn_close" (Position (Point 200 0) 100 20) "Close" (Just (\event -> "Clicked on close button.")) ]
...
  • Vytvořte funkci clickOnButton, která dostane naše GUI a nějakou událost. Pokud je parametrem instance MouseEvent a pozice, na kterou jsme klikli, je uvnitř některého z tlačítek GUI, pak vyhodnotí odpovídající funkci onClick a výsledkem bude získáný řetězec. Ve všech ostatních případech bude výsledkem Nothing.
clickOnButton :: Component -> Event -> Maybe String
ghci> clickOnButton gui (MouseEvent (Point 5 5))
Just "Clicked on new button."
ghci> clickOnButton gui (MouseEvent (Point 205 5))
Just "Clicked on close button."
ghci> clickOnButton gui (MouseEvent (Point 205 50))
Nothing
isInside :: Point -> Position -> Bool
isInside (Point pCol pRow) (Position (Point cornerCol cornerRow) width height) =
     cornerCol <= pCol && pCol <= cornerCol + width && cornerRow <= pRow && pRow <= cornerRow + height

getFirstOrNothing :: [Maybe a] -> Maybe a
getFirstOrNothing [] = Nothing
getFirstOrNothing (Nothing:xs) = getFirstOrNothing xs
getFirstOrNothing (Just x: xs) = Just x

clickOnButton :: Component -> Event -> Maybe String
clickOnButton (Button {position = pos, onClick = (Just func)}) (MouseEvent point) | isInside point pos = Just (func (MouseEvent point))
clickOnButton (Container {children=inner}) event = getFirstOrNothing [clickOnButton c event |c<-inner]
clickOnButton _ _ = Nothing

Doplňková cvičení

  • Uvažujme následující definici a příklad m-árního stromu.
data MTree a = MTree a [MTree a]
testTree1 :: MTree Int            
testTree1 = MTree 1 [(MTree 2 [(MTree 3 []),(MTree 4 [(MTree 5 []),(MTree 6 [])]), (MTree 7 []),(MTree 8 [])]), (MTree 9 [])]
  • Vytvořte funkci, která sečte všechny hodnoty uložené ve stromu m-ary.
msum :: MTree Int -> Int
  • Vytvořte funkci, která extrahuje všechny hodnoty z m-arního strimu do seznamu.
mToList :: MTree a -> [a]
  • Vytvořte funkci, která spočítá všechny listy v m-arním stromu.
mLeafCount :: MTree a -> Int
  • Vytvořte funkci, která najde maximální hodnotu uloženou v m-árním stromu.
mMaxTree :: Ord a => MTree a -> a
  • Vytvoření funkce, která zkontroluje, zda je daný prvek uložen v m-árním

stromu.

mContains :: Eq a => MTree a -> a -> Bool
  • Vytvoření funkce, která vrátí počet prvků větších než zadaná hodnota.
mGreaterThan :: Ord a => MTree a -> a -> Int