Add files for lab 01
This commit is contained in:
parent
dffb3bdce3
commit
3bb0b1ead0
8 changed files with 1486 additions and 0 deletions
222
Exercises/exercise-1/Code/A_Basics.hs
Normal file
222
Exercises/exercise-1/Code/A_Basics.hs
Normal file
|
@ -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 ]
|
225
Exercises/exercise-1/Code/A_BasicsTodo.hs
Normal file
225
Exercises/exercise-1/Code/A_BasicsTodo.hs
Normal file
|
@ -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"
|
202
Exercises/exercise-1/Code/B_Functions.hs
Normal file
202
Exercises/exercise-1/Code/B_Functions.hs
Normal file
|
@ -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
|
||||
|
206
Exercises/exercise-1/Code/B_FunctionsTodo.hs
Normal file
206
Exercises/exercise-1/Code/B_FunctionsTodo.hs
Normal file
|
@ -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"
|
||||
|
148
Exercises/exercise-1/Code/C_Types.hs
Normal file
148
Exercises/exercise-1/Code/C_Types.hs
Normal file
|
@ -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
|
||||
}
|
||||
|
149
Exercises/exercise-1/Code/C_TypesTodo.hs
Normal file
149
Exercises/exercise-1/Code/C_TypesTodo.hs
Normal file
|
@ -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
|
||||
}
|
||||
|
168
Exercises/exercise-1/Code/D_Control.hs
Normal file
168
Exercises/exercise-1/Code/D_Control.hs
Normal file
|
@ -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
|
166
Exercises/exercise-1/Code/D_ControlTodo.hs
Normal file
166
Exercises/exercise-1/Code/D_ControlTodo.hs
Normal file
|
@ -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
|
Loading…
Reference in a new issue