diff --git a/haskell/.devcontainer/Dockerfile b/haskell/.devcontainer/Dockerfile new file mode 100644 index 0000000..cfa1e8f --- /dev/null +++ b/haskell/.devcontainer/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:24.04 + +RUN apt-get update && apt-get install -y build-essential curl libffi-dev libffi8ubuntu1 libgmp-dev libgmp10 libncurses-dev +RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh +ENV PATH="$PATH:/root/.ghcup/bin" +RUN ghcup install hls +RUN apt-get update && apt-get install -y git zlib1g-dev +RUN cabal update && cabal install ghci-dap haskell-debug-adapter +RUN cabal update && cabal install --lib HUnit + diff --git a/haskell/.devcontainer/devcontainer.json b/haskell/.devcontainer/devcontainer.json new file mode 100644 index 0000000..7703059 --- /dev/null +++ b/haskell/.devcontainer/devcontainer.json @@ -0,0 +1,12 @@ +{ + "name": "Haskell Dev Container", + "build": { + "dockerfile": "Dockerfile" + }, + "customizations": { + "vscode": { "extensions": [ + "haskell.haskell", + "phoityne.phoityne-vscode" + ]} + } +} diff --git a/haskell/.vscode/settings.json b/haskell/.vscode/settings.json new file mode 100644 index 0000000..23fd35f --- /dev/null +++ b/haskell/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "editor.formatOnSave": true +} \ No newline at end of file diff --git a/haskell/.vscode/tasks.json b/haskell/.vscode/tasks.json new file mode 100644 index 0000000..e6efc25 --- /dev/null +++ b/haskell/.vscode/tasks.json @@ -0,0 +1,61 @@ + +{ + // Automatically created by phoityne-vscode extension. + + "version": "2.0.0", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "tasks": [ + { + // F7 + "group": { + "kind": "build", + "isDefault": false + }, + "label": "haskell build", + "type": "shell", + "command": "cabal configure && cabal build" + }, + { + // F6 + "group": "build", + "type": "shell", + "label": "haskell clean & build", + "command": "cabal clean && cabal configure && cabal build" + }, + { + // F8 + "group": { + "kind": "test", + "isDefault": true + }, + "type": "shell", + "label": "haskell test", + "command": "cabal test" + }, + { + // F6 + "isBackground": true, + "type": "shell", + "label": "haskell watch", + "command": "stack build --test --no-run-tests --file-watch" + } + + , + { + // Build a single Haskell file + "type": "shell", + "label": "haskell build single file", + "command": "ghc", + "args": [ "${file}" ], + "problemMatcher": [], + "group": { + "kind": "build", + "isDefault": true + } + } + + ] +} diff --git a/haskell/00-hello-world/README.md b/haskell/00-hello-world/README.md new file mode 100644 index 0000000..332cd4b --- /dev/null +++ b/haskell/00-hello-world/README.md @@ -0,0 +1,14 @@ +# Build + +`ghc hello-world.hs` + +# Run + +`runghc hello-world.hs` + +# Run interactively +``` +ghci +ghci> :load hello-world.hs +ghci> main +``` diff --git a/haskell/00-hello-world/hello-world.hs b/haskell/00-hello-world/hello-world.hs new file mode 100644 index 0000000..ecf15d6 --- /dev/null +++ b/haskell/00-hello-world/hello-world.hs @@ -0,0 +1,2 @@ +main :: IO () +main = putStrLn "Hello, World!" diff --git a/haskell/01-expressions/README.md b/haskell/01-expressions/README.md new file mode 100644 index 0000000..4386335 --- /dev/null +++ b/haskell/01-expressions/README.md @@ -0,0 +1,131 @@ +# Haskell - Expressions + +The following illustrates some haskell basics. It can be reproduced using `ghci`. + +## Types, Literals, and Constants along with Arithmetic and Boolean Operations + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`2` | `Num a => a` | `2` | +`2.03` | `Fractional a => a` | `2.03` | +`'c'` | `Char` | `'c'` +`"hallo"` | `String` | `"hallo"` | +`True` | `Bool` | `True` | +`x=5` | `Num a => a` | `5` | Constant named `x` +`2 + 3` | `Num a => a` | `5` | Similar `-,*,^` +`2 / 3` | `Fractional a => a` | `0.6666666666666666` | +`1 > 2` | `Bool` | `False` | Similar `<, <=, >=, ==, /=` +`True && False` | `Bool` | `False` | Similar `\|\|,not` +`((2+3)*5 > 1) \|\| not (1 > 2)` | `Bool` | `True` | Right part is not evaluated due to lazy evaluation + +## Unary Functions + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`sqrt(4)` | `Floating a => a` | `2.0` | +`sqrt 4` | `Floating a => a` | `2.0` | Similar `abs,negate,signum,recip` +`sqrt` | `Floating a => a -> a` | | +`sqrt 3^2 + 4^2` | `Floating a => a` | `19.0` | +`sqrt (3^2 + 4^2)` | `Floating a => a` | `5.0` | +`negate(sqrt (3^2 + 4^2))` | `Floating a => a` | `-5.0` | +`negate $ sqrt (3^2 + 4^2)` | `Floating a => a`| `-5.0` | Function **application** operator `$` +`(negate . sqrt) (3^2 + 4^2)`| `Floating a => a`| `-5.0` | Function **composition** operator `.` Reads `negate` **after** `negate . sqrt` | +`(negate . length) "hallo"` | `Int` | `-5` | +`negatedRoot = negate . sqrt` | `Floating c => c -> c` | | Constant named `negatedRoot` + +## Binary Functions + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`div 8 4` | `Integral a => a` | `2` | Similar `mod,gcd,lcm,not` +``8 `div` 4`` | `Integral a => a` | `2` | **Infix** notation using backticks +`8 + 4` | `Num a => a` | `12` | `-,*,^,<, <=, >=, ==, /=` +`(+) 8 4` | `Num a => a` | `12` | **Prefix** notation using parenthesis +`div` | `Integral a => a -> a -> a` | | **Currying** represents multi-argument functions as a chain of single-argument functions | +`div 8` | `Integral a => a -> a` | | binds the **first** argument; Similar `(8/),(8*),(8+),(8-)` | +`div8bySomething = div 8` | `Integral a => a -> a` | | +`div8bySomething 4` | `Integral a => a` | `2` | +`uncurry div` | `Integral c => (c, c) -> c` | | accepts a single tuple argument +`uncurry div (8,4)` | `Integral c => c` | `2` | +`uncurry` | `(a -> b -> c) -> (a, b) -> c`| | +`` (`div` 4) `` | `Integral a => a -> a` | | **Sectioning** binds the **second** argument of infix functions; Similar `(/8),(*8),(+8),(-8)` | +``divSomethingBy4 = (`div` 4)`` | `Integral a => a -> a` | | +`divSomethingBy4 8` | `Integral a => a` | `2` | +`const` | `a -> b -> a` | | +`const 8` | `Num a => b -> a` | | function that ignores the input and always returns 8 +`const 8 4` | `Num a => a` | `8` | + +## Tuples + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`(1, 2)` | `(Num a, Num b) => (a, b)` | `(1, 2)` | +`(1, "hello", True)` | `(Num a => (a, String, Bool)` | `(1, "hello", True)` | +`fst (1, 2)` | `Num a => a` | `1` | +`snd (1, 2)` | `Num b => b` | `2` | +`(,) 1 2` | `Num a => a -> a -> (a, a)` | `(1, 2)` | + +## Strings + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`"hello" ++ " world"` | `String` | `"hello world"` | String concatenation +`length "hello"` | `Int` | `5` | Length of the string +`null ""` | `Bool` | `True` | Checks if the string is empty + +## Control Structures + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`if 10/2==5 then "five" else "something else"` | `String` | `"five"` | +`case 1 of { 1 -> "small"; 2 -> "medium"; _ -> "large"}` | `String` | `"small"` | patterns are overlapping +`(let x = 5*2 in (x * 2,x /2))` | `(Fractional b, Num a) => (a, b)` | `(20,5.0)` | + +## Lists + +Expression | Type (:t) | Value | Comment +--- | --- | --- | -------- +`[]` | `[a]` | `[]` | +`1:7:4:[]` | `Num a => [a]` | `[1,7,4]` | actual construction of a list +`[1,7,4]` | `Num a => [a]` | `[1,7,4]` | +`8 : [1,7,4]` | `Num a => [a]` | `[8,1,7,4]` | **cons** operator, short for construct +`[1,7,4]` | `[Num a] => [a]` | `[1,7,4]` | +`(++)` | `[a] -> [a] -> [a]` | | +`[1,7,4] ++ [8]` | `Num a => [a]` | `[1,7,4,8]` | +`[1,7,4] !! 2` | `Num a => [a]` | `4` | +`(!!)` | `[a] -> Int -> a` | | +`head [1,7,4]` | `Num a => a` | `1` | Similar `last` +`tail [1,7,4]` | `[Num a] => [a]` | `[7,4]` | Similar `init` +`length [1,7,4]` | `Int` | `3` | +`null []` | `Bool` | `True` | +`reverse [1,7,4]` | `[Num a] => [a]` | `[4,7,1]` | Similar `Data.List.sort` +`take 2 [1,7,4]` | `[Num a]=>[a]` | `[1,7]` | Similar `drop` +`take` | `Int -> [a] -> [a]` | | +`sum [1,7,4]` | `Num a=>a` | `12` | Similar `product,maximum,minimum,length` +`foldl (-) 0 [1,7,4]` | `Num a => a` | `-12` | `(((0-1)-7)-4)` +`foldl` | `Foldable t => (b -> a -> b) -> b -> t a -> b` | | +`foldr (-) 0 [1,7,4]` | `Num a => a` | `-2` | `(1-(7-(4-0)))` +`elem 7 [1,7,4]` | `Bool` | `True` | +`all even [1,7,4]` | `Bool` | `False` | Similar `any` +`filter even [1,7,4]` | `[Integral a] => [a]` | `[1,7,4]` | Similar `odd,(>1),(/=3)` +`(length . filter (>1) ) [1,7,4]` | `Int` | `2` | +`Data.List.partition (>1) [1,7,4]` | `(Ord a, Num a) => ([a], [a])` | `([7,4],[1])` +`zip [1,7,4] ["one","seven","four","eight"]` | `Num a => [(a, String)]` | `[(1,"one"),(7,"seven"),(4,"four")]` | truncating to the shorter list +`Data.List.sortOn fst $ zip [1,7,4] ["one", "seven", "four"]` | `(Ord b, Num b) => [(b, String)]` | `[(1,"one"),(4,"four"),(7,"seven")]` +`Data.List.sort ["one","seven","four","eight"]` | `[String]` | `["eight","four","one","seven"]` +`reverse $ Data.List.sort ["one","seven","four","eight"]` | `[String]` | `["seven","one","four","eight"]` +`Data.List.sortBy compare ["one","seven","four","eight"]` | `[String]` | `["eight","four","one","seven"]` +`Data.List.sortBy (flip compare) ["one","seven","four","eight"]` | `[String]` | `["seven","one","four","eight"]` +`Data.List.sortBy (curry( (uncurry compare) . (uncurry(Control.Arrow.***) (length,length)))) ["one","seven","four","eight","ten"]` | `[String]` | `["one","ten","four","seven","eight"]` | +`Data.List.sortBy (Data.Ord.comparing length) ["one","seven","four","eight","ten"]` | `[String]` | `["one","ten","four","seven","eight"]` +`map (+1) [1,7,4]` | `Num a => [a]` | `[2,8,5]` | +`map length ["one", "seven", "four"]` | `[Int]` | `[3,5,4]` | +`map (negate . abs) [1,-7,4]` | `Num a => [a]` | `[-1,-7,-4]` | +`map even [1,7,4]` | `[Bool]` | `[False,False,True]` | +`[1..5]` | `(Num a, Enum a) => [a]` | `[1,2,3,4,5]` | +`[1,3..9]` | `(Num a, Enum a) => [a]` | `[1,3,5,7,9]` | +`[1..]` | `(Num a, Enum a) => [a]` | `[1,2,3,4,5,...]` | infinite sequence +`take 4 [10..]` | `(Num a, Enum a) => [a]` | `[1,2,3,4]` | laziness in action +`[x*2 \| x <- [1..5]]` | `[Num a] => [a]` | `[2, 4, 6, 8, 10]` | List comprehension +``[x*2 \| x <- [1..5], x `mod` 3 == 0]`` | `[Num a] => [a]` | `[6]` | +`[ (a,b,c) \| c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]` | `(Num c, Eq c, Enum c) => [(c, c, c)]` | `[(3,4,5),(6,8,10)]` diff --git a/haskell/02-list-operations/README.md b/haskell/02-list-operations/README.md new file mode 100644 index 0000000..233237c --- /dev/null +++ b/haskell/02-list-operations/README.md @@ -0,0 +1,10 @@ +# Assignment + +You are provided with: + +1. A vector of names +2. A vector of ages + +Your task is to compute a list containing the UPPERCASE names of the two oldest persons whose names ends with an "a". The result shall be sorted by alphabet. + +Complete the corresponding unit test in the file `02-list-operations/list-operations.hs`. diff --git a/haskell/02-list-operations/list-operations.hs b/haskell/02-list-operations/list-operations.hs new file mode 100644 index 0000000..96aef07 --- /dev/null +++ b/haskell/02-list-operations/list-operations.hs @@ -0,0 +1,23 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} + +{-# HLINT ignore "Avoid reverse" #-} +{-# HLINT ignore "Use void" #-} + +import Data.Char (toUpper) +import Data.List (sort, sortOn) +import Test.HUnit + +names = ["Oliver", "Emma", "Liam", "Ava", "Noah", "Sophia", "James", "Mia", "Elijah", "Isabella"] + +ages = [25, 30, 22, 29, 35, 28, 40, 26, 33, 31] + +-- we are looking for the UPPERCASE names of the two oldest persons whose names ends with an a sorted alphabetically" + +extractedNames = [] + +test1 = Test.HUnit.TestCase (assertEqual "ExtractedNames" ["EMMA", "ISABELLA"] extractedNames) + +tests = TestList [test1] + +main :: IO () +main = runTestTT test1 >> return () diff --git a/haskell/03-functions/functions.hs b/haskell/03-functions/functions.hs new file mode 100644 index 0000000..a601809 --- /dev/null +++ b/haskell/03-functions/functions.hs @@ -0,0 +1,33 @@ + +-- single parameter + +-- two parameters + +-- ============= pattern matching ================= + +-- ============= guards ================= + +-- ============= recursive algorithms ================= + +-- ============= recursive list algorithms ================= + +-- ============= merge sort ================= + +-- =========== Filter all numbers that can be divided by 10 =========== + +-- ========================== standard deviation using lambdas ===================== +-- The standard deviation is a measure of the amount of variation or dispersion of a set of values. +-- It is defined as the square root of the average of the squared differences from the mean. +-- +-- Formula: +-- σ = sqrt( (1/N) * Σ (xi - μ)^2 ) +-- where: +-- - σ is the standard deviation, +-- - N is the number of values, +-- - xi represents each value, +-- - μ is the mean of the values. + +stddev seq = 0 + +-- ======================= order list of strings by length ================= + diff --git a/haskell/04-hanoi/README.md b/haskell/04-hanoi/README.md new file mode 100644 index 0000000..53f5a1c --- /dev/null +++ b/haskell/04-hanoi/README.md @@ -0,0 +1,13 @@ +# Assignment - Tower of Hanoi {.alert} + +The Tower of Hanoi is a classic mathematical puzzle that involves three rods and a number of disks of different sizes. +The puzzle starts with all the disks stacked in ascending order of size on one rod (the source rod), with the smallest disk on top. +The objective is to move the entire stack to another rod (the destination rod), following these rules: + +1. Only one disk can be moved at a time. +2. Each move consists of taking the top disk from one rod and placing it on another rod. +3. No disk may be placed on top of a smaller disk. + +**Your task:** + +Implement a function that solves the Tower of Hanoi problem for `n` disks. The rods are represented as Chars (e.g. 'a','b','c'). The result shall be a list of moving instructions. Add your implementation to the file `04-hanoi/hanoi.hs` that already contains a test and a helper function that creates the move instruction for a single move. \ No newline at end of file diff --git a/haskell/04-hanoi/hanoi.hs b/haskell/04-hanoi/hanoi.hs new file mode 100644 index 0000000..5f64857 --- /dev/null +++ b/haskell/04-hanoi/hanoi.hs @@ -0,0 +1,70 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} + +{-# HLINT ignore "Use void" #-} +import Test.HUnit + +-- some helper function that describes a single move +move :: Int -> Char -> Char -> String +move n src dst = "move disk " ++ show n ++ " from " ++ [src] ++ " to " ++ [dst] + +-- transfers a number of disks (the Int) from a source (the first Char) rod to a destination (the second Char) rod via a temp (the third char) rod +-- returns a list of required operations +hanoi :: (Int,Char,Char,Char) -> [String] + +--TODO: implement here + +-- Source, Destination, Temp +hanoiTests :: [((Int, Char, Char, Char), [String])] +hanoiTests = + [ ( (1, 'a', 'c', 'b'), + [ "move disk 1 from a to c" + ] + ), + ( (1, 'b', 'a', 'c'), + [ "move disk 1 from b to a" + ] + ), + ( (2, 'a', 'c', 'b'), + [ "move disk 1 from a to b", + "move disk 2 from a to c", + "move disk 1 from b to c" + ] + ), + ( (3, 'a', 'c', 'b'), + [ "move disk 1 from a to c", + "move disk 2 from a to b", + "move disk 1 from c to b", + "move disk 3 from a to c", + "move disk 1 from b to a", + "move disk 2 from b to c", + "move disk 1 from a to c" + ] + ), + ( (4, 'a', 'c', 'b'), + [ "move disk 1 from a to b", + "move disk 2 from a to c", + "move disk 1 from b to c", + "move disk 3 from a to b", + "move disk 1 from c to a", + "move disk 2 from c to b", + "move disk 1 from a to b", + "move disk 4 from a to c", + "move disk 1 from b to c", + "move disk 2 from b to a", + "move disk 1 from c to a", + "move disk 3 from b to c", + "move disk 1 from a to b", + "move disk 2 from a to c", + "move disk 1 from b to c" + ] + ) + ] + +hanoiTestCases :: [Test] +hanoiTestCases = map (\(input, expected) -> TestCase (assertEqual ("hanoi " ++ show input) expected (hanoi input))) hanoiTests + +tests :: Test +tests = TestList hanoiTestCases + +main :: IO () +main = runTestTT tests >> return () diff --git a/haskell/05-geometry/geometry.hs b/haskell/05-geometry/geometry.hs new file mode 100644 index 0000000..ad56b4c --- /dev/null +++ b/haskell/05-geometry/geometry.hs @@ -0,0 +1,5 @@ +-- data Entity = + +-- data Shape = + +-- area :: Shape a -> a \ No newline at end of file diff --git a/haskell/06-complex/README.md b/haskell/06-complex/README.md new file mode 100644 index 0000000..652ca41 --- /dev/null +++ b/haskell/06-complex/README.md @@ -0,0 +1,72 @@ +# Complex Numbers + +## Assignment +Define a new type `Complex` that allows arithmetic calculations in an intuitive fashion. See below for the rules that shall be implemented. Incrementally make sure that all unit tests in `06-complex/complex.hs` are passed. + +## Complex Numbers + +Complex numbers are numbers of the form: $z = a + bi$ + +- **`a`**: The real part of the complex number. +- **`b`**: The imaginary part of the complex number. +- **`i`**: The imaginary unit, where $i^2 = -1$. + +Complex numbers are used in mathematics, physics, and engineering to extend the real number system and solve equations that have no real solutions, such as $x^2 + 1 = 0$. + +Complex numbers are often represented in both rectangular form $a + bi$ and polar/exponential form $r e^{i\theta}$. + + +## Rules for Computing with Complex Numbers + +### 1. **Addition and Subtraction** +- Add or subtract the real and imaginary parts separately. +- Rule: + $(a + bi) + (c + di) = (a + c) + (b + d)i$ + $(a + bi) - (c + di) = (a - c) + (b - d)i$ + +**Examples**: +1. $(2 + 3i) + (4 + 5i) = 6 + 8i$ +2. $(0 + 3i) + (2 + 0i) = 2 + 3i$ +3. $(7 + 2i) - (3 + 6i) = 4 - 4i$ +4. $(5 + 4i) - (1 + 2i) = 4 + 2i$ + +### 2. **Multiplication** +- Use the distributive property and the rule $i^2 = -1$. +- Rule: + $(a + bi)(c + di) = (ac - bd) + (ad + bc)i$ + +**Examples**: +1. $(1 + 2i)(3 + 4i) = -5 + 10i$ +2. $(2 + 3i)(1 - i) = 5 + i$ +3. $(4 + i)(2 - 3i) = 11 - 10i$ +4. $(3 + 2i)(3 + 2i) = 5 + 12i$ + +### 3. **Division** +- Multiply numerator and denominator by the conjugate of the denominator. +- Rule: + $\frac{a + bi}{c + di} = \frac{(a + bi)(c - di)}{(c + di)(c - di)}$ + Simplifies to: + $\frac{(ac + bd) + (bc - ad)i}{c^2 + d^2}$ + +**Examples**: +1. $\frac{1 + i}{1 - i} = 0 + 1i$ +2. $\frac{3 + 2i}{4 + 3i} = \frac{18 + i}{25} = 0.72 - 0.04i$ +3. $\frac{2 + i}{1 + i} = \frac{3 - i}{2} = 1.5 - 0.5i$ + +### 4. **Complex Conjugate** +- The conjugate of $a + bi$ is $a - bi$, used for simplifications and magnitude calculation. + +**Examples**: +1. Conjugate of $3 + 4i$ is $3 - 4i$. +2. Conjugate of $5 - i$ is $5 + i$. +3. Conjugate of $-2 + 3i$ is $-2 - 3i$. + +### 5. **Magnitude (Modulus)** +- The magnitude is the distance from the origin in the complex plane. +- Rule: $|a + bi| = \sqrt{a^2 + b^2}$ + +**Examples**: +1. $|3 + 4i| = \sqrt{3^2 + 4^2} = 5$ +2. $|1 - i| = \sqrt{1^2 + (-1)^2} = \sqrt{2}$ +3. $|0 + 5i| = \sqrt{0^2 + 5^2} = 5$ + diff --git a/haskell/06-complex/complex.hs b/haskell/06-complex/complex.hs new file mode 100644 index 0000000..6236379 --- /dev/null +++ b/haskell/06-complex/complex.hs @@ -0,0 +1,123 @@ +{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-} + +{-# HLINT ignore "Use void" #-} +import Test.HUnit + +data Complex a = TODO + +instance (Show a, Num a, Eq a) => Show (Complex a) where + -- show :: (Show a, Num a, Eq a) => Complex a -> String + -- TODO + +instance (Num a, Floating a) => Num (Complex a) where + -- (+) :: (Num a, Floating a) => Complex a -> Complex a -> Complex a + -- (*) :: (Num a, Floating a) => Complex a -> Complex a -> Complex a + -- TODO + -- abs (Complex re im) = TODO + -- signum (Complex re im) = TODO + -- fromInteger n = TODO + -- negate :: (Num a, Floating a) => Complex a -> Complex a + -- negate (Complex re im) = Complex (negate re) (negate im) + +instance (Fractional a, Floating a) => Fractional (Complex a) where + -- fromRational r = TODO + -- recip (Complex re im) = TODO + + -- (Complex re1 im1) / (Complex re2 im2) = TODO + +-- conj :: Num a => Complex a -> Complex a +-- TODO + +tests :: Test +tests = + TestList + [ Test.HUnit.TestCase (assertEqual "Show 1+2i" "1+2i" (show $ Complex 1 2)), + Test.HUnit.TestCase (assertEqual "Show 1" "1" (show $ Complex 1 0)), + Test.HUnit.TestCase (assertEqual "Show i" "i" (show i)), + Test.HUnit.TestCase (assertEqual "Show 5i" "5i" (show $ Complex 0 5)), + Test.HUnit.TestCase (assertEqual "Show 0" "0" (show $ Complex 0 0)), + Test.HUnit.TestCase (assertEqual "Compare Equal" True (Complex 2 3 == Complex 2 3)), + Test.HUnit.TestCase (assertEqual "Compare Real Not Equal" False (Complex 1 3 == Complex 2 3)), + Test.HUnit.TestCase + (assertEqual "Compare Imag Not Equal" False (Complex 2 4 == Complex 2 3)), + Test.HUnit.TestCase + (assertEqual "Compare Not Equal" False (Complex 2 3 /= Complex 2 3)), + Test.HUnit.TestCase + ( assertEqual + "Addition 1" + (Complex 6.0 8.0) + (Complex 2.0 3.0 + Complex 4.0 5.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Addition 2" + (Complex 2.0 3.0) + (Complex 0.0 3.0 + Complex 2.0 0.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Subtraction 1" + (Complex 4.0 (-4.0)) + (Complex 7.0 2.0 - Complex 3.0 6.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Subtraction 2" + (Complex 4.0 2.0) + (Complex 5.0 4.0 - Complex 1.0 2.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Negation" + (Complex (-7.0) (-2.0)) + (negate (Complex 7.0 2.0)) + ), + Test.HUnit.TestCase + ( assertEqual + "Multiplication 1" + (Complex (-5.0) 10.0) + (Complex 1.0 2.0 * Complex 3.0 4.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Multiplication 2" + (Complex 5.0 1.0) + (Complex 2.0 3.0 * Complex 1.0 (-1.0)) + ), + Test.HUnit.TestCase + ( assertEqual + "Multiplication 3" + (Complex 11.0 (-10.0)) + (Complex 4.0 1.0 * Complex 2.0 (-3.0)) + ), + Test.HUnit.TestCase + ( assertEqual + "Multiplication 4" + (Complex 5.0 12.0) + (Complex 3.0 2.0 * Complex 3.0 2.0) + ), + Test.HUnit.TestCase + ( assertEqual + "Magnitude 1" + (Complex 5.0 0.0) + (abs (Complex 3.0 4.0)) + ), + Test.HUnit.TestCase + ( assertEqual + "Magnitude 2" + (Complex (sqrt 2.0) 0.0) + (abs (Complex 1.0 (-1.0))) + ), + Test.HUnit.TestCase + ( assertEqual + "Magnitude 3" + (Complex 5.0 0.0) + (abs (Complex 0.0 5.0)) + ), + TestCase (assertEqual "Conjugate of 3+4i" (Complex 3 (-4)) (conj (Complex 3 4))), + TestCase (assertEqual "Conjugate of 5-i" (Complex 5 1) (conj (Complex 5 (-1)))), + TestCase (assertEqual "Conjugate of -2+3i" (Complex (-2) (-3)) (conj (Complex (-2) 3))) + ] + +main :: IO () +main = runTestTT tests >> return () diff --git a/haskell/07-monads/monads.hs b/haskell/07-monads/monads.hs new file mode 100644 index 0000000..f68a059 --- /dev/null +++ b/haskell/07-monads/monads.hs @@ -0,0 +1,2 @@ +import System.IO +import Text.Read (readMaybe) diff --git a/haskell/08-statistics/README.md b/haskell/08-statistics/README.md new file mode 100644 index 0000000..b2336be --- /dev/null +++ b/haskell/08-statistics/README.md @@ -0,0 +1,16 @@ +# Assignment - Statistics + +The file `08-statistics/data.csv` contains random data of students. It consists of the following columns: + +- first name +- last name +- study subject + +The rest of the columns -- and that number may vary -- contain points for different tasks. The first row is a header. + +Your task is to Write a program that reads a filename from standard input. The corresponding csv-file shall be parsed and then the following statistics shall be computed and printed to standard output: + +- the best student, i.e. the student with the most accumulated points (name and points shall be printed) +- the average points of all rows for each subject (subject and average points shall be printed in ascending order) + +Implement your solution in the file `08-statistics/statistics.hs` and upload the file to moodle. diff --git a/haskell/08-statistics/data.csv b/haskell/08-statistics/data.csv new file mode 100644 index 0000000..9f4657a --- /dev/null +++ b/haskell/08-statistics/data.csv @@ -0,0 +1,97 @@ +First Name,Last Name,Subject,Task1,Task2,Task3,Task4,Task5 +Alice,Johnson,IB,87,45,68,92,33 +Bob,Smith,CSB,56,79,42,88,12 +Charlie,Williams,IMB,73,94,35,64,89 +David,Brown,UIB,41,59,77,29,83 +Emma,Jones,IB,99,21,48,75,56 +Frank,Garcia,CSB,32,81,94,55,23 +Grace,Miller,IMB,74,53,61,95,39 +Henry,Davis,UIB,90,40,28,76,65 +Isabella,Rodriguez,IB,57,86,72,49,100 +Jack,Martinez,CSB,64,31,59,97,14 +Liam,Hernandez,IMB,85,67,50,78,41 +Mia,Lopez,UIB,44,92,33,56,81 +Noah,Gonzalez,IB,68,39,74,82,47 +Olivia,Wilson,CSB,95,20,62,53,34 +Paul,Anderson,IMB,29,88,49,31,77 +Sophia,Thomas,UIB,81,56,93,42,69 +Ethan,Moore,IB,47,77,65,59,92 +Zoe,Taylor,CSB,38,95,40,70,58 +Lucas,Harris,IMB,99,45,83,62,37 +Ava,Clark,UIB,53,66,91,85,29 +Benjamin,Walker,IB,71,58,80,34,61 +Chloe,Hall,CSB,76,23,67,47,94 +Daniel,Allen,IMB,39,82,55,90,73 +Ella,Young,UIB,86,32,99,60,25 +James,King,IB,42,75,48,93,57 +Madison,Wright,CSB,63,54,72,51,88 +Nathan,Scott,IMB,91,37,79,68,45 +Emma,Green,UIB,50,89,31,77,61 +Jacob,Baker,IB,74,69,58,46,97 +Emily,Adams,CSB,88,41,66,79,30 +Samuel,Nelson,IMB,33,85,92,40,50 +Sophia,Carter,UIB,69,47,71,58,94 +Alexander,Mitchell,IB,57,98,36,61,82 +Victoria,Perez,CSB,92,28,64,73,49 +William,Roberts,IMB,45,76,89,55,31 +Ella,Turner,UIB,78,59,41,96,68 +Daniel,Phillips,IB,60,32,85,71,43 +Natalie,Campbell,CSB,55,74,99,28,79 +Mason,Parker,IMB,93,50,39,82,66 +Hannah,Evans,UIB,48,69,57,95,22 +Christopher,Edwards,IB,90,88,34,53,72 +Lucy,Collins,CSB,31,46,81,77,56 +Gabriel,Stewart,IMB,65,79,62,90,44 +Avery,Morris,UIB,80,23,47,36,97 +Ryan,Ross,IB,58,71,83,45,74 +Sophie,Reed,CSB,99,33,60,81,51 +Isaac,Adams,IMB,42,92,30,57,85 +Leah,Bennett,UIB,76,64,55,89,39 +Tyler,Barnes,IB,67,40,78,95,20 +Scarlett,Morgan,CSB,34,84,91,47,53 +John,White,IMB,89,25,72,69,98 +Eleanor,James,UIB,54,58,49,87,62 +Caleb,Wood,IB,31,100,37,46,81 +Amelia,Hill,CSB,73,45,79,66,50 +Julian,Scott,IMB,97,34,89,22,76 +Charlotte,Bryant,UIB,63,92,41,88,32 +Isaiah,Mitchell,IB,81,68,52,39,74 +Lily,Foster,CSB,56,97,44,85,29 +Elijah,Howard,IMB,70,61,36,90,99 +Peyton,Washington,UIB,49,82,67,31,75 +Xavier,Cook,IB,88,55,93,42,64 +Nora,Bailey,CSB,37,72,57,96,47 +Owen,Rivera,IMB,59,30,80,77,91 +Harper,Cooper,UIB,50,99,33,28,65 +Eli,Fisher,IB,78,40,85,92,53 +Mila,Harrison,CSB,62,47,71,89,31 +Adam,Romero,IMB,34,66,98,42,75 +Stella,Patel,UIB,96,39,54,87,68 +Carson,Cox,IB,42,55,79,100,27 +Luna,Sanders,CSB,88,74,46,29,83 +Connor,Henderson,IMB,97,32,53,60,98 +Brooklyn,Bishop,UIB,45,90,61,72,31 +Andrew,Griffin,IB,58,42,87,65,49 +Natalia,Russell,CSB,70,23,74,56,92 +Jason,Myers,IMB,81,67,35,48,79 +Alexa,Hayes,UIB,37,98,55,93,42 +Christian,Long,IB,64,71,82,45,57 +Zoey,Gibson,CSB,90,54,38,100,73 +Brayden,Graham,IMB,39,89,66,53,31 +Maya,Sullivan,UIB,85,77,62,94,25 +Jaxon,Ford,IB,48,35,100,71,83 +Claire,Wallace,CSB,59,46,88,38,76 +Dominic,Cole,IMB,78,92,44,67,50 +Eva,Jenkins,UIB,100,29,53,79,61 +Riley,Perkins,IB,55,97,31,84,30 +Leo,Brooks,CSB,72,40,91,45,99 +Hailey,Reynolds,IMB,31,58,77,90,28 +Aaron,Fox,UIB,83,72,61,35,48 +Kennedy,Stevens,IB,41,66,85,92,53 +Sebastian,Webb,CSB,99,33,60,81,51 +Melanie,Ward,IMB,76,64,55,89,39 +Adrian,Bishop,UIB,67,40,78,95,20 +Jasmine,Cook,IB,34,84,91,47,53 +Miles,Barnes,CSB,89,25,72,69,98 +Savannah,Wood,IMB,54,58,49,87,62 +Hudson,Hill,UIB,31,100,37,46,81 \ No newline at end of file diff --git a/haskell/08-statistics/statistics.hs b/haskell/08-statistics/statistics.hs new file mode 100644 index 0000000..b7b03fc --- /dev/null +++ b/haskell/08-statistics/statistics.hs @@ -0,0 +1,17 @@ +import Data.List (nub, sortOn) +import System.IO + +-- separator -> Input -> Separated Input +-- split :: Char -> String -> [String] + +-- data Entry = + +-- averages :: [Entry] -> [(String, Float)] + +-- content -> Entries +-- parse :: String -> [Entry] + +main :: IO () +main = do + putStrLn "Enter a filename: " + putStrLn "..."