Difference between revisions of "FP Laboratory 11/cs"
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.") |
(Created page with "* 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 budem...") |
||
Line 64: | Line 64: | ||
<div style="clear:both"></div> | <div style="clear:both"></div> | ||
− | * | + | * 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"> | <syntaxhighlight lang="Haskell"> |
Revision as of 13:27, 15 November 2023
Complex data structure
Consider following data structure representing some kind of GUI.
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]}
As an example, we can use following data structure.
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řídyShow
.
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
- Extend the definition of a button in our GUI as follows.
data Event = MouseEvent Point
| KeyEvent {keyPressed::Char} deriving (Show)
...
| Button {name :: String, position :: Position, text :: String, onClick :: Maybe (Event -> String)}
...
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.
...
[ 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.")) ]
...
- Create a function
clickOnButton
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 corespondingonClick
function and the result will be produced string. In all other cases, the result will beNothing
.
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
Additional exercises
- Consider the following definition and the example of the m-ary tree.
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 [])]
- Create a function that sums all values stored in the m-ary tree.
msum :: MTree Int -> Int
- Create a function that extracts all values from the m-ary tree into a list.
mToList :: MTree a -> [a]
- Create a function that counts all leaves in the m-ary tree.
mLeafCount :: MTree a -> Int
- Create a function that finds a maximum value stored in the m-ary tree.
mMaxTree :: Ord a => MTree a -> a
- Create a function that checks whether a given element is stored in the m-ary tree.
mContains :: Eq a => MTree a -> a -> Bool
- Create a function that returns a number of elements greater than a given value.
mGreaterThan :: Ord a => MTree a -> a -> Int