From 3bb0b1ead0c7dff38e4cfeacc5933c0a2bbf7eaf Mon Sep 17 00:00:00 2001 From: Manuel Thalmann Date: Thu, 14 Mar 2024 14:59:07 +0100 Subject: [PATCH] Add files for lab 01 --- Exercises/exercise-1/Code/A_Basics.hs | 222 ++++++++++++++++++ Exercises/exercise-1/Code/A_BasicsTodo.hs | 225 +++++++++++++++++++ Exercises/exercise-1/Code/B_Functions.hs | 202 +++++++++++++++++ Exercises/exercise-1/Code/B_FunctionsTodo.hs | 206 +++++++++++++++++ Exercises/exercise-1/Code/C_Types.hs | 148 ++++++++++++ Exercises/exercise-1/Code/C_TypesTodo.hs | 149 ++++++++++++ Exercises/exercise-1/Code/D_Control.hs | 168 ++++++++++++++ Exercises/exercise-1/Code/D_ControlTodo.hs | 166 ++++++++++++++ 8 files changed, 1486 insertions(+) create mode 100644 Exercises/exercise-1/Code/A_Basics.hs create mode 100644 Exercises/exercise-1/Code/A_BasicsTodo.hs create mode 100644 Exercises/exercise-1/Code/B_Functions.hs create mode 100644 Exercises/exercise-1/Code/B_FunctionsTodo.hs create mode 100644 Exercises/exercise-1/Code/C_Types.hs create mode 100644 Exercises/exercise-1/Code/C_TypesTodo.hs create mode 100644 Exercises/exercise-1/Code/D_Control.hs create mode 100644 Exercises/exercise-1/Code/D_ControlTodo.hs diff --git a/Exercises/exercise-1/Code/A_Basics.hs b/Exercises/exercise-1/Code/A_Basics.hs new file mode 100644 index 0000000..5a9dd15 --- /dev/null +++ b/Exercises/exercise-1/Code/A_Basics.hs @@ -0,0 +1,222 @@ +{- Contents + A short introduction to Haskell; + The very basics +-} + + +----------------------------------------------------------- +-- Comments +----------------------------------------------------------- + +-- a single line comment + +{- a multiline comment + {- can be nested -} +-} + +{- Usually (from now on), we will try to adhere to the + following convention/structure for multiline comments: +-} + +{- Optional "title" of the comment + Content of the comment possibly spanning multiple lines + with length <= 60. +-} + + +----------------------------------------------------------- +-- Simple declarations +----------------------------------------------------------- + +{- Declaring a variable + Generally, in Haskell we define the type of a variable + in a separate line before we define the variable. +-} + +-- 'five' is the integer 5 +five :: Integer +five = 5 + +{- + 'six' is the integer 6. The type 'Integer' can + accomodate integers of arbitrary size +-} +six :: Integer +six = 6 + +-- 'four' is the float 4.0 +four :: Float +four = 4 + + +----------------------------------------------------------- +---- Numeric operations +----------------------------------------------------------- + +seven :: Integer +seven = five + 2 + +theIntEight :: Integer +theIntEight = 3 + five + +theFloatSeven :: Float +theFloatSeven = 3 + four + +twentyFour :: Float +twentyFour = 3 * 2 ^ 3 + +{- Integer division + 'div' denotes integer division. Generally, it is + possible to write a function between '`' to use infix + notation. I order to define functions that are + "natively" infix, you would use brackets '(...)' when + defining the function/operator. +-} +intSevenByThree :: Integer +intSevenByThree = seven `div` 3 + +{- Division + The "normal" division '/' is for "fractional numbers, + e.g. floats. +-} +floatSevenByThree :: Float +floatSevenByThree = 7 / 3 + +{- Exercise + Fill out the types, check your solutions with the REPL. + +x :: Integer +x = 2 ^ 3 + +y :: Float +y = 2.0 ^ 3 + +a :: Integer +a = x + 5 + +b :: Float +b = y/2 + +c :: Does not work +c = y `div` 3 +-} + + +----------------------------------------------------------- +---- Booleans and boolean operations +----------------------------------------------------------- + +true :: Bool +true = True + +false :: Bool +false = False + +alsoTrue :: Bool +alsoTrue = true || false + +alsoFalse :: Bool +alsoFalse = true && false + +moreTruth :: Bool +moreTruth = not alsoFalse + + +----------------------------------------------------------- +---- Strings and chars +----------------------------------------------------------- + +aChar :: Char +aChar = 'a' + +aString :: String +aString = "Happy new year" + +greeting :: String +greeting = aString ++ " " ++ "2022" + +greeting2 :: String +greeting2 = concat + [ aString + , " " + , "2022" + ] + + + +{- Warning + The operators are strongly typed; they can only digest + values of the same type. The following declarations are + thus rejected by the type checker: + + aString = "Happy new year" + greeting = aString ++ " " ++ 2022 + + five :: Int + five = 5 + seven = five + 2.0 +-} + + +----------------------------------------------------------- +---- Lists +----------------------------------------------------------- + +{- + We will often use `List` (linked lists), a generic + container to hold multiple values of *the same type*. +-} + +aListOfStrings :: [String] +aListOfStrings = [ "one", "two", "three" ] + +aListOfInts :: [Integer] +aListOfInts = [ 1, 2, 3 ] + +{- Exercise + Fill out the type, check your solutions with the REPL. + + friendGroups :: [[String]] + friendGroups = + [ ["Peter", "Anna", "Roman", "Laura"] + , ["Anna","Reto"] + , ["Christoph", "Mara", "Andrew"] + ] +-} + +{- List comprehension + Aside from declaring lists explicitly by writing down + their element (as shown before), lists can also be + declared by "list comprehension" and also as "ranges". +-} + +someInts :: [Integer] +someInts = [1..15] -- range + +someEvens :: [Integer] +someEvens = [ 2*x | x <- [-5..5]] + +pairs :: [(Integer, Bool)] +pairs = [ (x,y) | x <- [0..5], y <- [True, False]] + +lessThanPairs :: [(Integer,Integer)] +lessThanPairs = [ (x,y) + | x <- [0..5] + , y <- [0..5] + , x < y -- guard + , x > 1 -- second guard + ] + +{- Exercise + Use list comprehension to declare a list that contains + all square numbers between 1 and 100. +-} +squares :: [Integer] +squares = [ x*x | x <- [1..10]] + +{- Exercise + Use list comprehension to declare a list that contains + all odd square numbers between 1 and 100. +-} +oddSquares :: [Integer] +oddSquares = [ x | x <- squares, x `mod` 2 /= 0 ] \ No newline at end of file diff --git a/Exercises/exercise-1/Code/A_BasicsTodo.hs b/Exercises/exercise-1/Code/A_BasicsTodo.hs new file mode 100644 index 0000000..d58f3d3 --- /dev/null +++ b/Exercises/exercise-1/Code/A_BasicsTodo.hs @@ -0,0 +1,225 @@ +{- Contents + A short introduction to Haskell; + The very basics +-} + + +----------------------------------------------------------- +-- Comments +----------------------------------------------------------- + +-- a single line comment + +{- a multiline comment + {- can be nested -} +-} + +{- Usually (from now on), we will try to adhere to the + following conventuion/structure for multiline comments: +-} + +{- Optional "title" of the comment + Content of the comment possibly spaning multiple lines + with length <= 60. +-} + + +----------------------------------------------------------- +-- Simple declarations +----------------------------------------------------------- + +{- Declaring a variable + Generally, in Haskell we define the type of a variable + in a separate line before we define the variable. +-} + +-- 'five' is the integer 5 +five :: Integer +five = 5 + +{- + 'six' is the integer 6. The type 'Integer' can + accomodate integers of arbitrary size +-} +six :: Integer +six = 6 + +-- 'four' is the float 4.0 +four :: Float +four = 4 + + +----------------------------------------------------------- +---- Numeric operations +----------------------------------------------------------- + +seven :: Integer +seven = five + 2 + +theIntEight :: Integer +theIntEight = 3 + five + +theFloatSeven :: Float +theFloatSeven = 3 + four + +twentyFour :: Float +twentyFour = 3 * 2 ^ 3 + +{- Integer division + 'div' denotes integer division. Generally, it is + possible to write a function between '`' to use infix + notation. I order to define functions that are + "natively" infix, you would use brackets '(...)' when + defining the function/operator. +-} +intSevenByThree :: Integer +intSevenByThree = seven `div` 3 + +{- Division + The "normal" division '/' is for "fractional numbers, + e.g. floats. +-} +floatSevenByThree :: Float +floatSevenByThree = 7 / 3 + +{- Exercise + Fill out the types where possible + Check your solutions with the REPL. + + +x :: Integer +x = 2 ^ 3 + +y :: Float +y = 2.0 ^ 3 + +a :: ? +a = x + 5 + +b :: ? +b = y/2 + +c :: ? +c = y `div` 3 + +-} + + +----------------------------------------------------------- +---- Booleans and boolean operations +----------------------------------------------------------- + +true :: Bool +true = True + +false :: Bool +false = False + +alsoTrue :: Bool +alsoTrue = true || false + +alsoFalse :: Bool +alsoFalse = true && false + +moreTruth :: Bool +moreTruth = not alsoFalse + + +----------------------------------------------------------- +---- Strings and chars +----------------------------------------------------------- + +aChar :: Char +aChar = 'a' + +aString :: String +aString = "Happy new year" + +greeting :: String +greeting = aString ++ " " ++ "2022" + +greeting2 :: String +greeting2 = concat + [ aString + , " " + , "2022" + ] + + + +{- Warning + The operators are strongly typed; they can only digest + values of the same type. The following declarations are + thus rejected by the type checker: + + aString = "Happy new year" + greeting = aString ++ " " ++ 2022 + + five :: Int + five = 5 + seven = five + 2.0 +-} + + +----------------------------------------------------------- +---- Lists +----------------------------------------------------------- + +{- + We will often use `List` (linked lists), a generic + container to hold multiple values of *the same type*. +-} + +aListOfStrings :: [String] +aListOfStrings = [ "one", "two", "three" ] + +aListOfInts :: [Integer] +aListOfInts = [ 1, 2, 3 ] + +{- Exercise + Fill out the type, check your solutions with the REPL. + + friendGroups :: ? + friendGroups = + [ ["Peter", "Anna", "Roman", "Laura"] + , ["Anna","Reto"] + , ["Christoph", "Mara", "Andrew"] + ] +-} + +{- List comprehension + Aside from declaring lists explicitly by writing down + their element (as shown before), lists can also be + declared by "list comprehension" and also as "ranges". +-} + +someInts :: [Integer] +someInts = [1..15] -- range + +someEvens :: [Integer] +someEvens = [ 2*x | x <- [-5..5]] + +pairs :: [(Integer, Bool)] +pairs = [ (x,y) | x <- [0..5], y <- [True, False]] + +lessThanPairs :: [(Integer,Integer)] +lessThanPairs = [ (x,y) + | x <- [0..5] + , y <- [0..5] + , x < y -- guard + , x > 1 -- second guard + ] + +{- Exercise + Use list comprehension to declare a list that contains + all square numbers between 1 and 100. +-} +squares :: [Integer] +squares = error "fixme" + +{- Exercise + Use list comprehension to declare a list that contains + all odd square numbers between 1 and 100. +-} +oddSquares :: [Integer] +oddSquares = error "fixme" diff --git a/Exercises/exercise-1/Code/B_Functions.hs b/Exercises/exercise-1/Code/B_Functions.hs new file mode 100644 index 0000000..f4362c5 --- /dev/null +++ b/Exercises/exercise-1/Code/B_Functions.hs @@ -0,0 +1,202 @@ +{- Contents + A short integerroduction to Haskell; + On how to create simple functions. +-} + +----------------------------------------------------------- +-- Elementary Functions +----------------------------------------------------------- + +{- General Syntax for functions + * Input on the left, output on the right. + * Functions also have a type: input type on the left + side, output type on the right side, arrow inbetween. +-} +inc1 :: Integer -> Integer +inc1 n = n + 1 + +{- Lambda notation + Everything can also be written on the right side (lambda + notation). In fact, `inc1` is just syntactic sugar for + `inc2`. +-} +inc2 :: Integer -> Integer +inc2 = \n -> n + 1 + +{- Function application + To apply a function, write the function to the left of + the input/argument (no brackets needed). +-} +two :: Integer +two = inc1 1 + +someGreeting :: String -> String +someGreeting person = "Hello " ++ person + + +{- Exercise + Define a function `square`, that squares the given input + and write down its type. Define the same function using + the lambda notation. +-} +square :: Integer -> Integer +square x = x * x + + +----------------------------------------------------------- +-- Functions of Several Variables +----------------------------------------------------------- + +{- Tupled Inputs + 'addTupled' is binary function, its output depends on + two separate integers. +-} +addTupled :: (Integer, Integer) -> Integer +addTupled (x, y) = x + y + +{- + 'avg3Tupled' is a function that depends on three input + variables. +-} +avg3Tupled :: (Float, Float, Float) -> Float +avg3Tupled ( x, y, z ) = (x + y + z) / 3 + +{- Evaluating with Tuples + We evaluate a function of several variables by providing + values *for each* input variabele. +-} +four :: Float +four = avg3Tupled ( 3, 4, 5 ) + +{- + If we want to evaluate a multivariable function before + we have all inputs ready, we have to properly + parametrize it. We can do this with curried functions. + More on that later! +-} + + +{- A Curried Function + 'add' is the parametrized (a.k.a curried) version of + `add`. It is a function that returns a (unary) function. + + Note that the following three lines are equivalent + declarations: + + 'add = \x -> \y -> x + y' + + 'add x = \y -> x + y' + + 'add x y = x + y' + + Also note that the parenthesis are not needed in the + declaration of the type of 'add'. +-} +add :: Int -> (Int -> Int) +add n m = n + m + + +{- Partial Application + 'add3' is the function that adds '3' to any given input. + Thanks to currying, we can realize 'add3' as a special + case of 'add'. Thanks to partial application, this + corresponds to a single function call. +-} +add3 :: Int -> Int +add3 = add 3 + +{- Exercise + Declare a curried version of the function 'avg3Tupled' + as a lambda term. +-} +avg3 :: Float -> Float -> Float -> Float +-- avg3 x y z = (x + y + z) / 3 +-- avg3 x y = \ z -> (x + y + z) / 3 +-- avg3 x = \y z -> (x + y + z) /3 +avg3 = \x -> \y -> \z -> (x + y + z) / 3 + +{- Exercise + use the binary function '(++)' that concatenates strings + to specify a function that prepends "=> " to any given + input string. Use partial application +-} +prepArrow :: String -> String +prepArrow = (++) "=> " + + +----------------------------------------------------------- +-- Higher Order Functions +----------------------------------------------------------- + +{- Function as Input + 'apply' is a function that accepts a function as input + (and applies it to the remaining input) +-} +apply :: (a -> b) -> a -> b +apply f x = f x + +{- Function as Input and Output + 'twice' is a function that accepts and returns a + function. +-} +twice :: (a -> a) -> (a -> a) +twice f x = f (f x) +-- twice = f . f + +{- Exercise + Write a function `thrice` that applies a function three + times. +-} +thrice :: (a -> a) -> a -> a +thrice f x = f (twice f x) +-- thrice f = f . f . f + +{- Exercise + Write a function 'compose' that accepts two functions + and applies them to a given argument in order. +-} +compose :: (a -> b) -> (b -> c) -> a -> c +compose f g x = g (f x) + +{- Exercise + reimplement 'twice' and 'thrice' with as an application + of the function 'compose'. +-} +twiceByComp :: (a -> a) -> a -> a +twiceByComp f = compose f f + +thriceByComp :: (a -> a) -> a -> a +thriceByComp f = compose f (twiceByComp f) + +{- List map + A often used higher function is ``mapping'' of lists. + This function will apply a function (given as an + argument) to every element in a list (also given as an + argument) and return a new list containig the changed + values. There are a lot of other functions to work + with lists (and similar types). We will learn about + these functions later. +-} +mapping :: [Integer] +mapping = map (\n -> n + 1) [1, 2, 3] + +{-| Exercise + greet all friends using 'friends', 'map' and + 'greeting'. +-} +friends :: [String] +friends = + [ "Peter" + , "Nina" + , "Janosh" + , "Reto" + , "Adal" + , "Sara" + ] + +greeting :: String -> String +greeting person = "Hello " ++ person + +greetFriends :: [String] +greetFriends = map greeting friends + diff --git a/Exercises/exercise-1/Code/B_FunctionsTodo.hs b/Exercises/exercise-1/Code/B_FunctionsTodo.hs new file mode 100644 index 0000000..bc86fd5 --- /dev/null +++ b/Exercises/exercise-1/Code/B_FunctionsTodo.hs @@ -0,0 +1,206 @@ +{- Contents + A short integerroduction to Haskell; + On how to create simple functions. +-} + +----------------------------------------------------------- +-- Elementary Functions +----------------------------------------------------------- + +{- General Syntax for functions + * Input on the left, output on the right. + * Functions also have a type: input type on the left + side, output type on the right side, arrow inbetween. +-} +inc1 :: Integer -> Integer +inc1 n = n + 1 + +{- Lambda notation + Everything can also be written on the right side (lambda + notation). In fact, `inc1` is just syntactic sugar for + `inc2`. +-} +inc2 :: Integer -> Integer +inc2 = \n -> n + 1 + +{- Function application + To apply a function, write the function to the left of + the input/argument (no brackets needed). +-} +two :: Integer +two = inc1 1 + +someGreeting :: String -> String +someGreeting person = "Hello " ++ person + + +{- Exercise + Define a function `square`, that squares the given input + and write down its type. Define the same function using + the lambda notation. +-} +square :: Integer -> Integer +square x = error "fixme" + +squareLambda :: Integer -> Integer +squareLambda = error "fixme" + + +----------------------------------------------------------- +-- Functions of Several Variables +----------------------------------------------------------- + +{- Tupled Inputs + 'addTupled' is binary function, its output depends on + two separate integers. +-} +addTupled :: (Integer, Integer) -> Integer +addTupled (x, y) = x + y + +{- + 'avg3Tupled' is a function that depends on three input + variables. +-} +avg3Tupled :: (Float, Float, Float) -> Float +avg3Tupled ( x, y, z ) = (x + y + z) / 3 + +{- Evaluating with Tuples + We evaluate a function of several variables by providing + values *for each* input variabele. +-} +four :: Float +four = avg3Tupled ( 3, 4, 5 ) + +{- + If we want to evaluate a multivariable function before + we have all inputs ready, we have to properly + parametrize it. We can do this with curried functions. + More on that later! +-} + + +{- A Curried Function + 'add' is the parametrized (a.k.a curried) version of + `add`. It is a function that returns a (unary) function. + + Note that the following three lines are equivalent + declarations: + + 'add = \x -> \y -> x + y' + + 'add x = \y -> x + y' + + 'add x y = x + y' + + Also note that the parenthesis are not needed in the + declaration of the type of 'add'. +-} +add :: Int -> (Int -> Int) +add n m = n + m + + +{- Partial Application + 'add3' is the function that adds '3' to any given input. + Thanks to currying, we can realize 'add3' as a special + case of 'add'. Thanks to partial application, this + corresponds to a single function call. +-} +add3 :: Int -> Int +add3 = add 3 + +{- Exercise + Declare a curried version of the function 'avg3Tupled' + as a lambda term. +-} +avg3 :: Float -> Float -> Float -> Float +avg3 = error "fixme" + +{- Exercise + use the binary function '(++)' that concatenates strings + to specify a function that prepends "=> " to any given + input string. Use partial application +-} +prepArrow :: String -> String +prepArrow = error "fixme" + +-- When calling this +-- > prepArrow "foo" +-- It should output the following +-- '=> foo' + + +----------------------------------------------------------- +-- Higher Order Functions +----------------------------------------------------------- + +{- Function as Input + 'apply' is a function that accepts a function as input + (and applies it to the remaining input) +-} +apply :: (a -> b) -> a -> b +apply f x = f x + +{- Function as Input and Output + 'twice' is a function that accepts and returns a + function. +-} +twice :: (a -> a) -> (a -> a) +twice f x = f (f x) +-- twice f = f . f -- Alternatively use this syntax + +{- Exercise + Write a function `thrice` that applies a function three + times. +-} +thrice :: (a -> a) -> a -> a +thrice f x = error "fixme" + +{- Exercise + Write a function 'compose' that accepts two functions + and applies them to a given argument in order. +-} +compose :: (a -> b) -> (b -> c) -> a -> c +compose f g x = error "fixme" + +{- Exercise + reimplement 'twice' and 'thrice' with as an application + of the function 'compose'. +-} +twiceByComp :: (a -> a) -> a -> a +twiceByComp f = error "fixme" + +thriceByComp :: (a -> a) -> a -> a +thriceByComp f = error "fixme" + +{- List map + A often used higher function is ``mapping'' of lists. + This function will apply a function (given as an + argument) to every element in a list (also given as an + argument) and return a new list containig the changed + values. There are a lot of other functions to work + with lists (and similar types). We will learn about + these functions later. +-} +mapping :: [Integer] +mapping = map (\n -> n + 1) [1, 2, 3] + +{-| Exercise + greet all friends using 'friends', 'map' and + 'greeting'. +-} +friends :: [String] +friends = + [ "Peter" + , "Nina" + , "Janosh" + , "Reto" + , "Adal" + , "Sara" + ] + +greeting :: String -> String +greeting person = "Hello " ++ person + +greetFriends :: [String] +greetFriends = error "fixme" + diff --git a/Exercises/exercise-1/Code/C_Types.hs b/Exercises/exercise-1/Code/C_Types.hs new file mode 100644 index 0000000..ba39539 --- /dev/null +++ b/Exercises/exercise-1/Code/C_Types.hs @@ -0,0 +1,148 @@ +{- Contents + A short introduction to Haskell; + On how to create your own types +-} + +{- Preliminary Remark + There are several different Keywords to declare new + types ('data', 'newtype', 'type'). We will discuss the + difference of these keywords and when to use + which later. +-} + +----------------------------------------------------------- +-- Product Types +----------------------------------------------------------- + +{- Records + Records are tuples with custom named fields and a + constructor. +-} +data Character = Character + { firstName :: String + , lastName :: String + } + +han :: Character +han = Character + { firstName = "Han" + , lastName = "Solo" + } + +obiWan :: Character +obiWan = Character + { firstName = "Obi-Wan" + , lastName = "Kenobi" + } + +{- "Getters" + Values can be extracted from records by using the fields + name as a function. +-} +solo :: String +solo = firstName han + +kenobi :: String +kenobi = lastName obiWan + +{- 'first' and 'last' can be used like any other function. +-} +firstNames :: [String] +firstNames = map firstName [ han, obiWan ] + +{- Exercise +Greet Obi-Wan and Han, each with a phrase like e.g. +"Hello Obi-Wan". Use 'map' and 'first'. +-} +greetings :: [String] +greetings = map (("Hello " ++) . firstName) [ han, obiWan ] + +{- "Setters" + Values of a record can be changed with the syntax below. + What is important to note here is that 'han' will not be + changed. A copy of 'han' will be made with the first + name changed. +-} +ben :: Character +ben = han {firstName = "Ben"} + + +----------------------------------------------------------- +-- Sum Types +----------------------------------------------------------- + +{- Sum Types + The values of a sum type come in a number of different + variants. Each value can be uniquely assigned to one of + the variants. + Our first sum type contains exactly two values. This + type is isomorphic to 'Bool'. +-} +data YesNo + = Yes + | No + +yes :: YesNo +yes = Yes + +{- The following types look similar to the type above, but + they contain values in the options. +-} +data Identification + = Email String + | Username String + +hanId :: Identification +hanId = Username "han_32" + +obiWanId :: Identification +obiWanId = Email "kenobi@rebel-alliance.space" + +{- Recursive Types + A cool fact about union types is that they can be + recursive. The standard example is that of binary trees. +-} +data BinaryTree a + = Node a (BinaryTree a) (BinaryTree a) + | Leaf a + +{- + A simple binary tree with integers. +-} +tree :: BinaryTree Int +tree = Node 1 (Leaf 2) (Leaf 3) + +{- Lists are also a recursive sum type (with syntactic sugar + for 'Cons' and 'E') +-} +data MyList a + = Cons a (MyList a) + | Nil + +{- + [1,2,3] as 'MyList' +-} +list :: MyList Integer +list = + Cons + 1 + (Cons + 2 + (Cons + 3 + Nil + ) + ) + +{- Combining Unions and Records + It is possible to "inline" records in cases of union + types. +-} + +data Shape + = Circle { radius :: Float } + | Rectangle + { len :: Float + , width :: Float + } + diff --git a/Exercises/exercise-1/Code/C_TypesTodo.hs b/Exercises/exercise-1/Code/C_TypesTodo.hs new file mode 100644 index 0000000..41d9057 --- /dev/null +++ b/Exercises/exercise-1/Code/C_TypesTodo.hs @@ -0,0 +1,149 @@ +{- Contents + A short introduction to Haskell; + On how to create your own types +-} + +{- Preliminary Remark + There are several different Keywords to declare new + types ('data', 'newtype', 'type'). We will discuss the + difference of these keywords and when to use + which later. +-} + +----------------------------------------------------------- +-- Product Types +----------------------------------------------------------- + +{- Records + Records are tuples with custom named fields and a + constructor. +-} +data Character = Character + { firstName :: String + , lastName:: String + } + +han :: Character +han = Character + { firstName = "Han" + , lastName = "Solo" + } + +obiWan :: Character +obiWan = Character + { firstName = "Obi-Wan" + , lastName = "Kenobi" + } + +{- "Getters" + Values can be extracted from records by using the fields + name as a function. + firstName :: Character -> String + age :: Character -> Integer +-} +solo :: String +solo = firstName han + +kenobi :: String +kenobi = lastName obiWan + +{- 'first' and 'last' can be used like any other function. +-} +firstNames :: [String] +firstNames = map firstName [ han, obiWan ] + +{- Exercise +Greet Obi-Wan and Han, each with a phrase like e.g. +"Hello Obi-Wan". Use 'map' and 'first'. +-} +greetings :: [String] +greetings = error "fixme" + +{- "Setters" + Values of a record can be changed with the syntax below. + What is important to note here is that 'han' will not be + changed. A copy of 'han' will be made with the first + name changed. +-} +ben :: Character +ben = han {firstName = "Ben"} + + +----------------------------------------------------------- +-- Sum Types +----------------------------------------------------------- + +{- Sum Types + The values of a sum type come in a number of different + variants. Each value can be uniquely assigned to one of + the variants. + Our first sum type contains exactly two values. This + type is isomorphic to 'Bool'. +-} +data YesNo + = Yes -- Constructors + | No + +yes :: YesNo +yes = Yes + +{- The following types look similar to the type above, but + they contain values in the options. +-} +data Identification + = Email String + | Username String + +hanId :: Identification +hanId = Username "han_32" + +obiWanId :: Identification +obiWanId = Email "kenobi@rebel-alliance.space" + +{- Recursive Types + A cool fact about union types is that they can be + recursive. The standard example is that of binary trees. +-} +data BinaryTree a + = Node a (BinaryTree a) (BinaryTree a) + | Leaf a + +{- + A simple binary tree with integers. +-} +tree :: BinaryTree Int +tree = Node 1 (Leaf 2) (Leaf 3) + +{- Lists are also a recursive sum type (with syntactic sugar + for 'Cons' and 'E') +-} +data MyList a + = Cons a (MyList a) + | Nil + +{- + [1,2,3] as 'MyList' +-} +list :: MyList Integer +list = + Cons + 1 + (Cons + 2 + (Cons + 3 + Nil + ) + ) + +{- Combining Unions and Records + It is possible to "inline" records in cases of union + types. +-} +data Shape + = Circle { radius :: Float } + | Rectangle + { len :: Float + , width :: Float + } + diff --git a/Exercises/exercise-1/Code/D_Control.hs b/Exercises/exercise-1/Code/D_Control.hs new file mode 100644 index 0000000..899549d --- /dev/null +++ b/Exercises/exercise-1/Code/D_Control.hs @@ -0,0 +1,168 @@ +----------------------------------------------------------- +-- If Else and Guards +----------------------------------------------------------- + +{- IfElse + * If-else expressions denote "normal" values +-} +three :: Integer +three = if True then 3 else 4 + +four :: Integer +four = if False then 0 else 4 + +collatzNext :: Integer -> Integer +collatzNext x = + if x `mod` 2 == 0 then + x `div` 2 + else + 3*x+1 + +{- Nesting + If-else expressions with multiple cases are possible + with nesting. +-} +describe :: Integer -> String +describe x = + if x < 3 then + "small" + else if x < 5 then + "medium" + else + "large" + +{- Guards + An alternative way to declare functions by case analysis + are guards. +-} +describe' :: Integer -> String +describe' x -- First match wins + | x < 3 = "small" + | x < 4 = "medium" + | otherwise = "large" + +sign :: Integer -> Integer +sign x + | x < 0 = -1 + | x > 0 = 1 + | otherwise = 0 + +{- Exercise + rewrite the function 'fIfElse' using guards. +-} +fIfElse :: Integer -> String +fIfElse n = + if n `mod` 2 == 1 then + if n `mod` 3 /= 0 then + "Oddity" + else + "Odd" + else + "Even" + +fGuard :: Integer -> String +fGuard n + | n `mod` 2 == 0 = "Even" + | n `mod` 3 == 0 = "Odd" + | otherwise = "Oddity" + +----------------------------------------------------------- +-- Cases and pattern matching +----------------------------------------------------------- + +{- Cases 1 + Aside from if-else and guards, Haskell also allows for + functions to be declared in separate clauses. +-} + +count :: Integer -> String +count 0 = "zero" +count 1 = "one" +count 2 = "two" +count _ = "I don't know" + +{- Cases 2 + There is also an alternative syntax for cases (that make + declarations a bit easier to maintain in some cases). +-} + +count' :: Integer -> String +count' n = case n of + 0 -> "zero" + 1 -> "one" + 2 -> "two" + _ -> "I don't know" + + +{- Cases Importance + The true "power" of cases is in conjunction with custom + types and pattern matching. We will learn how this works + later. +-} + +data Shape + = Rectangle Float Float + | Circle Float + +circumference :: Shape -> Float +circumference shape = case shape of + Rectangle length width -> + 2*length + 2*width + Circle radius -> + 2*radius*pi + +{- Exercise + Define a function + 'area :: Shape -> Float' + to compute the area of any given shape. Use spearate + cases such as in 'Case 1' above. +-} +area :: Shape -> Float +area (Rectangle l w) = l*w +area (Circle r) = r*r*pi + + +----------------------------------------------------------- +-- Let and where +----------------------------------------------------------- + +{- Let Bindings + It is possible to name one or more values in a + "let-block" and these abbreviations in the subsequent + expression. +-} +five :: Integer +five = + let + x = 2 + y = x + 1 + in + x + y + +myFunc :: Integer -> Integer +myFunc x = + let + complicated = + x `mod` 2 == 0 && x `mod` 4 /= 0 + in + if complicated then + x `div` 2 + else + x + 1 + +{- Where + Where is the same as let, but it does not preceed but + follow a "main" declaration. +-} +six :: Integer +six = + x + y + where + x = 2 + y = x + 2 + +myFunc' :: Integer -> Integer +myFunc' x = + (magicNumber * x) + 1 + where + magicNumber = x + 42 diff --git a/Exercises/exercise-1/Code/D_ControlTodo.hs b/Exercises/exercise-1/Code/D_ControlTodo.hs new file mode 100644 index 0000000..4a1ef06 --- /dev/null +++ b/Exercises/exercise-1/Code/D_ControlTodo.hs @@ -0,0 +1,166 @@ +----------------------------------------------------------- +-- If Else and Guards +----------------------------------------------------------- + +{- IfElse + * If-else expressions denote "normal" values +-} +three :: Integer +three = if True then 3 else 4 + +four :: Integer +four = if False then 0 else 4 + +collatzNext :: Integer -> Integer +collatzNext x = + if x `mod` 2 == 0 then + x `div` 2 + else + 3*x+1 + +{- Nesting + If-else expressions with multiple cases are possible + with nesting. +-} +describe :: Integer -> String +describe x = + if x < 3 then + "small" + else if x < 5 then + "medium" + else + "large" + +{- Guards + An alternative way to declare functions by case analysis + are guards. +-} +describe' :: Integer -> String +describe' x + | x < 3 = "small" + | x < 4 = "medium" + | otherwise = "large" + +sign :: Integer -> Integer +sign x + | x < 0 = -1 + | x > 0 = 1 + | otherwise = 0 + +{- Exercise + rewrite the function 'fIfElse' using guards. +-} +fIfElse :: Integer -> String +fIfElse n = + if n `mod` 2 == 1 then + if n `mod` 3 /= 0 then + "Oddity" + else + "Odd" + else + "Even" + + +fGuard :: Integer -> String +fGuard = error "fixme" + + +----------------------------------------------------------- +-- Cases and pattern matching +----------------------------------------------------------- + +{- Cases 1 + Aside from if-else and guards, Haskell also allows for + functions to be declared in separate clauses. +-} + +count :: Integer -> String +count 0 = "zero" +count 1 = "one" +count 2 = "two" +count _ = "I don't know" + +{- Cases 2 + There is also an alternative syntax for cases (that make + declarations a bit easier to maintain in some cases). +-} + +count' :: Integer -> String +count' n = case n of + 0 -> "zero" + 1 -> "one" + 2 -> "two" + _ -> "I don't know" + + +{- Cases Importance + The true "power" of cases is in conjunction with custom + types and pattern matching. We will learn how this works + later. +-} + +data Shape + = Rectangle Float Float + | Circle Float + +circumference :: Shape -> Float +circumference shape = case shape of + Rectangle length width -> + 2*length + 2*width + Circle radius -> + 2*radius*pi + +{- Exercise + Define a function + 'area :: Shape -> Float' + to compute the area of any given shape. Use spearate + cases such as in 'Case 1' above. +-} +area :: Shape -> Float +area = error "fixme" + + +----------------------------------------------------------- +-- Let and where +----------------------------------------------------------- + +{- Let Bindings + It is possible to name one or more values in a + "let-block" and these abbreviations in the subsequent + expression. +-} +five :: Integer +five = + let + x = 2 + y = x + 1 + in + x + y + +myFunc :: Integer -> Integer +myFunc x = + let + complicated = + x `mod` 2 == 0 && x `mod` 4 /= 0 + in + if complicated then + x `div` 2 + else + x + 1 + +{- Where + Where is the same as let, but it does not preceed but + follow a "main" declaration. +-} +six :: Integer +six = + x + y + where + x = 2 + y = x + 2 + +myFunc' :: Integer -> Integer +myFunc' x = + (magicNumber * x) + 1 + where + magicNumber = x + 42