Difference between revisions of "FP Test3 2025"

From Marek Běhálek Wiki
Jump to navigation Jump to search
(Created page with "<translate> = Home Preparation For Test 3 - 2024 = </translate> <translate> == Data definition == </translate> <translate> Consider following type representing a maze: </tra...")
 
 
(14 intermediate revisions by the same user not shown)
Line 1: Line 1:
 
<translate>
 
<translate>
= Home Preparation For Test 3 - 2024 =
+
= Home Preparation For Test 3 - 2025 =
</translate>
+
(Theme: Vending Machine)
  
<translate>
+
== Data types and examples ==
== Data definition ==
 
 
</translate>
 
</translate>
  
 
<translate>
 
<translate>
Consider following type representing a maze:
+
Let's define a data types representing the state of a vending machine:
 +
* a type <code>Stock</code> for the available ingredients (name and quantity),
 +
* a type <code>Drink</code> with the drink’s name, price, and list of required ingredients,
 +
* a type <code>Machine</code> that combines both stock and available drinks.
 
</translate>
 
</translate>
  
<syntaxhighlight lang="Haskell">type Maze = [String]</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">
 +
type Stock = [(String, Int)]
 +
data Drink = Drink { name :: String , price :: Int , ingredients :: [String] } deriving Show
 +
data Machine = Machine {stock :: Stock, drinks :: [Drink] } deriving Show
  
<translate>
+
stockExample :: Stock
If you want to print this maze on a screen you can use:
+
stockExample = [ ("water", 10) , ("coffee", 4) , ("milk", 2) , ("tea", 3) , ("sugar", 5) , ("cocoa", 1) ]
</translate>
 
  
<syntaxhighlight lang="Haskell">
+
drinksList :: [Drink]
printMaze :: Maze -> IO ()
+
drinksList = [
printMaze x = putStr (concat (map (++"\n") x))
+
    Drink "Coffee" 30 ["water","coffee"],
</syntaxhighlight>
+
    Drink "StrongCoffee" 40 ["water","coffee","coffee"],
 +
    Drink "Tea" 25 ["water", "water","tea"],
 +
    Drink "Cappuccino" 45 ["water","coffee","milk"],
 +
    Drink "Latte" 50 ["water","coffee","milk","sugar"],
 +
    Drink "Chocolate" 50 ["water","cocoa","milk"],
 +
    Drink "Cocoa" 60 ["milk","cocoa","milk"]
 +
    ]
  
<translate>
+
machineExample :: Machine
<!--T:14-->
+
machineExample = Machine stockExample drinksList
Maze example:
 
</translate>
 
 
 
<syntaxhighlight lang="Haskell">
 
sample1 :: Maze
 
sample1 = ["*********",
 
          "* *  * *",
 
          "* * * * *",
 
          "* * * * *",
 
          "*  *  *",
 
          "******* *",
 
          "        *",
 
          "*********"]
 
sample2 :: Maze
 
sample2 = ["      ",
 
          "      ",
 
          "  ***  ",
 
          "  ***  ",
 
          "  ***  ",
 
          "      ",
 
          "      "]
 
sample3 :: Maze
 
sample3 = ["  * *  ",
 
          " ##### ",
 
          "  ***  ",
 
          "  * *  ",
 
          "  ***  ",
 
          "    * ",
 
          "      "]
 
sample4 :: Maze
 
sample4 = ["*********",
 
          "*s*  *e*",
 
          "* *  * *",
 
          "* *  * *",
 
          "*      *",
 
          "******* *",
 
          "        *",
 
          "*********"]
 
arrow :: Maze
 
arrow = [ "....#....",
 
          "...###...",
 
          "..#.#.#..",
 
          ".#..#..#.",
 
          "....#....",
 
          "....#....",
 
          "....#####"]
 
</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
ghci> printMaze sample1                                         
 
*********
 
* *  * *
 
* * * * *
 
* * * * *
 
*  *  *
 
******* *
 
        *
 
*********
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 92: Line 45:
  
 
<translate>
 
<translate>
*Place one maze above another.
+
* Implement a function <code>findDrink</code> that finds a drink by name from a list.
 +
If the drink does not exist, raise an error <code>"No such drink"</code>.
 
</translate>
 
</translate>
  
<syntaxhighlight lang="Haskell">above :: Maze -> Maze -> Maze</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">findDrink :: [Drink] -> String -> Drink</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
*Main> printMaze(above arrow arrow)
+
ghci> findDrink drinksList "Tea" 
....#....
+
Drink {name = "Tea", price = 25, ingredients = ["water","water","tea"]}
...###...
 
..#.#.#..
 
.#..#..#.
 
....#....
 
....#....
 
....#####
 
....#....
 
...###...
 
..#.#.#..
 
.#..#..#.
 
....#....
 
....#....
 
....#####
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
*Place two mazes side by side (consider, that they have the same height).
+
* Implement a function <code>inStock</code> that checks whether a given ingredient is currently available in stock.
 
</translate>
 
</translate>
  
<syntaxhighlight lang="Haskell">sideBySide :: Maze -> Maze -> Maze</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">inStock :: Stock -> String -> Bool</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
*Main> printMaze(sideBySide arrow arrow)
+
ghci> inStock stockExample "milk"
....#........#....
+
True
...###......###...
+
ghci> inStock stockExample "juice"
..#.#.#....#.#.#..
+
False
.#..#..#..#..#..#.
 
....#........#....
 
....#........#....
 
....#####....#####
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
*Rotate maze to the left and to the right.  
+
* Implement a function <code>useIngredient</code> that decreases the quantity of a given ingredient by one unit.
 +
Ingredients with zero quantity should be removed from the stock.
 
</translate>
 
</translate>
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
rotateR :: Maze -> Maze
+
useIngredient :: Stock -> String -> Stock
rotateL :: Maze -> Maze
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
*Main> printMaze (rotateR arrow)      
+
ghci> useIngredient  stockExample "milk"
.......
+
[("water",10),("coffee",4),("milk",1),("tea",3),("sugar",5),("cocoa",1)]
...#...
+
ghci> useIngredient  stockExample "cocoa"
....#..
+
[("water",10),("coffee",4),("milk",2),("tea",3),("sugar",5)]
.....#.
 
#######
 
#....#.
 
#...#..
 
#..#...
 
#......
 
*Main> printMaze(rotateL arrow)
 
......#
 
...#..#
 
..#...#
 
.#....#
 
#######
 
.#.....
 
..#....
 
...#...
 
.......
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
* Define a function <code>getFromMaze</code> that takes a character from a maze. The coordinates are given as: <code>(row index, column index)</code> where the indexes start from <code>(0,0)</code>. The coordinate <code>(0,0)</code> is a top left corner.  
+
* Implement a function <code>canServeDrink</code> that checks whether all ingredients required for a drink are available.
 
</translate>
 
</translate>
  
<syntaxhighlight lang="Haskell">getFromMaze :: Maze -> (Int, Int) -> Char</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">canServeDrink :: Stock -> Drink -> Bool</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
ghci> getFromMaze  sample1  (1,1)
+
ghci> canServeDrink stockExample (findDrink drinksList "Cocoa")
' '
+
True
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
* Using the same coordinates as for the function above, define a function <code>putIntoMaze</code> that takes a maze and a list of triplets <code>(row index, column index, character)</code> and puts on given coordinate given character.
+
* Implement a function <code>serveDrink</code> that updates the stock after preparing a drink.
 +
If some ingredient is missing, raise an error <code>"Can not serve this drink"</code>.
 
</translate>
 
</translate>
  
<syntaxhighlight lang="Haskell">putIntoMaze :: Maze -> [(Int, Int, Char)] -> Maze</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">serveDrink :: Stock -> Drink -> Stock</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
ghci> printMaze(putIntoMaze sample2 [(0,0,'1'),(6,6,'2'),(0,6,'3')])
+
ghci> serveDrink stockExample (findDrink drinksList "Cocoa") 
1    3
+
[("water",10),("coffee",4),("tea",3),("sugar",5)]
 
 
  ***
 
  ***
 
  ***
 
 
 
      2
 
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
Using the same coordinates as for the functions above, define a function <code>getPart</code> that takes a maze and extracts a rectangular part of this maze. The maze is given as first parameter. The extracted part is defined by the position of the top left corner (second parameter) and its size (third parameter, given as (height, width)).
+
Implement a function <code>processOrders</code> that processes a list of orders (drink names) in sequence. It returns:
 +
** <code>Just</code> a new machine with the updated stock if all drinks can be served, or
 +
** <code>Nothing</code> if any of the orders cannot be fulfilled.
 
</translate>
 
</translate>
  
 
<syntaxhighlight lang="Haskell">
 
<syntaxhighlight lang="Haskell">
getPart :: Maze -> (Int,Int) -> (Int,Int) -> Maze
+
processOrders :: Machine -> [String] -> Maybe Machine
 
</syntaxhighlight>
 
</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
ghci> printMaze(getPart sample1 (1,1) (7,7))
+
ghci> processOrders machineExample ["Coffee","Tea","Coffee","Chocolate"]
*  *
+
Just (Machine {stock = [("water",5),("coffee",2),("milk",1),("tea",2),("sugar",5)], drinks = [ {- skipping list of drinks -} ]})
* * *
+
</syntaxhighlight>
* * *
 
  *
 
******
 
  
*******
+
<translate>
 +
* Implement a function <code>processPayment</code> that processes orders similarly but returns the total price of all successfully served drinks,
 +
or <code>Nothing</code> if any order fails.
 +
</translate>
 +
 
 +
<syntaxhighlight lang="Haskell">processPayment :: Machine -> [String] -> Maybe Int</syntaxhighlight>
 +
<syntaxhighlight lang="Haskell" class="myDark">
 +
ghci> processPayment  machineExample ["Coffee","Tea","Coffee","Chocolate"]
 +
Just 135
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
<translate>
 
<translate>
* Implement the function <code>solveMaze</code>. It has 1 argument. It is a list of strings representing a maze row by row from top to bottom ('*' - wall, ' ' - empty square, 's' - starting position, 'e' - ending possition). At the beginning we are at position 's' and we want to get the length of the shortest path to the position denotated by 'e'. Such path compose from steps. in each step we can move one square left, right, up or down. The function returns a number of these steps in the shortest path from 's' to 'e'.</translate>
+
* Implement a function <code>bestValue</code> that finds the best possible combination of orders (any subset of the given list) maximizing the total profit.  
 +
 
 +
Hint: You can generate all subsets of the input list, evaluate each with <code>processPayment</code>, and return one that yields the highest total payment.
 +
</translate>
  
<syntaxhighlight lang="Haskell">solveMaze :: Result -> Int</syntaxhighlight>
+
<syntaxhighlight lang="Haskell">bestValue :: Machine -> [String] -> [String]</syntaxhighlight>
 
<syntaxhighlight lang="Haskell" class="myDark">
 
<syntaxhighlight lang="Haskell" class="myDark">
solveMaze sample4
+
ghci> bestValue machineExample ["Coffee","Tea","Coffee","Chocolate"]   
12
+
["Coffee","Tea","Coffee","Chocolate"]
 +
ghci> bestValue machineExample ["Cappuccino","Cappuccino","Tea","Cocoa","Chocolate"]
 +
["Cappuccino","Tea","Chocolate"]
 +
ghci> bestValue machineExample ["Cocoa","Chocolate"]                               
 +
["Cocoa"]
 
</syntaxhighlight>
 
</syntaxhighlight>

Latest revision as of 08:05, 11 November 2025

Home Preparation For Test 3 - 2025

(Theme: Vending Machine)

Data types and examples

Let's define a data types representing the state of a vending machine:

  • a type Stock for the available ingredients (name and quantity),
  • a type Drink with the drink’s name, price, and list of required ingredients,
  • a type Machine that combines both stock and available drinks.
