Difference between revisions of "FP Laboratory 11/cs"

From Marek Běhálek Wiki
Jump to navigation Jump to search
(Created page with "Výsledek pro naše data z předchozího příkladu by měl vypadat takto.")
(Updating to match new version of source page)
 
(16 intermediate revisions by 2 users not shown)
Line 1: Line 1:
== Complex data structure ==
+
== Složitá datová struktura ==  
Consider following data structure representing some kind of GUI.
+
Uvažujme následující datovou strukturu reprezentující nějaký druh grafického uživatelského rozhraní.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 13: Line 13:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
As an example, we can use following data structure.
+
Jako příklad můžeme použít následující datovou strukturu.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
 
gui :: Component
 
gui :: Component
 
gui =
 
gui =
   Container
+
   Container "My App"
    "My App"
+
     [ Container "Menu"
     [ Container
 
        "Menu"
 
 
         [ Button "btn_new" (Position (Point 0 0) 100 20) "New",
 
         [ Button "btn_new" (Position (Point 0 0) 100 20) "New",
 
           Button "btn_open" (Position (Point 100 0) 100 20) "Open",
 
           Button "btn_open" (Position (Point 100 0) 100 20) "Open",
Line 64: Line 62:
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
* Cerate a function <code>insertInto</code>, it will insert an element into the existing container from a GUI. The functions parameters will be:  
+
* Vytvořte funkci <code>insertInto</code>, která vloží prvek do existujícího kontejneru z GUI. Parametry funkce budou:  
** first parameter will be the GUI, where we are inserting the new element;
+
** prvním parametrem bude GUI, do kterého budeme vkládat nový prvek;
** second parameter is the name of the container, where we insert the new element, you can safely assume, that it will always exist. The element will be placed as last in the container;
+
** 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í;
** last parameter is the inserted element.
+
** posledním parametrem je vložený prvek.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 97: Line 95:
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
* Extend the definition of a button in our GUI as follows.
+
<div class="mw-translate-fuzzy">
 +
* 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>
 +
 
 +
<syntaxhighlight lang="Haskell">
 +
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>
 +
 
 +
<div class="mw-collapsible mw-collapsed" data-collapsetext="Hide solution" data-expandtext="Show solution">
 +
<syntaxhighlight lang="Haskell">
 +
deleteFrom :: Component -> String -> Component
 +
deleteFrom (Container x child) target = Container x [deleteFrom c target |c<-child, name c /=target ]
 +
deleteFrom c _ = c
 +
</syntaxhighlight>
 +
</div>
 +
<div style="clear:both"></div>
 +
 
 +
* Rozšiřte definici tlačítka v našem GUI takto.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 106: Line 134:
 
...
 
...
 
</syntaxhighlight>
 
</syntaxhighlight>
Our '''onClick''' is a function, that will be fired when the button is clicked on. The parameter of this function is data describing the firing event.
+
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">
Line 115: Line 143:
 
...
 
...
 
</syntaxhighlight>
 
</syntaxhighlight>
* Create a function <code>clickOnButton</code> that will take our GUI and an event. If it is a mouse event, and the position where we have clicked is inside some of the buttons from the gui, then it evaluates the coresponding <code>onClick</code> function and the result will be produced string. In all other cases, the result will be <code>Nothing</code>.
+
* 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">
 
<syntaxhighlight lang="Haskell">
Line 149: Line 177:
 
<div style="clear:both"></div>
 
<div style="clear:both"></div>
  
== Additional exercises ==
+
== Doplňková cvičení ==
  
* Consider the following definition and the example of the m-ary tree.
+
* Uvažujme následující definici a příklad m-árního stromu.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 162: Line 190:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that sums all values stored in the m-ary tree.
+
* Vytvořte funkci, která sečte všechny hodnoty uložené ve stromu m-ary.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 168: Line 196:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that extracts all values from the m-ary tree into a list.
+
* Vytvořte funkci, která extrahuje všechny hodnoty z m-arního strimu do seznamu.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 174: Line 202:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that counts all leaves in the m-ary tree.
+
* Vytvořte funkci, která spočítá všechny listy v m-arním stromu.
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
 
mLeafCount :: MTree a -> Int
 
mLeafCount :: MTree a -> Int
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that finds a maximum value stored in the m-ary tree.
+
* Vytvořte funkci, která najde maximální hodnotu uloženou v m-árním stromu.
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 185: Line 213:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that checks whether a given element is stored in the m-ary tree.  
+
* Vytvoření funkce, která zkontroluje, zda je daný prvek uložen v m-árním
 +
stromu.  
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
Line 191: Line 220:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
* Create a function that returns a number of elements greater than a given value.
+
* Vytvoření funkce, která vrátí počet prvků větších než zadaná hodnota.
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
 
mGreaterThan :: Ord a => MTree a -> a -> Int
 
mGreaterThan :: Ord a => MTree a -> a -> Int
 
</syntaxhighlight>
 
</syntaxhighlight>

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