diff --git a/src/Pages/Software/haskell/en/caesar-cipher.md b/src/Pages/Software/haskell/en/caesar-cipher.md new file mode 100644 index 0000000..1c5f687 --- /dev/null +++ b/src/Pages/Software/haskell/en/caesar-cipher.md @@ -0,0 +1,31 @@ +## Caesar Cipher + +The implementation of the [Caesar's Cipher](https://en.wikipedia.org/wiki/Caesar_cipher) in Haskell. + +*Source*: [Programming in Haskell, by Graham Hutton](https://people.cs.nott.ac.uk/pszgmh/pih.html) + +```haskell +import Data.Char +import Prelude + +let2int :: Char -> Int +let2int c | isLower c = ord c - ord 'a' + | otherwise = ord c - ord 'A' + +int2let :: Int -> Bool -> Char +int2let n isLowercase = chr (ord (if isLowercase then 'a' else 'A') + n) + +shift :: Int -> Char -> Char +shift n c | isLower c = int2let ((let2int c + n) `mod` 26) (isLower c) + | isUpper c = int2let ((let2int c + n) `mod` 26) (isLower c) + | otherwise = c + +encode :: Int -> String -> String +encode n xs = [shift n x | x <- xs] + + +ghci> encode 5 "This is a Caesar Cipher" +-- "Ymnx nx f Hfjxfw Hnumjw" +ghci> encode (-5) "Ymnx nx f Hfjxfw Hnumjw" +-- "This is a Caesar Cipher" +``` \ No newline at end of file diff --git a/src/Pages/Software/haskell/en/conditional-expressions-and-guarded-equations.md b/src/Pages/Software/haskell/en/conditional-expressions-and-guarded-equations.md new file mode 100644 index 0000000..70046c9 --- /dev/null +++ b/src/Pages/Software/haskell/en/conditional-expressions-and-guarded-equations.md @@ -0,0 +1,41 @@ +## Conditional expressions and Guarded equations + +--- + +### Conditional expressions + +```haskell +signum :: Int -> Int +signum n = if n < 0 then -1 else + if n == 0 then 0 else 1 +``` + +And a **safetail** function, where an empty list is returned instead of an error when given an empty list. + +```haskell +safetail :: [a] -> [a] +safetail xs = if length xs > 0 then tail xs else [] +``` + +--- + +### Guarded equations + +An alternative to conditional expressions, functions can be defined with guarded equations. + +An example of the **signum** function: + +```haskell +signum :: Int -> Int +signum n | n < 0 = -1 + | n == 0 = 0 + | otherwise = 1 +``` + +Here is **safetail** with guarded equations: + +```haskell +safetail :: [a] -> [a] +safetail xs | length xs > 0 = tail xs + | otherwise = [] +``` \ No newline at end of file diff --git a/src/Pages/Software/haskell/en/graham-hutton-answers.md b/src/Pages/Software/haskell/en/graham-hutton-answers.md new file mode 100644 index 0000000..8611b55 --- /dev/null +++ b/src/Pages/Software/haskell/en/graham-hutton-answers.md @@ -0,0 +1,252 @@ +## Programming in Haskell by Graham Hutton + +This book is what I used to learn the programming language Haskell. This page contains all my exercise answers. + +*Source*: [Programming in Haskell, by Graham Hutton](https://people.cs.nott.ac.uk/pszgmh/pih.html) + +* [Chapter 4 - Defining functions](#Chapter-4) +* [Chapter 5 - List comprehensions](#Chapter-5) +* [Chapter 6 - Recursive functions](#Chapter-6) + +--- + +#### Chapter-4 +##### Defining functions + +###### exercise 1 + +```haskell +halve :: [Int] -> ([Int], [Int]) +halve xs = + (take n xs, drop n xs) + where n = length xs `div` 2 + +halve :: [Int] -> ([Int], [Int]) +halve xs = + splitAt (length xs `div` 2) xs +``` + +###### exercise 2 + +```haskell +-- a (head & tail) +third :: [a] -> a +third xs = head (tail (tail xs)) + +-- b (list indexing) +third :: [a] -> a +third xs = xs !! 2 + +-- c (pattern matching) +third :: [a] -> a +third (_:_:a:_) = a +``` + +###### exercise 3 + +```haskell +-- a (conditional expression) +safetail :: [a] -> [a] +safetail xs = if length xs > 0 then tail xs else [] + +-- b (guarded equation) +safetail :: [a] -> [a] +safetail xs | length xs > 0 = tail xs + | otherwise = [] + +-- c (pattern matching) +safetail :: [a] -> [a] +safetail [] = [] +safetail xs = tail xs + -- or: + -- safetail (_:xs) = xs +``` + +###### exercise 4 + +```haskell +(||) :: Bool -> Bool -> Bool +True || _ = True +_ || True = True +_ = False +``` + +###### exercise 5 + +```haskell +-- Use conditional expressions to define &&. +(<#>) :: Bool -> Bool -> Bool +a <#> b = + if a then + if b then True else False + else + False +``` + +###### exercise 6 + +```haskell +(<#>) :: Bool -> Bool -> Bool +a <#> b = + if a then b else False +``` + +###### exercise 7 + +```haskell +mult :: Int -> Int -> Int -> Int +mult x y z = x*y*z + +-- rewritten to use lambda functions. +mult :: Int -> (Int -> (Int -> Int)) +mult = \x -> (\y -> (\z -> x * y * z)) +``` + +###### exercise 8 + +[Luhn algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) + +```haskell +luhnDouble :: Int -> Int +luhnDouble x = x * 2 `mod` 9 + +luhn :: Int -> Int -> Int -> Int -> Bool +luhn a b c d = + sum ((map luhnDouble [a,c]) ++ [b,d]) `mod` 10 == 0 + +--ghci> luhn 1 7 8 4 +--True +--ghci> luhn 4 7 8 3 +--False +``` + +--- + +#### Chapter-5 +##### List comprehensions + +* exercise 1 + +```haskell +sum [x^2 | x <- [0..100]] +-- 338350 +``` + +###### exercise 2 + +```haskell +grid :: Int -> Int -> [(Int, Int)] +grid n m = + [(x,y) | x <- [0..n], y <- [0..m]] + +ghci> grid 1 2 +-- [(0,0),(0,1),(0,2),(1,0),(1,1),(1,2)] +``` + +###### exercise 3 + +```haskell +square :: Int -> [(Int,Int)] +square n = + [(x,y) | (x,y) <- grid n n, x /= y] + +ghci> square 2 +-- [(0,1),(0,2),(1,0),(1,2),(2,0),(2,1)] +``` + +###### exercise 4 + +```haskell +replicate :: Int -> a -> [a] +replicate n item = + [item | _ <- [1..n]] + +ghci> replicate 4 "test" +-- ["test","test","test","test"] +``` + +###### exercise 5 + +[Pythagorean theorem](https://en.wikipedia.org/wiki/Pythagorean_theorem) + +```haskell +isPythagorean :: Int -> Int -> Int -> Bool +isPythagorean x y z = + x^2 + y^2 == z^2 + +pyths :: Int -> [(Int,Int,Int)] +pyths n = + [(x,y,z) | x <- [1..n], y <- [1..n], z <- [1..n], isPythagorean x y z] + +ghci> pyths 10 +-- [(3,4,5),(4,3,5),(6,8,10),(8,6,10)] +``` + +###### exercise 6 + +[Perfect number](https://en.wikipedia.org/wiki/Perfect_number) + +```haskell +factors :: Int -> [Int] +factors n = [x | x <- [1..n], n `mod` x == 0] + +perfects :: Int -> [Int] +perfects limit = + [x | x <- [1..limit], sum (factors x) - x == x] + +ghci> perfects 10000 +-- [6,28,496,8128] +``` + +###### exercise 7 +*(I did not understand this one)* + +###### exercise 8 + +Use the **find** library function in [Data.List 9.8.2](https://downloads.haskell.org/ghc/9.8.2/docs/libraries/base-4.19.1.0-179c/Data-List.html#v:find) + +```haskell +find :: (a -> Bool) -> [a] -> Maybe a +-- The find function takes a predicate and a list and returns the first element in the list matching the predicate, or Nothing if there is no such element. +``` + +```haskell +positions :: Eq a => a -> [a] -> [Int] +positions x xs = + [i | (x',i) <- zip xs [0..], x == x'] + +-- using find function, though I doubt its correct... + +positions :: Eq a => a -> [a] -> [Int] +positions x xs = + [i | (x',i) <- zip xs [0..], isJust (find (==x) [x'])] + +positions 2 [1,1,0,2,46,6,8,9,2,3,4,2,4,9,2] +-- [3,8,11,14] + +-- You can also use: +positions :: Eq a => a -> [a] -> [Int] +positions x = elemIndices x +``` + +###### exercise 9 + +[Scalar product](https://en.wikipedia.org/wiki/Dot_product) + +```haskell +scalarproduct :: [Int] -> [Int] -> Int +scalarproduct xs ys = + sum [x*y | (x,y) <- zip xs ys] + +ghci> scalarproduct [1,2,3] [4,5,6] +-- 32 +``` + +###### execise 10 + +[Caesar's Cipher](./caesar-cipher) + +--- + +#### Chapter-6 +##### Recursive functions \ No newline at end of file diff --git a/src/Pages/Software/haskell/en/lambda-expressions.md b/src/Pages/Software/haskell/en/lambda-expressions.md new file mode 100644 index 0000000..8bf58d9 --- /dev/null +++ b/src/Pages/Software/haskell/en/lambda-expressions.md @@ -0,0 +1,47 @@ +## Lambda expressions + +You can define a function like: + +```haskell +double :: Int -> Int +double x = x + x +``` + +Which can also be written as an anonymous function: + +```haskell +\x -> x + x +``` + +Here, the **\\** symbol represents the Greek letter lambda: **λ**. This is derived from [lambda calculus](https://en.wikipedia.org/wiki/Lambda_calculus). + +Lambda expressions can be used to more explicitly state that a function is returned. + +Consider: + +```haskell +const :: a -> b -> a +const x _ = x +``` + +This can be written using a lambda expression and added parenthesis in the type definition. This is more explicit in that a function is being returned. + +```haskell +const :: a -> (b -> a) +const x = \_ -> x +``` + +And as an anonymous function. Consider the difference between these similar functions that return a list of odd numbers: + +```haskell +odds :: Int -> [Int] +odds n = map f [0..n-1] + where f x = x*2 + 1 + +odds :: Int -> [Int] +odds n = map (\x -> x*2 + 1) [0..n-1] + +-- > odds 15 +-- > [1,3,5,7,9,11,13,15,17,19,21,23,25,27,29] +``` + diff --git a/src/Pages/Software/haskell/en/lists.md b/src/Pages/Software/haskell/en/lists.md new file mode 100644 index 0000000..e2c3efe --- /dev/null +++ b/src/Pages/Software/haskell/en/lists.md @@ -0,0 +1,119 @@ +## Lists + +Lists are constructed one element at a time starting from an empty **[]** list using the *cons* operator **:**. For example, **[1,2,3]** can be decomposed as: + +```haskell +[1,2,3] +-- +1 : [2,3] +-- +1 : (2 : [3]) +-- +1 : (2 : (3 : [])) +``` + +To verify if a list with 3 numbers starts with the integer **1**, you can use pattern matching. + +```haskell +startsWithOne :: [Int] -> Bool +startsWithOne [1, _, _] = True +startsWithOne _ = False +``` + +### Access elements + +To access an element in a list, the indexing operator **!!** can be used. + +```haskell +-- Get the third element of a list. +third :: [a] -> a +third xs = xs !! 2 +``` + +### list comprehension + +* Wikipedia: [List comprehension](https://en.wikipedia.org/wiki/List_comprehension). + + +```haskell +ghci> [x^2 | x <- [1..6]] +-- [1,4,9,16,25,36] +``` + +* The **|** symbol is read as: "*such that*". +* The **<-** symbol is read as: "*drawn from*". +* And **x <- [1..6]** is called a: "*generator*". + +A list comprehension can have more than one generator. + +```haskell +ghci> [(x,y) | x <- [1,2,3], y <- [4,5]] +-- [(1,4),(1,5),(2,4),(2,5),(3,4),(3,5)] +``` + +Examples of list comprehensions: + +```haskell +halve :: [Int] -> ([Int], [Int]) +halve xs = + ([x | x <- xs, x < 4], [x | x <- xs, x >= 4]) + +-- halve [1,2,3,4,5,6] +-- ([1,2,3],[4,5,6]) +``` + +How to actually halve the list properly: + +```haskell +halve :: [Int] -> ([Int], [Int]) +halve xs = + (take n xs, drop n xs) + where n = length xs `div` 2 +-- or + splitAt (length xs `div` 2) xs +``` + +Here the **length** function replaces all elements with a 1 and sums the total: + +```haskell +length :: [a] -> Int +length xs = sum [1 | _ <- xs] +length [1,4,8,90] +-- 4 +``` + +You can use logical expressions as a **guard**, to filter values created by list comprehensions. + +```haskell +factors :: Int -> [Int] +factors n = [x | x <- [1..n], n `mod` x == 0] + +factors 20 +-- [1,2,4,5,10,20] +factors 13 +-- [1,13] +``` + +And you can use this **factors** function to determine **prime** numbers. + +* Wikipedia: [Prime number](https://en.wikipedia.org/wiki/Prime_number) + +```haskell +prime :: Int -> Bool +prime n = factors n == [1,n] + +prime 15 +--False +prime 13 +-- True +``` +And with this **prime** function, we can use list comprehension to determine a range of prime numbers! + +```haskell +primes :: Int -> [Int] +primes n = [x | x <- [2..n], prime x] + +primes 50 +-- [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47] +``` + diff --git a/src/Pages/Software/haskell/en/main.md b/src/Pages/Software/haskell/en/main.md index b2b3ca5..25fe823 100644 --- a/src/Pages/Software/haskell/en/main.md +++ b/src/Pages/Software/haskell/en/main.md @@ -2,4 +2,15 @@ These are my notes on the functional programming language Haskell. -* [Curried Functions](./curried-functions) \ No newline at end of file +* [Curried functions](./curried-functions) +* [Conditional expressions and Guarded equations](./conditional-expressions-and-guarded-equations) +* [Lambda expressions](./lambda-expressions) +* [Lists](./lists) +* [Strings](./strings) + * [Caesar Cipher](./caesar-cipher) +* [Pattern matching](./pattern-matching) +* [Recursive functions](./recursive-functions) + +### Books + +* [Programming in Haskell by: Graham Hutton (exercise answers)](./graham-hutton-answers) \ No newline at end of file diff --git a/src/Pages/Software/haskell/en/pattern-matching.md b/src/Pages/Software/haskell/en/pattern-matching.md new file mode 100644 index 0000000..604a17d --- /dev/null +++ b/src/Pages/Software/haskell/en/pattern-matching.md @@ -0,0 +1,9 @@ +## Pattern matching + +An example to determine the third element of a list, (with at least 3 elements): + +```haskell +third :: [a] -> a +third (_:_:x:_) = x +``` + diff --git a/src/Pages/Software/haskell/en/recursive-functions.md b/src/Pages/Software/haskell/en/recursive-functions.md new file mode 100644 index 0000000..6d91e93 --- /dev/null +++ b/src/Pages/Software/haskell/en/recursive-functions.md @@ -0,0 +1,21 @@ +## Recursive functions + +Recursion is the basic mechanism for looping in Haskell. + +Determine the [factorial](https://en.wikipedia.org/wiki/Factorial). + +```haskell +factorial :: Int -> Int +factorial 0 = 1 +factorial n = n * factorial (n-1) +``` + +The factorial of 3, actually is calculated as such: + +```haskell +factorial 3 +3 * factorial 2 +3 * (2 * factorial 1) +3 * (2 * (1 * factorial 0)) +3 * (2 * (1 * 1)) +``` \ No newline at end of file diff --git a/src/Pages/Software/haskell/en/strings.md b/src/Pages/Software/haskell/en/strings.md new file mode 100644 index 0000000..7ad7e8e --- /dev/null +++ b/src/Pages/Software/haskell/en/strings.md @@ -0,0 +1,33 @@ +## Strings + +Strings are not primitive types, but a list of characters. + +For example, + +```haskell +"abc" :: String +-- is actually: +['a','b','c'] :: [Char] +``` +Because of this, polymorphic functions on lists, can be used with strings. + +```haskell +"abcde" !! 2 +-- 'c' +take 3 "abcde" +-- "abc" +length "abcde" +-- 5 +zip "abc" [1,2,3,4] +-- [('a',1),('b',2),('c',3)] +``` + +And you can use list comprehensions with Strings. + +```haskell +count :: Char -> String -> Int +count x xs = length [x' | x' <- xs, x == x'] + +count 'a' "paragraph" +-- 3 +```