mirror of
https://github.com/iritmaximus/haskell.git
synced 2026-10-12 19:13:39 +03:00
256 lines
8.0 KiB
Haskell
256 lines
8.0 KiB
Haskell
-- Exercise set 7
|
|
|
|
module Set7 where
|
|
|
|
import Mooc.Todo
|
|
import Data.List
|
|
import Data.List.NonEmpty (NonEmpty ((:|)))
|
|
import Data.Monoid
|
|
import Data.Semigroup
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 1: you'll find below the types Time, Distance and Velocity,
|
|
-- which represent time, distance and velocity in seconds, meters and
|
|
-- meters per second.
|
|
--
|
|
-- Implement the functions below.
|
|
|
|
data Distance = Distance Double
|
|
deriving (Show,Eq)
|
|
|
|
data Time = Time Double
|
|
deriving (Show,Eq)
|
|
|
|
data Velocity = Velocity Double
|
|
deriving (Show,Eq)
|
|
|
|
-- velocity computes a velocity given a distance and a time
|
|
velocity :: Distance -> Time -> Velocity
|
|
velocity = todo
|
|
|
|
-- travel computes a distance given a velocity and a time
|
|
travel :: Velocity -> Time -> Distance
|
|
travel = todo
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 2: let's implement a simple Set datatype. A Set is a list of
|
|
-- unique elements. The set is always kept ordered.
|
|
--
|
|
-- Implement the functions below. You might need to add class
|
|
-- constraints to the functions' types.
|
|
--
|
|
-- Examples:
|
|
-- member 'a' (Set ['a','b','c']) ==> True
|
|
-- add 2 (add 3 (add 1 emptySet)) ==> Set [1,2,3]
|
|
-- add 1 (add 1 emptySet) ==> Set [1]
|
|
|
|
data Set a = Set [a]
|
|
deriving (Show,Eq)
|
|
|
|
-- emptySet is a set with no elements
|
|
emptySet :: Set a
|
|
emptySet = todo
|
|
|
|
-- member tests if an element is in a set
|
|
member :: Eq a => a -> Set a -> Bool
|
|
member = todo
|
|
|
|
-- add a member to a set
|
|
add :: a -> Set a -> Set a
|
|
add = todo
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 3: a state machine for baking a cake. The type Event represents
|
|
-- things that can happen while baking a cake. The type State is meant
|
|
-- to represent the states a cake can be in.
|
|
--
|
|
-- Your job is to
|
|
--
|
|
-- * add new states to the State type
|
|
-- * and implement the step function
|
|
--
|
|
-- so that they have the following behaviour:
|
|
--
|
|
-- * Baking starts in the Start state
|
|
-- * A successful cake (reperesented by the Finished value) is baked
|
|
-- by first adding eggs, then adding flour and sugar (flour and
|
|
-- sugar can be added in which ever order), then mixing, and
|
|
-- finally baking.
|
|
-- * If the order of Events differs from this, the result is an Error cake.
|
|
-- No Events can save an Error cake.
|
|
-- * Once a cake is Finished, it stays Finished even if additional Events happen.
|
|
--
|
|
-- The function bake just calls step repeatedly. It's used for the
|
|
-- examples below. Don't modify it.
|
|
--
|
|
-- Examples:
|
|
-- bake [AddEggs,AddFlour,AddSugar,Mix,Bake] ==> Finished
|
|
-- bake [AddEggs,AddFlour,AddSugar,Mix,Bake,AddSugar,Mix] ==> Finished
|
|
-- bake [AddFlour] ==> Error
|
|
-- bake [AddEggs,AddFlour,Mix] ==> Error
|
|
|
|
data Event = AddEggs | AddFlour | AddSugar | Mix | Bake
|
|
deriving (Eq,Show)
|
|
|
|
data State = Start | Error | Finished
|
|
deriving (Eq,Show)
|
|
|
|
step = todo
|
|
|
|
-- do not edit this
|
|
bake :: [Event] -> State
|
|
bake events = go Start events
|
|
where go state [] = state
|
|
go state (e:es) = go (step state e) es
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 4: remember how the average function from Set4 couldn't really
|
|
-- work on empty lists? Now we can reimplement average for NonEmpty
|
|
-- lists and avoid the edge case.
|
|
--
|
|
-- PS. The Data.List.NonEmpty type has been imported for you
|
|
--
|
|
-- Examples:
|
|
-- average (1.0 :| []) ==> 1.0
|
|
-- average (1.0 :| [2.0,3.0]) ==> 2.0
|
|
|
|
average :: Fractional a => NonEmpty a -> a
|
|
average = todo
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 5: reverse a NonEmpty list.
|
|
--
|
|
-- PS. The Data.List.NonEmpty type has been imported for you
|
|
|
|
reverseNonEmpty :: NonEmpty a -> NonEmpty a
|
|
reverseNonEmpty = todo
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 6: implement Semigroup instances for the Distance, Time and
|
|
-- Velocity types from exercise 1. The instances should perform
|
|
-- addition.
|
|
--
|
|
-- When you've defined the instances you can do things like this:
|
|
--
|
|
-- velocity (Distance 50 <> Distance 10) (Time 1 <> Time 2)
|
|
-- ==> Velocity 20
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 7: implement a Monoid instance for the Set type from exercise 2.
|
|
-- The (<>) operation should be the union of sets.
|
|
--
|
|
-- What's the right definition for mempty?
|
|
--
|
|
-- What are the class constraints for the instances?
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 8: below you'll find two different ways of representing
|
|
-- calculator operations. The type Operation1 is a closed abstraction,
|
|
-- while the class Operation2 is an open abstraction.
|
|
--
|
|
-- Your task is to add:
|
|
-- * a multiplication case to Operation1 and Operation2
|
|
-- (named Multiply1 and Multiply2, respectively)
|
|
-- * functions show1 and show2 that render values of
|
|
-- Operation1 and Operation2 to strings
|
|
--
|
|
-- Examples:
|
|
-- compute1 (Multiply1 2 3) ==> 6
|
|
-- compute2 (Multiply2 2 3) ==> 6
|
|
-- show1 (Add1 2 3) ==> "2+3"
|
|
-- show1 (Multiply1 4 5) ==> "4*5"
|
|
-- show2 (Subtract2 2 3) ==> "2-3"
|
|
-- show2 (Multiply2 4 5) ==> "4*5"
|
|
|
|
data Operation1 = Add1 Int Int
|
|
| Subtract1 Int Int
|
|
deriving Show
|
|
|
|
compute1 :: Operation1 -> Int
|
|
compute1 (Add1 i j) = i+j
|
|
compute1 (Subtract1 i j) = i-j
|
|
|
|
show1 :: Operation1 -> String
|
|
show1 = todo
|
|
|
|
data Add2 = Add2 Int Int
|
|
deriving Show
|
|
data Subtract2 = Subtract2 Int Int
|
|
deriving Show
|
|
|
|
class Operation2 op where
|
|
compute2 :: op -> Int
|
|
|
|
instance Operation2 Add2 where
|
|
compute2 (Add2 i j) = i+j
|
|
|
|
instance Operation2 Subtract2 where
|
|
compute2 (Subtract2 i j) = i-j
|
|
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 9: validating passwords. Below you'll find a type
|
|
-- PasswordRequirement describing possible requirements for passwords.
|
|
--
|
|
-- Implement the function passwordAllowed that checks whether a
|
|
-- password is allowed.
|
|
--
|
|
-- Examples:
|
|
-- passwordAllowed "short" (MinimumLength 8) ==> False
|
|
-- passwordAllowed "veryLongPassword" (MinimumLength 8) ==> True
|
|
-- passwordAllowed "password" (ContainsSome "0123456789") ==> False
|
|
-- passwordAllowed "p4ssword" (ContainsSome "0123456789") ==> True
|
|
-- passwordAllowed "password" (DoesNotContain "0123456789") ==> True
|
|
-- passwordAllowed "p4ssword" (DoesNotContain "0123456789") ==> False
|
|
-- passwordAllowed "p4ssword" (And (ContainsSome "1234") (MinimumLength 5)) ==> True
|
|
-- passwordAllowed "p4ss" (And (ContainsSome "1234") (MinimumLength 5)) ==> False
|
|
-- passwordAllowed "p4ss" (Or (ContainsSome "1234") (MinimumLength 5)) ==> True
|
|
|
|
data PasswordRequirement =
|
|
MinimumLength Int
|
|
| ContainsSome String -- contains at least one of given characters
|
|
| DoesNotContain String -- does not contain any of the given characters
|
|
| And PasswordRequirement PasswordRequirement -- and'ing two requirements
|
|
| Or PasswordRequirement PasswordRequirement -- or'ing
|
|
deriving Show
|
|
|
|
passwordAllowed :: String -> PasswordRequirement -> Bool
|
|
passwordAllowed = todo
|
|
|
|
------------------------------------------------------------------------------
|
|
-- Ex 10: a DSL for simple arithmetic expressions with addition and
|
|
-- multiplication. Define the type Arithmetic so that it can express
|
|
-- expressions like this. Define the functions literal and operation
|
|
-- for creating Arithmetic values.
|
|
--
|
|
-- Define two interpreters for Arithmetic: evaluate should compute the
|
|
-- expression, and render should show the expression as a string.
|
|
--
|
|
-- Examples:
|
|
-- evaluate (literal 3) ==> 3
|
|
-- render (literal 3) ==> "3"
|
|
-- evaluate (operation "+" (literal 3) (literal 4)) ==> 7
|
|
-- render (operation "+" (literal 3) (literal 4)) ==> "(3+4)"
|
|
-- evaluate (operation "*" (literal 3) (operation "+" (literal 1) (literal 1)))
|
|
-- ==> 6
|
|
-- render (operation "*" (literal 3) (operation "+" (literal 1) (literal 1)))
|
|
-- ==> "(3*(1+1))"
|
|
--
|
|
|
|
data Arithmetic = Todo
|
|
deriving Show
|
|
|
|
literal :: Integer -> Arithmetic
|
|
literal = todo
|
|
|
|
operation :: String -> Arithmetic -> Arithmetic -> Arithmetic
|
|
operation = todo
|
|
|
|
evaluate :: Arithmetic -> Integer
|
|
evaluate = todo
|
|
|
|
render :: Arithmetic -> String
|
|
render = todo
|