type Stock = [(String, Int)]
data Drink = Drink { name :: String , price :: Int , ingredients :: [String] } deriving Show
data Machine = Machine {stock :: Stock, drinks :: [Drink] } deriving Show

stockExample :: Stock
stockExample = [ ("water", 10) , ("coffee", 4) , ("milk", 2) , ("tea", 3) , ("sugar", 5) , ("cocoa", 1) ]

drinksList :: [Drink]
drinksList = [ 
    Drink "Coffee" 30 ["water","coffee"],
    Drink "StrongCoffee" 40 ["water","coffee","coffee"],
    Drink "Tea" 25 ["water", "water","tea"],
    Drink "Cappuccino" 45 ["water","coffee","milk"],
    Drink "Latte" 50 ["water","coffee","milk","sugar"],
    Drink "Chocolate" 50 ["water","cocoa","milk"],
    Drink "Cocoa" 60 ["milk","cocoa","milk"]
    ]

machineExample :: Machine
machineExample = Machine stockExample drinksList

Tasks

Create functions that:

  • Implement a function findDrink that finds a drink by name from a list.

If the drink does not exist, raise an error "No such drink".

findDrink :: [Drink] -> String -> Drink
ghci> findDrink drinksList "Tea"  
Drink {name = "Tea", price = 25, ingredients = ["water","water","tea"]}
  • Implement a function inStock that checks whether a given ingredient is currently available in stock.
inStock :: Stock -> String -> Bool
ghci> inStock stockExample "milk"
True
ghci> inStock stockExample "juice"
False
  • Implement a function useIngredient that decreases the quantity of a given ingredient by one unit.

Ingredients with zero quantity should be removed from the stock.

useIngredient :: Stock -> String -> Stock
ghci> useIngredient  stockExample "milk"
[("water",10),("coffee",4),("milk",1),("tea",3),("sugar",5),("cocoa",1)]
ghci> useIngredient  stockExample "cocoa"
[("water",10),("coffee",4),("milk",2),("tea",3),("sugar",5)]
  • Implement a function canServeDrink that checks whether all ingredients required for a drink are available.
canServeDrink :: Stock -> Drink -> Bool
ghci> canServeDrink stockExample (findDrink drinksList "Cocoa")
True
  • Implement a function serveDrink that updates the stock after preparing a drink.

If some ingredient is missing, raise an error "Can not serve this drink".

serveDrink :: Stock -> Drink -> Stock
ghci> serveDrink stockExample (findDrink drinksList "Cocoa")   
[("water",10),("coffee",4),("tea",3),("sugar",5)]
  • Implement a function processOrders that processes a list of orders (drink names) in sequence. It returns:
    • Just a new machine with the updated stock if all drinks can be served, or
    • Nothing if any of the orders cannot be fulfilled.
processOrders :: Machine -> [String] -> Maybe Machine
ghci> processOrders machineExample ["Coffee","Tea","Coffee","Chocolate"]
Just (Machine {stock = [("water",5),("coffee",2),("milk",1),("tea",2),("sugar",5)], drinks = [ {- skipping list of drinks -} ]})
  • Implement a function processPayment that processes orders similarly but returns the total price of all successfully served drinks,

or Nothing if any order fails.

processPayment :: Machine -> [String] -> Maybe Int
ghci> processPayment  machineExample ["Coffee","Tea","Coffee","Chocolate"]
Just 135
  • Implement a function bestValue that finds the best possible combination of orders (any subset of the given list) maximizing the total profit.

Hint: You can generate all subsets of the input list, evaluate each with processPayment, and return one that yields the highest total payment.

bestValue :: Machine -> [String] -> [String]
ghci> bestValue machineExample ["Coffee","Tea","Coffee","Chocolate"]    
["Coffee","Tea","Coffee","Chocolate"]
ghci> bestValue machineExample ["Cappuccino","Cappuccino","Tea","Cocoa","Chocolate"]
["Cappuccino","Tea","Chocolate"]
ghci> bestValue machineExample ["Cocoa","Chocolate"]                                
["Cocoa"]