This commit is contained in:
2024-12-07 21:07:38 +01:00
parent 2fded76a5c
commit a9676272f2
120 changed files with 15925 additions and 1 deletions

View File

@ -0,0 +1,126 @@
---
type: mixed
---
## Function definitions
![](Pasted%20image%2020241125164049.png)
- We don't have a return, we have an expression
- Calling a function is done like this:
```haskell
name arg1 arg2 ... argn
```
Or in this case:
``` haskell
add 1 2 -- This will return 3
```
## Types
```haskell
name :: <type>
```
### Integer
```haskell
x :: Integer
x = 1
```
### Boolean
```haskell
y :: Bool
y = True
```
### Float
```haskell
z :: Float
z = 1.6
```
- Data Types
- **Int**: platform-dependent precision integers
- **Char**: character literals, functions from Data.Char module (e.g., isSpace, isDigit, isAlpha, isLower, isUpper, toLower, toUpper, digitToInt)
- **Tuple types**: (e1, ..., en), functions fst and snd for pairs
- Type classes
- `Num`: numeric types (e.g., Int, Integer, Float, Double)
- `Eq`: equality types, with operators `==` and `/=`
- `Ord`: ordered types, with operators `<` and `<=`
[More types here](https://www.tutorialspoint.com/haskell/haskell_types_and_type_class.htm)
## Infix functions
- Functions written **between** their arguments, rather than before them.
- `5 + 3` is an infix function
- By default, functions with symbols (like `+`, `-`, `*`) are infix.
- You can also make any function infix by surrounding it with backticks (`` ` ``).
Continuing with the `add` example, we could call it like this:
```haskell
add 5 3 -- 8
```
but we could also do this
```haskell
5 `add` 3
```
This achieves more natural placement of the function (in some cases).
## Types in functions
We define them as a chain of types for the arguments (including the return type) with `->`
In the `add` example:
```haskell
add :: Integer -> Integer -> Integer
add x y = x + y
```
## Let
- Save the result of an expression and then return it
- Defines variables **before** the main expression
```haskell
let <name> = <expression> in <result>
```
i.e.
```haskell
let x = 5
y = 10
in x + y -- This will "return" x+y = 15
```
## Where
- Inverse of let - defines variables **after** the main expression
- Makes more intuitive mathematical sense
```haskell
<result> where <name> = <expression>
```
i.e.
```haskell
x + y where
x = 5
y = 10
```
## If branches
### If then else (ternary)
```haskell
equalize x y =
if xLarger then x - 1 else y - 1
where
xLarger = x > y
```

View File

@ -0,0 +1,23 @@
---
type: mixed
---
## Definition
- Pure mathematical functions
- Declarative paradigm
- Instead of writing step-by-step, we define the desired outcome
- Immutable data
- => No/Less side effects
- Programs are easier to verify
- We can mathematically prove the algorithms
## Lazy vs. Strict
- Calculations are delayed until the results are actually needed
### Example
Imagine a list of numbers, but you only need the first one that meets a condition. With lazy evaluation, the program stops processing the list as soon as it finds the result, instead of checking every number.

View File

@ -0,0 +1,36 @@
---
type: mixed
---
### Stuff
- Definition and syntax of lists
- List comprehension:
- Set comprehensions and list comprehension
- Generators (pattern <- list expression)
- Tests (Boolean expressions within list comprehension)
- Multiple generators and dependent generators
- Meaning of list comprehensions
- Important list functions:
- `length`
- `++` (concatenation)
- `reverse`
- `replicate`
- `head`, `last`, `tail`, `init`
- `take`, `drop`
- `concat`
- `zip`, `unzip`
- `and`, `or`, `sum`, `product`
- Pattern matching on lists:
- Constructors: `[]` (empty list) and `:` (cons operator)
- Patterns and pattern matching using wildcards (_)
- Recursion over lists
- Indexing lists using `!!` operator
- Examples:
- `concat` function using primitive recursion
- `insertionSort` function
- `zip` and `take` functions using multiple arguments in recursion
- `quickSort` function using general recursion
- `reverse'` function using an accumulating parameter
- `ups` function (finding maximal ascending sublists) using an accumulating parameter

View File

@ -0,0 +1,10 @@
---
type: mixed
---
### Stuff
- Definition of polymorphism and type variables
- Examples of polymorphic functions:
- `id`, `fst`, `swap`, `silly`, `silly2`
- List functions from the Prelude (e.g., `length`, `++`, `reverse`, `replicate`, `head`, `tail`, `take`, `drop`, `concat`, `zip`, `unzip`, `and`, `or`, `sum`, `product`)
- Polymorphism vs. overloading

View File

@ -0,0 +1,96 @@
---
type: mixed
---
[Divide and Conquer](Divide%20and%20Conquer.md)
## Loops do not exist in Haskell
So we have to use recursion!
```haskell
funcName <args> = ... name <args'> ...
```
where `args'` is the augmented args (recursive).
using if-then-else
```haskell
factorial :: Int -> Int
factorial n =
if n == 0 then
1
else
n * factorial(n-1)
```
## Guards
Moving from the `factorial` example:
```haskell
factorial n
| n == 0 = 1
| otherwise = n * factorial (n-1) -- Catch all
```
Note the indentation and the pipes (`|`). We can add any amount of conditions, unlike the if-then else.
## Pattern matching
i.e. with the `factorial` example. `_` is a wildcard (ignore the value). Note how we are "re-defining" the function
```haskell
factorial :: Int -> Int
factorial 0 = 1 -- Base case: when n is 0
factorial n = n * factorial (n - 1) -- Recursive call with n-1
```
## Accumulators
A variable that **accumulates** or **stores** a running total or result during the execution of a function, especially in loops or recursive functions. It is essentially a helper function.
```haskell
factorial :: Int -> Int
factorial n = factorialHelper n 1
where
factorialHelper 0 acc = acc -- Base case: when n is 0, return the accumulator
factorialHelper n acc = factorialHelper (n - 1) (n * acc) -- Multiply n by accumulator and recurse
```
In this example, by using an accumulator and tail-recursion[^1] we achieve a $\mathcal{O}(n)$ time complexity [^2]. We should **always strive for tail-recursive algorithms**, as normal recursion *can* cause stack overflow.
## Function composition
In Haskell, the **composition operator** is `(.)`. It allows us to compose two functions together into a new function.
The operator is defined as:
```haskell
(f . g) x = f (g x)
```
i.e. we have 2 functions:
```haskell
increment :: Int -> Int
increment x = x + 1
square :: Int -> Int
square x = x * x
```
we can combine them like so:
```haskell
incrementThenSquare :: Int -> Int
incrementThenSquare = square . increment
```
---
[^1]In tail recursion, the recursive call is the ***last operation*** in the function. This means that once a recursive call is made, theres no need to retain the current functions state or stack frame.
[^2] Computational limits still exist! Although the time complexity is perceived as $\mathcal{O}(n)$, that may not actually be the case, as computers are slow.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB