├── README.md ├── exam1 ├── README.md ├── version1-problem1.rkt ├── version1-problem2.rkt ├── version1-problem3.rkt ├── version1-problem4.rkt ├── version1-problem5.rkt ├── version2-problem1.rkt ├── version2-problem2.rkt ├── version2-problem3.rkt ├── version2-problem4.rkt └── version2-problem5.rkt ├── samples ├── README.md ├── cat.hs ├── count_words_from_file.rkt ├── file-utils.rkt ├── game.hs ├── helloworld.hs ├── histogram.hs ├── linecount.hs ├── read_readme.rkt ├── wordcount.hs └── words ├── week1 ├── README.md ├── homework │ ├── README.md │ ├── area.rkt │ ├── circle.rkt │ └── cube-sums.rkt ├── lab.rkt └── notes.md ├── week10 ├── README.md ├── hello.hs └── homework │ └── README.md ├── week11 ├── README.md ├── homework │ └── README.md └── lab.hs ├── week12 ├── README.md └── lab.hs ├── week13 ├── README.md └── lab.hs ├── week14 ├── README.md ├── exam-preparation │ ├── README.md │ ├── balance.hs │ ├── cone.hs │ ├── mergeandsortdigits.hs │ ├── order.hs │ └── repeater.hs ├── example.hs ├── lab.hs └── trees.hs ├── week2 ├── README.md ├── homework │ ├── README.md │ ├── binary.rkt │ ├── fence.rkt │ └── series.rkt └── lab.rkt ├── week3 ├── README.md ├── homework │ ├── README.md │ ├── math.rkt │ └── program.rkt └── lab.rkt ├── week4 ├── README.md ├── homework │ └── README.md └── lab.rkt ├── week5 ├── README.md ├── homework │ └── README.md └── list-problems.rkt ├── week6 ├── README.md ├── homework │ └── README.md └── lab.rkt ├── week7 ├── README.md ├── homework │ ├── README.md │ └── solutions.rkt └── lab.rkt ├── week8 ├── README.md ├── homework │ └── README.md └── lab.rkt └── week9 └── lab.rkt /README.md: -------------------------------------------------------------------------------- 1 | # Functional Programming 2015 2 | 3 | Functional Programming for 2nd year CS students at FMI / Sofia University. 4 | 5 | The languages that are going to be covered are Racket and Haskell. 6 | 7 | ## Intro materials 8 | 9 | * [DrRacket](http://download.racket-lang.org/) - this is going to be the IDE for the course 10 | * [The Racket Guide](http://docs.racket-lang.org/guide/index.html) - everything you need to get you started. 11 | 12 | ## Books 13 | 14 | If you pick any of the books below, you will be really really ahead of others. 15 | 16 | * [Structure and Interpretation of Computer Programs, Second Edition](https://mitpress.mit.edu/sicp/full-text/book/book-Z-H-4.html#%_toc_start) 17 | * [How To Design Problems](http://www.htdp.org/) 18 | * [Learn You A Haskell for Great Good!](http://learnyouahaskell.com/) 19 | * [The Haskell Book](http://haskellbook.com/) 20 | 21 | ## REPL and XREPL 22 | 23 | If you are using command-line based racket repl, you can use the `racket` command for the basic repl. But the basic repl sucks big time. 24 | 25 | We suggest you to use the **xrepl**. 26 | 27 | There are two ways to start it: 28 | 29 | * Run `$ rackert -il xrepl` and you will start the xrepl. You will know if you get the `->` prompt. 30 | * Run `$ racket` and inside it, type `(dynamic-require 'xrepl #f)`. You will get the `->` prompt again. 31 | 32 | XREPL is much much better. Use it! 33 | -------------------------------------------------------------------------------- /exam1/README.md: -------------------------------------------------------------------------------- 1 | # Solutions from first exam 2 | 3 | [Exam is located here](https://docs.google.com/document/d/1-ZoGJVt_1oOHVuug8t1-317HbJnB2cvngMCq5Bory1c/edit) 4 | -------------------------------------------------------------------------------- /exam1/version1-problem1.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (sum ns) 7 | (apply + ns)) 8 | 9 | (define (divisible? x y) 10 | (= 0 (remainder x y))) 11 | 12 | (define (sum-digits n) 13 | (sum (map string->number 14 | (map ~a (string->list (~a n)))))) 15 | 16 | (define (sum-sum-digit a b k) 17 | (sum (filter (lambda (x) 18 | (divisible? (sum-digits x) k)) 19 | (range a (add1 b))))) 20 | 21 | 22 | (run-tests 23 | (test-suite 24 | "Testing first problem" 25 | (check-equal? (sum-sum-digit 1 10 1) 55 "When k = 1, we get the sum of all ints between a and b, inclusive") 26 | (check-equal? (sum-sum-digit 1 10 2) 20 "When k = 2, we get the sum of all even numbers between 1 and 9. 10 is not included, becasue 1 + 0 % 2 != 0") 27 | (check-equal? (sum-sum-digit 1 10 12) 0 "When k > b, we get 0.") 28 | (check-equal? (sum-sum-digit 1 100 7) 693 "A larger example") 29 | )) 30 | -------------------------------------------------------------------------------- /exam1/version1-problem2.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (first-or-value xs value) 7 | (if 8 | (empty? xs) 9 | value 10 | (first xs))) 11 | 12 | (define (take n items) 13 | (cond 14 | [(empty? items) (list)] 15 | [(= n 0) (list)] 16 | [else (cons (first items) (take (- n 1) (rest items)))])) 17 | 18 | (define (drop n items) 19 | (cond 20 | [(empty? items) (list)] 21 | [(= n 0) items] 22 | [else (drop (- n 1) (rest items))])) 23 | 24 | (define (sublists-at index items) 25 | (define (inner items-to-take items) 26 | (cond 27 | [(> items-to-take (length items)) (list)] 28 | [else (cons (take items-to-take items) (inner (add1 items-to-take) items))])) 29 | (inner 1 (drop index items))) 30 | 31 | (define (all-consecutive-subsequences items) 32 | (apply append 33 | (map (lambda (index) 34 | (sublists-at index items)) 35 | (range 0 (length items))))) 36 | 37 | (define (is-increasing? ns) 38 | (cond 39 | [(empty? ns) #t] 40 | [(empty? (rest ns)) #t] 41 | [else (and (< (first ns) (second ns)) 42 | (is-increasing? (rest ns)))])) 43 | 44 | (define (max-ordered-sublist ns) 45 | (first-or-value 46 | (sort 47 | (filter is-increasing? (all-consecutive-subsequences ns)) 48 | #:key length >) 49 | '())) 50 | 51 | (run-tests 52 | (test-suite 53 | "Testing the problem for max ordered sublist" 54 | (check-equal? (max-ordered-sublist '(1 5 2 4 6 8 3 4 1)) '(2 4 6 8) "Example from exam") 55 | (check-equal? (max-ordered-sublist '(1 2 3 4 5 6 7 8)) '(1 2 3 4 5 6 7 8) "When the sequence is increasing, the answer is the entire list") 56 | (check-equal? (max-ordered-sublist '(7 8 9 1 2 3 4)) '(1 2 3 4) "Check if it finds the longest subsequence") 57 | (check-equal? (max-ordered-sublist '()) '() "Empty for empty list") 58 | (check-pred (lambda (x) 59 | (or 60 | (equal? (max-ordered-sublist x) '(1 2 3 4)) 61 | (equal? (max-ordered-sublist x) '(7 8 9 10)))) 62 | '(7 8 9 10 1 2 3 4) 63 | "When we have to equal subsequences, both are correct answers") 64 | 65 | )) -------------------------------------------------------------------------------- /exam1/version1-problem3.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (zip-with f xs ys) 7 | (map f xs ys)) 8 | 9 | (define (zip xs ys) 10 | (zip-with cons xs ys)) 11 | 12 | (define (enumerate l) 13 | (zip-with list (range 0 (length l)) l)) 14 | 15 | (define (take n items) 16 | (cond 17 | [(empty? items) (list)] 18 | [(= n 0) (list)] 19 | [else (cons (first items) (take (- n 1) (rest items)))])) 20 | 21 | ; all? returns #f for empty lists 22 | (define (all? p xs) 23 | (let ((n (length xs)) 24 | (m (length (filter p xs)))) 25 | (and 26 | (not (zero? n)) 27 | (= n m)))) 28 | 29 | (define (triangular? m) 30 | (all? zero? 31 | (apply append (map (lambda (enum-row) 32 | (take (first enum-row) (second enum-row))) 33 | (enumerate m))))) 34 | 35 | (run-tests 36 | (test-suite 37 | "Tests for 3rd problem" 38 | (check-true (triangular? '((1 2 3) 39 | (0 5 6) 40 | (0 0 9))) "Example from exam") 41 | (check-false (triangular? '((0 2 3) 42 | (0 0 6) 43 | (1 0 0))) "Example from exam") 44 | (check-false (triangular? '((1))) "1x1 matrix cannot be triangular") 45 | (check-true (triangular? '((1 2) 46 | (0 3))) "2x2 matrix can be triangular if there is one 0 in the bottom") 47 | (check-true (triangular? '((1 2 3 4) 48 | (0 1 5 6) 49 | (0 0 1 7) 50 | (0 0 0 8))) "A 4x4 triangular matrix") 51 | (check-false (triangular? '((1 2 3 4) 52 | (0 1 5 6) 53 | (0 0 1 7) 54 | (0 1 0 8))) "A 4x4 non-triangular matrix") 55 | )) -------------------------------------------------------------------------------- /exam1/version1-problem4.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (id x) x) 7 | 8 | (define (last? xs) 9 | (= 1 (length xs))) 10 | 11 | (define (sum ns) 12 | (apply + ns)) 13 | 14 | (define (compose f g) 15 | (lambda (x) 16 | (f (g x)))) 17 | 18 | (define (take n items) 19 | (cond 20 | [(empty? items) (list)] 21 | [(= n 0) (list)] 22 | [else (cons (first items) (take (- n 1) (rest items)))])) 23 | 24 | (define (drop n items) 25 | (cond 26 | [(empty? items) (list)] 27 | [(= n 0) items] 28 | [else (drop (- n 1) (rest items))])) 29 | 30 | (define (consify n xs) 31 | (cond 32 | [(empty? xs) (list)] 33 | [else (append (list (take n xs)) (consify n (drop n xs)))])) 34 | 35 | (define (pair-compose fs) 36 | (lambda (x) 37 | (sum (map (lambda (f) (f x)) 38 | (map (lambda (f-pair) 39 | (cond 40 | [(last? f-pair) (compose (first f-pair) id)] 41 | [else (compose (first f-pair) (second f-pair))])) 42 | (consify 2 fs)))))) 43 | 44 | (run-tests 45 | (test-suite 46 | "Testing 4th problem with function compositions" 47 | (let ((g (pair-compose (list add1 add1)))) 48 | (check-equal? (g 1) 3 "Composing add1 . add1 and calling it with 1 should give us 3")) 49 | 50 | (let* ((minus1 (lambda (x) (- x 1))) 51 | (add2 (lambda (x) (+ x 2))) 52 | (g (pair-compose (list add1 add2 minus1)))) 53 | (check-equal? (g 1) 4 "We are calculating (add1 . add2)(1) + (minus1 . id)(1) = 4 + 0 = 4")) 54 | 55 | (let ((g (pair-compose (list add1)))) 56 | (check-equal? (g 1) 2 "We should compose add1 with id and get 2")) 57 | 58 | (let* ((square (lambda (x) (* x x))) 59 | (cube (lambda (x) (* x x x))) 60 | (negate-sign (lambda (x) (* x -1))) 61 | (g (pair-compose (list square cube negate-sign square cube negate-sign square cube)))) 62 | (check-equal? (g 2) 116 "(square . cube)(2) + (negate-sign . square)(2) + (cube . negate-sign)(2) + (square . cube)(2) = 64 + (-4) + (-8) + 64 = 116")) 63 | )) -------------------------------------------------------------------------------- /exam1/version1-problem5.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (all-sums ns) 7 | (define (helper ns index sum) 8 | (cond 9 | [(= index (length ns)) 10 | (list sum)] 11 | [else (append (helper ns (add1 index) (+ sum (list-ref ns index))) 12 | (helper ns (add1 index) sum))])) 13 | (remove-duplicates (rest (sort (helper ns 0 0) <)))) 14 | 15 | 16 | (define (sort-and-dedup answer) 17 | (sort (remove-duplicates answer) <)) 18 | 19 | (run-tests 20 | (test-suite 21 | "Running tests for 5th problem - all sums" 22 | (check-equal? (sort-and-dedup (all-sums '(1 2 3))) '(1 2 3 4 5 6) "Example from exam") 23 | (check-equal? (sort-and-dedup (all-sums '(0 1 2))) '(0 1 2 3) "Example from exam") 24 | (check-equal? (sort-and-dedup (all-sums '(3 7 9))) '(3 7 9 10 12 16 19)) 25 | (check-equal? (sort-and-dedup (all-sums '(0))) '(0) "All sums from 0 are 0") 26 | (check-equal? (sort-and-dedup (all-sums '(1 2 3 4 5 6 7 8 9 10))) 27 | '(1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55) 28 | "All possible sums between numbers 1 to 10") 29 | )) -------------------------------------------------------------------------------- /exam1/version2-problem1.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (sum ns) 7 | (apply + ns)) 8 | 9 | (define (product ns) 10 | (apply * ns)) 11 | 12 | (define (divisible? x y) 13 | (= 0 (remainder x y))) 14 | 15 | (define (sum-divisors n) 16 | (define (helper i) 17 | (if (= i n) 18 | i 19 | (+ (if (= 0 (remainder n i)) i 0) (helper (add1 i))))) 20 | (helper 1)) 21 | 22 | (define (product-sum-div a b k) 23 | (product (filter (lambda (x) 24 | (divisible? (sum-divisors x) k)) 25 | (range a (add1 b))))) 26 | 27 | (run-tests 28 | (test-suite 29 | "Testing first problem" 30 | (check-equal? (product-sum-div 1 10 1) 3628800) 31 | (check-equal? (product-sum-div 1 10 2) 6300) 32 | (check-equal? (product-sum-div 1 10 12) 6) 33 | (check-equal? (product-sum-div 1 100 7) 268304563877986508606716826418256281600000) 34 | )) 35 | -------------------------------------------------------------------------------- /exam1/version2-problem2.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (first-or-value xs value) 7 | (if 8 | (empty? xs) 9 | value 10 | (first xs))) 11 | 12 | (define (take-while p xs) 13 | (cond 14 | [(empty? xs) (list)] 15 | [(p (first xs)) (cons (first xs) (take-while p (rest xs)))] 16 | [else (list)])) 17 | 18 | (define (drop-while p xs) 19 | (cond 20 | [(empty? xs) (list)] 21 | [(not (p (first xs))) xs] 22 | [else (drop-while p (rest xs))])) 23 | 24 | (define (p-equal? x) 25 | (lambda (y) 26 | (equal? x y))) 27 | 28 | (define (group xs) 29 | (cond 30 | [(empty? xs) (list)] 31 | [else (let ((p (p-equal? (first xs)))) 32 | (cons (take-while p xs) (group (drop-while p xs))))])) 33 | 34 | (define (max-eq-subseq xs) 35 | (first-or-value 36 | (sort (group xs) #:key length >) '())) 37 | 38 | (run-tests 39 | (test-suite 40 | "Tests for max-eq-subseq" 41 | (check-equal? (max-eq-subseq '(1 2 3 3 4 4 4 5 6 6)) '(4 4 4) "Example from exam") 42 | (check-pred (lambda (x) 43 | (or 44 | (equal? (max-eq-subseq x) '(2 2)) 45 | (equal? (max-eq-subseq x) '(4 4)) 46 | (equal? (max-eq-subseq x) '(6 6)))) 47 | '(1 2 2 3 4 4 5 6 6) 48 | "Example from exam") 49 | (check-equal? (max-eq-subseq '(1 1 1 1 1)) '(1 1 1 1 1) "Entire list is the result") 50 | (check-equal? (max-eq-subseq '()) '() "Empty is empty") 51 | )) 52 | -------------------------------------------------------------------------------- /exam1/version2-problem3.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (zip-with f xs ys) 7 | (map f xs ys)) 8 | 9 | (define (zip xs ys) 10 | (zip-with cons xs ys)) 11 | 12 | (define (enumerate l) 13 | (zip-with list (range 0 (length l)) l)) 14 | 15 | (define (drop n items) 16 | (cond 17 | [(empty? items) (list)] 18 | [(= n 0) items] 19 | [else (drop (- n 1) (rest items))])) 20 | 21 | ; all? returns #f for empty lists 22 | (define (all? p xs) 23 | (let ((n (length xs)) 24 | (m (length (filter p xs)))) 25 | (and 26 | (not (zero? n)) 27 | (= n m)))) 28 | 29 | ; (1 0 0) 30 | ; (4 5 0) 31 | ; (7 8 9) 32 | (define (triangular? m) 33 | (all? zero? 34 | (apply append 35 | (map (lambda (enum-row) 36 | (drop (add1 (first enum-row)) (second enum-row))) 37 | (enumerate m))))) 38 | 39 | (run-tests 40 | (test-suite 41 | "Tests for 3rd problem" 42 | (check-false (triangular? '((1 2 3) 43 | (0 5 6) 44 | (0 0 9))) "Example from exam") 45 | (check-false (triangular? '((0 2 3) 46 | (0 0 6) 47 | (1 0 0))) "Example from exam") 48 | (check-true (triangular? '((1 0 0) 49 | (4 5 0) 50 | (7 8 9))) "Example from exam") 51 | (check-false (triangular? '((0 0 1) 52 | (4 0 0) 53 | (7 0 0))) "Example from exam") 54 | (check-false (triangular? '((1))) "1x1 matrix cannot be triangular") 55 | (check-true (triangular? '((1 0) 56 | (1 3))) "2x2 matrix can be triangular if there is one 0 in the top corner") 57 | (check-false (triangular? '((1 2 3 4) 58 | (0 1 5 6) 59 | (0 0 1 7) 60 | (0 0 0 8))) "A 4x4 non-triangular matrix") 61 | (check-true (triangular? '((1 0 0 0) 62 | (0 1 0 0) 63 | (0 0 1 0) 64 | (0 1 0 8))) "A 4x4 triangular matrix") 65 | )) 66 | -------------------------------------------------------------------------------- /exam1/version2-problem4.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (sum ns) 7 | (apply + ns)) 8 | 9 | (define (zip-with f xs ys) 10 | (map f xs ys)) 11 | 12 | (define (zip xs ys) 13 | (zip-with cons xs ys)) 14 | 15 | (define (fst pair) 16 | (car pair)) 17 | 18 | (define (snd pair) 19 | (cdr pair)) 20 | 21 | (define (id x) x) 22 | 23 | (define (compose f g) 24 | (lambda (x) 25 | (f (g x)))) 26 | 27 | (define (pad-last-with to-pad times xs) 28 | (append xs 29 | (map (lambda (_) to-pad) 30 | (range 0 times)))) 31 | 32 | (define (list-compose fs1 fs2) 33 | (let* 34 | ((n (length fs1)) 35 | (m (length fs2)) 36 | (fs (pad-last-with id (abs (min 0 (- n m))) fs1)) 37 | (gs (pad-last-with id (abs (min 0 (- m n))) fs2)) 38 | (zipped (zip fs gs))) 39 | (lambda (x) 40 | (sum 41 | (map (lambda (f-pair) 42 | ((compose (fst f-pair) (snd f-pair)) x)) 43 | zipped))))) 44 | 45 | (run-tests 46 | (test-suite 47 | "Testing 4th problem with function compositions" 48 | (let ((g (list-compose (list add1 add1) (list add1 add1)))) 49 | (check-equal? (g 1) 6 "(add1 . add1)(1) + (add1 . add1)(1) = 3 + 3 = 6")) 50 | 51 | (let* ([minus1 (lambda (x) (- x 1))] 52 | [add2 (lambda (x) (+ x 2))] 53 | [g (list-compose (list add1 add2) (list minus1 minus1))]) 54 | (check-equal? (g 1) 3 "(add1 . minus1)(1) + (add2 . minus1(1) = 1 + 2 = 3")) 55 | 56 | (let ((g (list-compose (list add1) (list)))) 57 | (check-equal? (g 1) 2 "We should compose add1 with id and get 2")) 58 | 59 | (let* ((square (lambda (x) (* x x))) 60 | (cube (lambda (x) (* x x x))) 61 | (negate-sign (lambda (x) (* x -1))) 62 | [g (list-compose (list square negate-sign cube square) 63 | (list square cube negate-sign cube))]) 64 | (check-equal? (g 2) 64 "(square . square)(2) + (negate-sign . cube)(2) + (cube . negate-sign)(2) + (squre . cube)(2) = 16 + -8 + -8 + 64 = 64")) 65 | )) 66 | -------------------------------------------------------------------------------- /exam1/version2-problem5.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require rackunit) 4 | (require rackunit/text-ui) 5 | 6 | (define (all-prods ns) 7 | (define (helper ns index prod) 8 | (cond 9 | [(= index (length ns)) 10 | (list prod)] 11 | [else (append (helper ns (add1 index) (* prod (list-ref ns index))) 12 | (helper ns (add1 index) prod))])) 13 | (cond 14 | [(equal? (remove-duplicates ns) '(0)) '(0)] 15 | [else (remove-duplicates (rest (sort (helper ns 0 1) <)))])) 16 | 17 | 18 | (define (sort-and-dedup answer) 19 | (sort (remove-duplicates answer) <)) 20 | 21 | (run-tests 22 | (test-suite 23 | "Running tests for 5th problem - all products" 24 | (check-equal? (sort-and-dedup (all-prods '(1 2 3))) '(1 2 3 6) "Example from exam") 25 | (check-equal? (sort-and-dedup (all-prods '(0 1 2))) '(0 1 2) "Example from exam") 26 | (check-equal? (sort-and-dedup (all-prods '(3 7 9))) '(3 7 9 21 27 63 189)) 27 | (check-equal? (sort-and-dedup (all-prods '(1))) '(1) "All possible sums from 1") 28 | (check-equal? (sort-and-dedup (all-prods '(0))) '(0) "All products from 0 are 0") 29 | (check-equal? (sort-and-dedup (all-prods '(1 2 3 4 5 6 7 8 9 10))) 30 | '(1 31 | 2 32 | 3 33 | 4 34 | 5 35 | 6 36 | 7 37 | 8 38 | 9 39 | 10 40 | 12 41 | 14 42 | 15 43 | 16 44 | 18 45 | 20 46 | 21 47 | 24 48 | 27 49 | 28 50 | 30 51 | 32 52 | 35 53 | 36 54 | 40 55 | 42 56 | 45 57 | 48 58 | 50 59 | 54 60 | 56 61 | 60 62 | 63 63 | 64 64 | 70 65 | 72 66 | 80 67 | 84 68 | 90 69 | 96 70 | 100 71 | 105 72 | 108 73 | 112 74 | 120 75 | 126 76 | 135 77 | 140 78 | 144 79 | 150 80 | 160 81 | 162 82 | 168 83 | 180 84 | 189 85 | 192 86 | 200 87 | 210 88 | 216 89 | 224 90 | 240 91 | 252 92 | 270 93 | 280 94 | 288 95 | 300 96 | 315 97 | 320 98 | 324 99 | 336 100 | 350 101 | 360 102 | 378 103 | 384 104 | 400 105 | 420 106 | 432 107 | 448 108 | 450 109 | 480 110 | 504 111 | 540 112 | 560 113 | 576 114 | 600 115 | 630 116 | 640 117 | 648 118 | 672 119 | 700 120 | 720 121 | 756 122 | 800 123 | 810 124 | 840 125 | 864 126 | 900 127 | 945 128 | 960 129 | 1008 130 | 1050 131 | 1080 132 | 1120 133 | 1134 134 | 1152 135 | 1200 136 | 1260 137 | 1296 138 | 1344 139 | 1350 140 | 1400 141 | 1440 142 | 1512 143 | 1600 144 | 1620 145 | 1680 146 | 1728 147 | 1800 148 | 1890 149 | 1920 150 | 2016 151 | 2100 152 | 2160 153 | 2240 154 | 2268 155 | 2400 156 | 2520 157 | 2592 158 | 2688 159 | 2700 160 | 2800 161 | 2880 162 | 3024 163 | 3150 164 | 3200 165 | 3240 166 | 3360 167 | 3456 168 | 3600 169 | 3780 170 | 3840 171 | 4032 172 | 4200 173 | 4320 174 | 4480 175 | 4536 176 | 4800 177 | 5040 178 | 5184 179 | 5400 180 | 5600 181 | 5670 182 | 5760 183 | 6048 184 | 6300 185 | 6480 186 | 6720 187 | 7200 188 | 7560 189 | 8064 190 | 8100 191 | 8400 192 | 8640 193 | 9072 194 | 9450 195 | 9600 196 | 10080 197 | 10368 198 | 10800 199 | 11200 200 | 11340 201 | 11520 202 | 12096 203 | 12600 204 | 12960 205 | 13440 206 | 14400 207 | 15120 208 | 16200 209 | 16800 210 | 17280 211 | 18144 212 | 18900 213 | 19200 214 | 20160 215 | 21600 216 | 22400 217 | 22680 218 | 24192 219 | 25200 220 | 25920 221 | 26880 222 | 28800 223 | 30240 224 | 32400 225 | 33600 226 | 34560 227 | 36288 228 | 37800 229 | 40320 230 | 43200 231 | 45360 232 | 50400 233 | 51840 234 | 56700 235 | 57600 236 | 60480 237 | 64800 238 | 67200 239 | 72576 240 | 75600 241 | 80640 242 | 86400 243 | 90720 244 | 100800 245 | 103680 246 | 113400 247 | 120960 248 | 129600 249 | 134400 250 | 151200 251 | 172800 252 | 181440 253 | 201600 254 | 226800 255 | 241920 256 | 259200 257 | 302400 258 | 362880 259 | 403200 260 | 453600 261 | 518400 262 | 604800 263 | 725760 264 | 907200 265 | 1209600 266 | 1814400 267 | 3628800) 268 | "All possible products between numbers 1 to 10") 269 | )) -------------------------------------------------------------------------------- /samples/README.md: -------------------------------------------------------------------------------- 1 | # Code Samples for basic Racket / Haskell IO 2 | 3 | ## Racket 4 | 5 | * [This is a great guide for doing IO in Racket](https://docs.racket-lang.org/guide/ports.html) 6 | * [General Racket guide can be found here - Learn X in Y Minutes for Racket](https://learnxinyminutes.com/docs/racket/) 7 | 8 | ## Haskell 9 | 10 | * [IO in Haskell from LYAH](http://learnyouahaskell.com/input-and-output) 11 | -------------------------------------------------------------------------------- /samples/cat.hs: -------------------------------------------------------------------------------- 1 | -- програмата чете име на файл като вход 2 | -- прочита файла и изкарва неговото съдържание на екрана 3 | -- симулира често-срещаната команда cat 4 | 5 | main = do 6 | fileName <- getLine 7 | fileContents <- readFile fileName 8 | putStrLn fileContents 9 | -------------------------------------------------------------------------------- /samples/count_words_from_file.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require "file-utils.rkt") 4 | 5 | (define (word-count words) 6 | (define (word-count-loop words table) 7 | (cond 8 | [(empty? words) table] 9 | [else 10 | (hash-set! table (first words) (+ (hash-ref table (first words) 0) 1)) (word-count-loop (rest words) table)])) 11 | (let 12 | ([h (make-hash)]) 13 | (word-count-loop words h))) 14 | 15 | (define (main) 16 | (let* ([content-lines (lines (read-file "words"))] 17 | [data (apply append (map words content-lines))] 18 | [count (word-count data)]) 19 | (hash-for-each count (lambda (key value) 20 | (displayln (string-append key " : " (~a value))))))) 21 | (main) 22 | -------------------------------------------------------------------------------- /samples/file-utils.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide 4 | lines 5 | unlines 6 | words 7 | unwords 8 | read-file) 9 | 10 | ; Splits str in list of new lines 11 | (define (lines str) 12 | (string-split str "\n" #:trim? #t)) 13 | 14 | ; Joins the list into one str 15 | (define (unlines strs) 16 | (string-join strs "\n")) 17 | 18 | (define (words str) 19 | (string-split str " " #:trim? #t)) 20 | 21 | (define (unwords strs) 22 | (string-join strs " ")) 23 | 24 | ; You can also use (file->string path) function 25 | (define (read-file path) 26 | (call-with-input-file path 27 | (lambda (in) 28 | (port->string in)))) 29 | -------------------------------------------------------------------------------- /samples/game.hs: -------------------------------------------------------------------------------- 1 | import System.Random 2 | import Control.Monad 3 | 4 | checkAnswer :: (Int, String, Int) -> Int -> Bool 5 | checkAnswer (a, "+", b) given = a + b == given 6 | checkAnswer (a, "-", b) given = a - b == given 7 | checkAnswer (a, "*", b) given = a * b == given 8 | checkAnswer (a, "%", b) given = a `div` b == given 9 | checkAnswer (_, _, _) _ = False 10 | 11 | randomRange :: Int -> Int -> IO Int 12 | randomRange a b = getStdRandom (randomR (a,b)) 13 | 14 | randomEquation :: IO (Int, String, Int) 15 | randomEquation = do 16 | number1 <- randomRange 1 100 17 | number2 <- randomRange 1 100 18 | operatorIndex <- randomRange 0 3 19 | let operator = ["+", "-", "*", "%"] !! operatorIndex 20 | return (number1, operator, number2) 21 | 22 | tauntUser :: (Int, String, Int) -> IO () 23 | tauntUser (a, oper, b) = putStrLn $ "CAN YOU ANSWER THIS?\n" ++ ((show a) ++ " " ++ oper ++ " " ++ (show b)) ++ " = ?" 24 | 25 | main = forever $ do 26 | expression@(a, oper, b) <- randomEquation 27 | tauntUser expression 28 | userGuess <- getLine 29 | let parsedGuess = read userGuess :: (Int) 30 | let result = checkAnswer expression parsedGuess 31 | if result then putStrLn "You guessed right!" else putStrLn "You guessed wrong :(" 32 | -------------------------------------------------------------------------------- /samples/helloworld.hs: -------------------------------------------------------------------------------- 1 | -- програмата се пуска така : runhaskell helloworld.hs 2 | -- програмата чака за вход от потребителя (неговото име) 3 | -- и изкарва съобщение като изход 4 | main = do 5 | name <- getLine 6 | putStrLn $ unwords ["Hello", name, "!"] 7 | 8 | -------------------------------------------------------------------------------- /samples/histogram.hs: -------------------------------------------------------------------------------- 1 | import Data.List 2 | 3 | -- програмата чете файл 4 | -- и изкарва на екрана хистограма на думите му 5 | 6 | 7 | -- pure part 8 | 9 | sortGT (w1, c1) (w2, c2) 10 | | c1 < c2 = GT 11 | | c1 > c2 = LT 12 | | c1 == c2 = compare w1 w2 13 | 14 | histogram :: [String] -> [(String, Int)] 15 | histogram allWords = sortBy sortGT $ map (\g -> (head g, length g) ) $ group $ sort allWords 16 | 17 | singleHistResultToString :: (String, Int) -> String 18 | singleHistResultToString (word, count) = concat [word, " - ", (show count)] 19 | 20 | -- impure part 21 | 22 | main = do 23 | fileName <- getLine 24 | contents <- readFile fileName 25 | let 26 | allWords = histogram $ words contents 27 | preparedForPrint = map singleHistResultToString allWords 28 | mapM_ putStrLn preparedForPrint -- mapM_ map-ва IO функция върху списък от неща (В случая, String-ове) 29 | 30 | -------------------------------------------------------------------------------- /samples/linecount.hs: -------------------------------------------------------------------------------- 1 | -- програмта приема име на файл като вход 2 | -- чете файла и изкарва броя на редовете, които се срещат в него 3 | 4 | -- pure part 5 | 6 | countLines :: String -> Int 7 | countLines str = length $ lines str 8 | 9 | 10 | -- impure part 11 | 12 | -- тук имаме собствена функция, която е impure 13 | -- тя връща IO String, като използва bind оператор (>>=), за да направи следното : 14 | -- първо прочита един ред от getLine и веднага дава String-овата стойност като аргумент на readFile 15 | -- readFile от своя страна връща IO String 16 | getFileContents :: IO String 17 | getFileContents = getLine >>= readFile 18 | 19 | main = do 20 | fileContents <- getFileContents 21 | let 22 | lineCount = countLines fileContents 23 | putStrLn $ unwords ["There", "are", "a", "total", "of", (show lineCount), "lines", "in", "the", "file!"] 24 | -------------------------------------------------------------------------------- /samples/read_readme.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require "file-utils.rkt") 4 | 5 | (define (main) 6 | (let* ([contents (read-file "README.md")]) 7 | (displayln contents) 8 | (displayln (length (lines contents))))) 9 | 10 | (main) 11 | -------------------------------------------------------------------------------- /samples/wordcount.hs: -------------------------------------------------------------------------------- 1 | -- програмта приема име на файл като вход 2 | -- чете файла и изкарва броя на думите, които се срещат в него 3 | 4 | -- pure part 5 | 6 | countWords :: String -> Int 7 | countWords str = length $ words str 8 | 9 | 10 | -- impure part 11 | 12 | -- тук имаме собствена функция, която е impure 13 | -- тя връща IO String, като използва bind оператор (>>=), за да направи следното : 14 | -- първо прочита един ред от getLine и веднага дава String-овата стойност като аргумент на readFile 15 | -- readFile от своя страна връща IO String 16 | getFileContents :: IO String 17 | getFileContents = getLine >>= readFile 18 | 19 | main = do 20 | fileContents <- getFileContents 21 | let 22 | wordCount = countWords fileContents 23 | putStrLn $ unwords ["There", "are", "a", "total", "of", (show wordCount), "words", "in", "the", "file!"] 24 | -------------------------------------------------------------------------------- /samples/words: -------------------------------------------------------------------------------- 1 | This is a story for one panda panda panda 2 | -------------------------------------------------------------------------------- /week1/README.md: -------------------------------------------------------------------------------- 1 | # Week 1 2 | 3 | Introduction to the Racket language. 4 | 5 | * [Some really raw notes can be found here](notes.md) 6 | * [First homework is here](homework/) 7 | -------------------------------------------------------------------------------- /week1/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 1 2 | 3 | Implement each problem in a separate `.rkt` file. 4 | 5 | ## Product of digits 6 | 7 | Implement a racket function that finds the product of digits of a number. 8 | 9 | ```racket 10 | (define (product-digits n) 11 | ...) 12 | ``` 13 | 14 | Here are few examples: 15 | 16 | ``` 17 | -> (product-digits 123) 18 | 6 19 | -> (product-digits 12345) 20 | 120 21 | -> (product-digits 12355430) 22 | 0 23 | ``` 24 | 25 | ## Circle 26 | 27 | Implement a racket predicate called `circle?` that checks if a point is inside / on a circle. 28 | 29 | The predicate should take 5 arguments - 30 | 31 | * `(point-x, point-y)` - the coordinates of the point. 32 | * `(circle-x, circle-y)` - the coordinates of the center of the circle 33 | * `radisu` - the radius of the circle. 34 | 35 | ```racket 36 | (define (circle? circle-x circle-y radius point-x point-y) 37 | ...) 38 | ``` 39 | 40 | The formula that must be satisfied is: 41 | 42 | ``` 43 | (point-x - circle-x)^2 + (point-y - circle-y)^2 <= radius^2 44 | ``` 45 | 46 | Everthing is happening on a [Cartesian coordinate system](https://en.wikipedia.org/wiki/Cartesian_coordinate_system) 47 | 48 | Here are few examples: 49 | 50 | ``` 51 | -> (circle? 0 0 3 2 2) 52 | #t 53 | -> (circle? 0 0 5 3 3) 54 | #t 55 | -> (circle? 0 0 3 4 3) 56 | #f 57 | -> (circle? 10 10 3 11 12) 58 | #t 59 | -> (circle? 10 10 3 11 13) 60 | #f 61 | ``` 62 | 63 | ## Area of triangle 64 | 65 | Implement a Racket function that calculates the area of a given triangle using [Heron's formula](https://en.wikipedia.org/wiki/Heron%27s_formula). 66 | 67 | ```racket 68 | (define (area a b c) 69 | ...) 70 | ``` 71 | 72 | This one is easy. You will figure it out ;) 73 | 74 | ## Prime number 75 | 76 | Implement a Racket predicate that cheks if a given number is prime. 77 | 78 | ```racket 79 | (define (prime? n) 80 | ...) 81 | ``` 82 | 83 | [Here is a list of prime numbers to test with](https://primes.utm.edu/lists/small/1000.txt) 84 | 85 | ## Cube sums 86 | 87 | Implement a Racket predicate called `cube-sums?` that takes one integer `n` and returns `#t` if there are any two integers `a` and `b` which has the following property: 88 | 89 | ``` 90 | n = a^3 + b^3 91 | ``` 92 | 93 | ```racket 94 | (define (cube-sums? n) 95 | ...) 96 | ``` 97 | 98 | For example, lets see few numbers with cube sums: 99 | 100 | ``` 101 | 2 = 1^3 + 1^3 102 | 9 = 2^3 + 1^3 103 | 16 = 2^3 + 2^3 104 | 91 = 4^3 + 3^3 105 | 1729 = 9^3 + 10^3 106 | 87539319 = 167^3 + 436^3 107 | ``` 108 | 109 | **Extra credit** 110 | 111 | Implement a Racket function called `(count-cube-sums from to)` which counts how many numbers satisfy the `cube-sums?` predicate between `[from, to]` 112 | -------------------------------------------------------------------------------- /week1/homework/area.rkt: -------------------------------------------------------------------------------- 1 | (define (half-perimeter a b c) 2 | (/ (+ a b c) 2)) 3 | 4 | (define (area a b c) 5 | (let* 6 | ([p (half-perimeter a b c)] 7 | [pa (- p a)] 8 | [pb (- p b)] 9 | [pc (- p c)]) 10 | (sqrt (* p pa pb pc)))) 11 | -------------------------------------------------------------------------------- /week1/homework/circle.rkt: -------------------------------------------------------------------------------- 1 | (define (square x) 2 | (* x x)) 3 | 4 | (define (circle? circle-x circle-y radius point-x point-y) 5 | (<= (+ (square (- point-x circle-x)) (square (- point-y circle-y))) (square radius))) 6 | -------------------------------------------------------------------------------- /week1/homework/cube-sums.rkt: -------------------------------------------------------------------------------- 1 | (define (cube-sum a b) 2 | (+ (* a a a) (* b b b))) 3 | 4 | (define (cube-sum-equal? n a b) 5 | (= n (cube-sum a b))) 6 | 7 | (define (search-cube-sums-a target a b) 8 | (cond 9 | [(> (cube-sum a b) target) (list)] 10 | [(cube-sum-equal? target a b) (append (list target a b) (search-cube-sums-a target (+ a 1) b))] 11 | [else (append (list) (search-cube-sums-a target (+ a 1) b))])) 12 | 13 | (define (search-cube-sums target a b) 14 | (cond 15 | [(> (cube-sum a b) target) (list)] 16 | [else (append (search-cube-sums-a target a b) (search-cube-sums target a (+ b 1)))])) 17 | 18 | (define (list-cube-sums to) 19 | (cond 20 | [(zero? to) (list)] 21 | [else (append (list (search-cube-sums to 1 1)) (list-cube-sums (- to 1)))])) 22 | 23 | -------------------------------------------------------------------------------- /week1/lab.rkt: -------------------------------------------------------------------------------- 1 | (define (square x) 2 | (* x x)) 3 | 4 | (define (square-sum a b) 5 | (+ (square a) (square b))) 6 | 7 | (define (triangle? a b c) 8 | (and 9 | (> (+ b c) a) 10 | (> (+ a b) c) 11 | (> (+ a c) b))) 12 | 13 | (define (pyth? a b c) 14 | (= (square c) (square-sum a b))) 15 | 16 | ; Факториел, написан чрез if 17 | (define (fact2 n) 18 | (if 19 | (zero? n) 20 | 0 21 | (* n (fact2 (- n 1))))) 22 | 23 | ; Факториел, написан чрез cond 24 | (define (fact n) 25 | (cond 26 | [(zero? n) 1] 27 | [else (* n (fact (- n 1)))])) 28 | 29 | (define (cut-last-digit n) 30 | (remainder n 10)) 31 | 32 | (define (cut-number-from-last-digit n) 33 | (quotient n 10)) 34 | 35 | ; Използваме по-хубави имена за функциите remainder и quotient 36 | ; Така разбираме какво се случва 37 | (define (sum-digits n) 38 | (cond 39 | [(zero? n) 0] 40 | [else (+ (cut-last-digit n) (sum-digits (cut-number-from-last-digit n)))])) 41 | -------------------------------------------------------------------------------- /week1/notes.md: -------------------------------------------------------------------------------- 1 | # First class 2 | 3 | Functional programming is becoming more and more important. The paradigm is finding place even in languages like C++ and Java. 4 | 5 | We are going to study Racket & Haskell. 6 | 7 | * Racket is a dynamically typed language from the LISP family. 8 | * It is very different from the other languages that we know. 9 | * We are going to talk about values, expressions and functions. 10 | 11 | First of all, what is the purpose of a variable? 12 | 13 | ```c++ 14 | int a = 5; 15 | a = 6; 16 | a++; 17 | ``` 18 | 19 | We hold a value, assing it to a name and change it after this. This is the basis of our programming knowledge. 20 | 21 | Our variables are our data. We have expressions and statements. Expressions are evaluated to values. 22 | 23 | What is a statement? An expression with some kind of empty value. 24 | 25 | In Racket, the first very important thing is that everything is an expression. Event `if` statements. 26 | 27 | The most simple expressions are values themselves - `5`, `"racket"`. 28 | 29 | Racket expressions are built using `()`: 30 | 31 | ```racket 32 | -> (+ 1 2) 33 | 3 34 | -> (= 1 1) 35 | #t 36 | -> (= 1 2) 37 | #f 38 | -> (if (= 1 1) 42 43) 39 | 42 40 | ``` 41 | 42 | **We use prefix notation** - it is not `(1 + 2)` but `(+ 1 2)`. 43 | 44 | Now, to understand more about the language, we must see how we can define functions. 45 | 46 | For example, if we want to define the simple **square** function, this is the way: 47 | 48 | ```racket 49 | (define (square x) 50 | (* x x)) 51 | ``` 52 | 53 | The syntax is as follows: 54 | 55 | ```racket 56 | (define (function-name arg1 arg2 ... argn) 57 | (function-body-expression)) 58 | ``` 59 | 60 | **The function returns the value of the `(function-body-expression)`** 61 | 62 | Now, if we want to call the function from the REPL: 63 | 64 | ``` 65 | -> (square 2) 66 | 4 67 | ``` 68 | 69 | We use the following notation: `(function-name arg1 arg2 ... argN)` 70 | 71 | This builds almost the entire language. 72 | 73 | Aritmetic operations are functions: 74 | 75 | ``` 76 | -> (+ 1 2) 77 | 3 78 | -> (* 1 2) 79 | 2 80 | -> (- 1 2) 81 | -1 82 | -> (/ 1 2) 83 | 1/2 84 | ``` 85 | 86 | ## Everything is an expression 87 | 88 | If we have the following syntax for if expression: 89 | 90 | ```racket 91 | (if 92 | (boolean-expression) 93 | (truth-expression) 94 | (false-expression)) 95 | ``` 96 | 97 | First, the `(boolean-expression)` is evaluated to either `#t` or `#f`. Then, one of the two expressions are evaluated and the entire if expressions has the value either of the `(truth-expression)` or the `(false-expression)` 98 | 99 | Lets see this in action - a function that takes one integer and returns `"zero"` if `(= n 0)` or `"else"`. 100 | 101 | ```racket 102 | (define (zero-or-else? n) 103 | (if (= n 0) 104 | "zero" 105 | "else)) 106 | ``` 107 | 108 | **As you can see, the function returns the result of the if expression.** 109 | 110 | ## Function with multiple lines 111 | 112 | Our function is not limited to only one body expression. The rule is that the function return the result of the last body expression. 113 | 114 | ```racket 115 | (define (square-and-print n) 116 | (display "I am squaring") 117 | (* n n)) 118 | ``` 119 | 120 | There the result of the function is going to be the result of the last expression `(* n n)` 121 | 122 | Suck functions are called functions with side effects. This is a big topic and we are going to discuss it later. For now, we are going to to only functions with one body expression. 123 | 124 | ## Cond expression 125 | 126 | Now, we have the `if` statement. But if we like to do multiple checks, there is a better expression - `cond`. 127 | 128 | ```racket 129 | (cond 130 | [(check-rule-expression) (expression-to-evaluate-if-check-rule-is-true)] 131 | [(check-rule-expression2) (expression-if-check-rule-is-true)] 132 | ... 133 | [else (else-expression)]) 134 | ``` 135 | 136 | This can work as your normal `switch` statement in different languages. 137 | 138 | The `cond` expression returns the first expression which check-rule holds true. 139 | 140 | For example: 141 | 142 | ```racket 143 | (define (traffic-light color) 144 | (cond 145 | [(equal? color "red") "stop"] 146 | [(equal? color "yellow") "prepare"] 147 | [(equal? color "green") "go"] 148 | [else "sorry, no idea."])) 149 | ``` 150 | 151 | ## Recursion 152 | 153 | Now, if we want to solve some problems, we need to rely on our good old friend - recursion. We are going to build an expression in our recursion which in the end is going to be evaluated to the proper value. 154 | 155 | For example, here is a function that finds the factorial of n: 156 | 157 | ```racket 158 | (define (fact n) 159 | (cond 160 | [(= n 0) 1] 161 | [else (* n (fact (- n 1)))])) 162 | ``` 163 | 164 | The magic here is the recursion part: `(* n (fact (- n 1)))`. 165 | 166 | Lets see how this works: 167 | 168 | ``` 169 | (fact 5) = 170 | (* 5 (fact 4)) = 171 | (* 5 (* 4 (fact 3))) = 172 | (* 5 (* 4 (* 3 (fact 2)))) = 173 | (* 5 (* 4 (* 3 (* 2 (fact 1))))) = 174 | (* 5 (* 4 (* 3 (* 2 (* 1 (fact 0)))))) = 175 | (* 5 (* 4 (* 3 (* 2 (* 1 1))))) = 176 | (* 5 (* 4 (* 3 (* 2 1)))) = 177 | (* 5 (* 4 (* 3 2))) = 178 | (* 5 (* 4 6)) = 179 | (* 5 24) = 180 | 120 181 | ``` 182 | 183 | The recursion is actually building one expression. The end of the recursion triggers the evaluation of the expression. 184 | 185 | With this knowledge, now we can solve some problems. 186 | -------------------------------------------------------------------------------- /week10/README.md: -------------------------------------------------------------------------------- 1 | # Седмица 8 - въведение в Haskell. 2 | 3 | Запознахме се с езика за функционално програмиране Haskell. 4 | За него са характерни следните факти: 5 | 6 | * Статично типизиран е. Функциите имат типова сигнатура, която описва типовете на параметрите и типът на резултата 7 | * Освен, че е статично типизиран, Haskell поддържа type inference - може да предположи типовата сигнатура на нашата функция, като не се налага да пишем такава сигнатура за всяка функция. 8 | * Езикът е "pure functional", където [всяка функция е чиста](http://en.wikipedia.org/wiki/Pure_function) Ако извикаме два пъти една и съща функция с едканви аргументи, трябва да получим един и същ отговор. Функциите в Haskell нямат странични ефекти. 9 | * Lazy evaluation. В Haskell, може да направим следния израз: 10 | 11 | ```haskell 12 | take 10 [1..] 13 | ``` 14 | 15 | Което ще ни върне първите 10 елемента на списъка от 1 до безкрайност. 16 | 17 | Повече за това, може да прочетете тук - http://en.wikipedia.org/wiki/Lazy_evaluation 18 | 19 | * Haskell е език, който се компилира. GHCi представлява интерактивен REPL, в който може да зареждаме Haskell-ски файлове, които се компилират, след което може да извикваме функции от тях. 20 | 21 | ## Основен синтаксис и функции 22 | 23 | ### Синтаксис на функция 24 | 25 | В Haskell, пишем функции, а нашата програма представлява композиция от функции. 26 | 27 | Основният синтаксис за функция изглежда така: 28 | 29 | ```haskell 30 | inc x = x + 1 31 | ``` 32 | 33 | * Нямаме ключова дума за функция - започваме функция с нейното име 34 | * Тялото на функцията се намира след знака за `=` 35 | * Всяка функция връща стойност, представляващ резултатът на изразът в тялото и 36 | 37 | В Haskell, може да дефинираме строго типът на нашата функция: 38 | 39 | ```haskell 40 | inc :: Int -> Int 41 | int x = x + 1 42 | ``` 43 | 44 | В този случай, може да увеличаваме само цели числа. 45 | 46 | Ако пробваме следния израз, ще получим грешка, защото `2.0` e `Double` 47 | 48 | ```haskell 49 | > inc 2.0 50 | :4:5: 51 | No instance for (Fractional Int) arising from the literal `2.0' 52 | Possible fix: add an instance declaration for (Fractional Int) 53 | In the first argument of `inc', namely `2.0' 54 | In the expression: inc 2.0 55 | In an equation for `it': it = inc 2.0 56 | ``` 57 | 58 | Ако оставим Haskell сам да предположи тъпът на подобна наша функция: 59 | 60 | ```haskell 61 | dec x = x - 1 62 | ``` 63 | 64 | Ако попитаме с `:t` в GHCi, Haskell ще ни каже, че може да оперира с тази функция в/у по-голямо множество от типове, които попадат в типовия клас `Num`. Това обаче, ще дойде по-късно в играта. 65 | 66 | ```haskell 67 | > :t dec 68 | dec :: Num a => a -> a 69 | ``` 70 | 71 | Но с такава типова декларация, предположена от самия Haskell, може да смятаме повече неща: 72 | 73 | ```haskell 74 | > dec 1 75 | 0 76 | > dec 2.5 77 | 1.5 78 | ``` 79 | 80 | ### Pattern Matching 81 | 82 | В Haskell имаме възможността да напишем функция, която да има множество дефиниции, като в отделните дефиниции да очакваме конкретна стойност или конкретен вид на нашия аргумент. 83 | 84 | Например, функцията, която пресмята factoriel, изглежда така: 85 | 86 | ```haskell 87 | factorial 0 = 1 88 | factorial n = n * factorial (n - 1) 89 | ``` 90 | 91 | Първата дефиниция е дъното на нашата рекурсия и ще се извика само, когато `n` стане равно на `0` 92 | 93 | По подобен начин може да решим и въпросът с n-тото число на Фибоначи: 94 | 95 | ```haskell 96 | fib 1 = 1 97 | fib 2 = 1 98 | fib n = fib (n - 1) + fib (n - 2) 99 | ``` 100 | 101 | Pattern matching-a ни позволява да правим доста интересни неща, с които ще се занимаваме по-нататък: 102 | 103 | Имплементацията на сумата на цифрите на 1 число, с Pattern Matching, би изглеждала така: 104 | 105 | ```haskell 106 | sumDigits :: Int -> Int 107 | sumDigits 0 = 0 108 | sumDigits n = (mod n 10) + sumDigits (div n 10) 109 | ``` 110 | 111 | ### Guards - подобие на cond в Haskell 112 | 113 | Когато искаме да направим функция, която трябва да се справи с множество случаи (if-elses) и търсим нещо подобно на `cond` в Scheme, то тогава на помощ идват стражите! 114 | 115 | Синтактично те изглеждат като `|` и се пишат след дефиницията на функцията, като трябва да са на 1 табулация (или 4 разстояния от началото на функцията) 116 | 117 | Например, функцията, която пресмята абсолютна стойност на x: (Казва се така, защото вече имаме подобна вградена): 118 | 119 | ```haskell 120 | abs' x 121 | | x < 0 = (-x) 122 | | otherwise = x 123 | ``` 124 | 125 | Всеки `|` се проверява, дали първият израз ще върне истина. Ако това не се случи, се продължава към следващите стражи до достигане на `otherwise` 126 | 127 | 128 | Обърнете внимание, че основната функция няма `=`, преди стражите! 129 | 130 | Имплементация на сумата на цифрите на едно число би изглеждала така: 131 | 132 | ```haskell 133 | sumDigits :: Int -> Int 134 | sumDigits n 135 | | n == 0 = 0 136 | | otherwise = (mod n 10) + sumDigits (div n 10) 137 | ``` 138 | 139 | ### Локални дефиниции чрез where 140 | 141 | Както в Scheme можехме да дефинираме функции в нашите функции и те да бъдат скрити от глобалния scope, така в Haskell имаме специален `where` синтаксис за локални дефиниции 142 | 143 | Например, следната функция изчислява колко храна ще ни трябва за една седмица, в зоопарк с N на брой панди, като знаем, че всяка панда яде по 10 бамбука и 2 чувала с ориз на ден на ден: 144 | 145 | ```haskell 146 | foodForPandas :: Int -> Int -> Int 147 | foodForPandas timeSpan pandaCount = totalFood 148 | where 149 | bambooPerDayPerPanda = 10 150 | ricePerDayPerPanda = 2 151 | bambooCount = bambooPerDayPerPanda * pandaCount * timeSpan 152 | riceCount = ricePerDayPerPanda * pandaCount * timeSpan 153 | totalFood = bambooCount + riceCount 154 | ``` 155 | 156 | Т.е ако имаме 7 дена и 10 панди, ще ни трябват около 840 единици с храна. 157 | 158 | Синтаксисът изисква дефинициите след `where` да са на 1 табулация от горния ред. 159 | 160 | За да използваме вече дефинирано име в друга дефиниция, то тази друга дефиниция трябва да се намира под вече дефинираното име. 161 | 162 | Други два примера са функциите за проверка на просто число и намиране на лице на триъгълник по формула на Херон: 163 | 164 | ```haskell 165 | isPrime :: Int -> Bool 166 | isPrime n 167 | | n <= 1 = False 168 | | otherwise = isPrime' 2 n 169 | where 170 | isPrime' current n 171 | | current == n = True 172 | | mod n current == 0 = False 173 | | otherwise = isPrime' (current + 1) n 174 | ``` 175 | 176 | ```haskell 177 | area :: Double -> Double -> Double -> Double 178 | area a b c = sqrt (p * pa * pb * pc) 179 | where 180 | p = (a + b + c) / 2 181 | pa = p - a 182 | pb = p - b 183 | pc = p - c 184 | ``` 185 | 186 | ## Материали за четене 187 | 188 | Прочетете следните глави от Learn You a Haskell: 189 | 190 | * http://learnyouahaskell.com/introduction 191 | * http://learnyouahaskell.com/starting-out до списъци 192 | 193 | ## Настройки за редактор 194 | 195 | Тъй като Haskell изисква специфичен синтаксис с табулации, е хубаво да си настроите вашият текстов редактор, така, че да ви помага. 196 | 197 | За Sublime, може да сложите следните language preferences, като: 198 | 199 | 1. Отворите .hs файл и отидете на Preferences 200 | 2. Oт там изберете `-> Settings More -> Syntax Specific - User` 201 | 3. И сложете следните настройки: 202 | 203 | ```json 204 | { 205 | "draw_white_space": "all", 206 | "tab_size": 4, 207 | "translate_tabs_to_spaces": true 208 | } 209 | ``` 210 | 211 | Накратко - ще виждате всеки white space и един tab ще бъде 4 space-a. 212 | 213 | Освен това, може да си инсталирате следния Plugin - https://github.com/SublimeHaskell/SublimeHaskell 214 | -------------------------------------------------------------------------------- /week10/hello.hs: -------------------------------------------------------------------------------- 1 | inc :: Int -> Int 2 | inc x = x + 1 3 | 4 | double :: Int -> Int 5 | double x = x * 2 6 | 7 | square :: Int -> Int 8 | square x = x * x 9 | 10 | -- Pattern matching 11 | -- Една и съща функция, която обаче чака точно специфичен аргумент 12 | -- Не сме задължен да слагаме типова декларация на функцията 13 | factorial 0 = 1 14 | factorial n = n * factorial (n - 1) 15 | 16 | fib 1 = 1 17 | fib 2 = 1 18 | fib n = fib (n - 1) + fib (n - 2) 19 | 20 | -- Guards за if-ове 21 | abs' x 22 | | x < 0 = (-x) 23 | | otherwise = x 24 | 25 | sumDigits :: Int -> Int 26 | sumDigits 0 = 0 27 | sumDigits n = (mod n 10) + sumDigits (div n 10) 28 | 29 | -- Имплементация с guards 30 | sumDigits' :: Int -> Int 31 | sumDigits' n 32 | | n == 0 = 0 33 | | otherwise = (mod n 10) + sumDigits (div n 10) 34 | 35 | pyth :: Int -> Int -> Int -> Bool 36 | pyth a b c = a^2 + b^2 == c^2 37 | 38 | -- Проверява дали x е в интервала [a, b] 39 | between :: Int -> Int -> Int -> Bool 40 | between x a b = (x >= a) && (x <= b) 41 | 42 | -- where за локални дефиниции 43 | -- Правим опашкова рекурсия с локална функция 44 | isPrime :: Int -> Bool 45 | isPrime n 46 | | n <= 1 = False 47 | | otherwise = isPrime' 2 n 48 | where 49 | isPrime' current n 50 | | current == n = True 51 | | mod n current == 0 = False 52 | | otherwise = isPrime' (current + 1) n 53 | 54 | area :: Double -> Double -> Double -> Double 55 | area a b c = sqrt (p * pa * pb * pc) 56 | where 57 | p = (a + b + c) / 2 58 | pa = p - a 59 | pb = p - b 60 | pc = p - c 61 | 62 | foodForPandas :: Int -> Int -> Int 63 | foodForPandas timeSpan pandaCount = totalFood 64 | where 65 | bambooPerDayPerPanda = 10 66 | ricePerDayPerPanda = 2 67 | bambooCount = bambooPerDayPerPanda * pandaCount * timeSpan 68 | riceCount = ricePerDayPerPanda * pandaCount * timeSpan 69 | totalFood = bambooCount + riceCount 70 | -------------------------------------------------------------------------------- /week10/homework/README.md: -------------------------------------------------------------------------------- 1 | Homework 10 2 | ========= 3 | 4 | ## Задача 1 - Truncatable primes 5 | 6 | Да се напише предикат `truncatablePrime`, който връща "истина" точно когато едно число х притежава едновременно следните свойства: 7 | 8 | * числото х е просто 9 | * всички числа, които се получават чрез премахване на цифри в края на х също са прости 10 | Пример за такова число е `3797`, тъй като `3797` е просто и числата, които се получават чрез последователно премахване на цифри в края му (`379`, `37` и `3`), също са прости. 11 | 12 | ### Сигнатура: 13 | 14 | ```haskell 15 | truncatablePrime :: Int -> Bool 16 | ``` 17 | 18 | ### Примери: 19 | 20 | ```haskell 21 | > truncatablePrime 3797 22 | True 23 | > truncatablePrime 47 24 | False 25 | ``` 26 | 27 | ## Задача 2 - Contains Digits? 28 | 29 | Да се напише предикат със следната сигнатура: 30 | 31 | ```haskell 32 | containsDigits :: Int -> Int -> Bool 33 | ``` 34 | 35 | Която проверява дали всяка цифра на вторият аргумент се среща като цифра на първия аргумент. 36 | 37 | ### Примери: 38 | 39 | ```haskell 40 | > containsDigits 111111 222 41 | False 42 | > containsDigits 1111112 222 43 | True 44 | > containsDigits 12345 123 45 | True 46 | > containsDigits 1230 11111111111111111111111130 47 | True 48 | ``` 49 | 50 | ## Задача 3 - Product of Digits 51 | 52 | Да се напише функция, която намира произведението на всички цифри на дадено число: 53 | 54 | ```haskell 55 | productOfDigits :: Int -> Int 56 | ``` 57 | 58 | ### Примери 59 | 60 | ```haskell 61 | > productOfDigits 123 62 | 6 63 | > productOfDigits 12345 64 | 120 65 | > productOfDigits 12355430 66 | 0 67 | ``` 68 | 69 | ## Задача 4 - Interesting Number 70 | 71 | С `d(n)` ще означаваме сумата на всички делители на числото `n` без самото `n`. Ще наричаме числото `n` интересно, ако за `k = d(n)` е изпълнено `d(k) = n`. Пример за такова число е `220`, тъй като негови делители са числата `1, 2, 4, 5, 10, 11, 20, 22, 44, 55` и `110` и `d(220) = 284`. От друга страна делителите на `284` са `1, 2, 4, 71` и `142` и `d(284) = 220`. 72 | 73 | Обратно, `284` също е интересно число, тъй като `d(284) = 220` и `d(220) = 284`). 74 | 75 | Да се напише предикат `interestingNumber`, който проверява дали едно число е интересно: 76 | 77 | ```haskell 78 | interestingNumber :: Int -> Bool 79 | ``` 80 | 81 | Не се интересуваме от числа, по-малки или равни на 1. 82 | 83 | ## Задача 5 - Квадрант 84 | 85 | Да се напише функция `quadrant`, която приема 2 аргумента - координати на точка и връща номера на квадранта (спрямо стандартна координатна система), в който тази точка лежи. 86 | 87 | Връзкате между координатите на точката и квадрантите на координатната система може да си припомните от следната картинка: 88 | 89 | ![Quadrant](http://ctle.hccs.edu/gcpass/PREPMath/lessonimages/rcsgif3.GIF "Quadrant") 90 | 91 | Ако функцията се извика с координатите на началото на координатната система (0, 0), то да се върне 0 като резултат. 92 | 93 | Функцията трябва да връща номера на квадранта като цяло число (т.е. резултатът може да е 0, 1, 2, 3 или 4) 94 | 95 | ### Сигнатура 96 | 97 | ```haskell 98 | quadrant :: Double -> Double -> Int 99 | ``` 100 | 101 | ### Примери 102 | 103 | ```haskell 104 | > quadrant 1 1.7 105 | 1 106 | > quadrant -5 3 107 | 2 108 | > quadrant -3 -5 109 | 3 110 | > quadrant 3 -5 111 | 4 112 | > quadrant 0 0 113 | 0 114 | ``` 115 | -------------------------------------------------------------------------------- /week11/README.md: -------------------------------------------------------------------------------- 1 | # Второ упражнение по Haskell 2 | 3 | Нещата за които си говорихме: 4 | 5 | * Кошници, Типове и Типови класове 6 | * Списъци, функции за списъци и работа със списъци в Haskell 7 | 8 | ## Кошници, Типове и Типови класове 9 | 10 | Тази тема в книгата - http://learnyouahaskell.com/types-and-typeclasses 11 | 12 | Haskell е статично типизиран език и всяка функция си има своята сигнатура. 13 | 14 | Нека да имаме следната функция: 15 | 16 | ```haskell 17 | add :: Int -> Int -> Int 18 | add a b = a + b 19 | ``` 20 | 21 | Ако извикаме тази функция с две цели числа, тя ще работи: 22 | 23 | ```haskell 24 | > add 1 2 25 | 3 26 | ``` 27 | 28 | Обаче ако извикаме функцията с `Double` числа, ще получим грешка: 29 | 30 | ```haskell 31 | > add 1.0 2.0 32 | :4:5: 33 | No instance for (Fractional Int) arising from the literal `1.0' 34 | Possible fix: add an instance declaration for (Fractional Int) 35 | In the first argument of `add', namely `1.0' 36 | In the expression: add 1.0 2.0 37 | In an equation for `it': it = add 1.0 2.0 38 | ``` 39 | 40 | Какъв е проблемът? Би трябвало да може да съберем както две цели числа, така и две числа с плаваща запетая - не бива това да е проблем. 41 | 42 | Проблемът идва от сигнатурата, която ние сами написахме: `add :: Int -> Int -> Int` 43 | 44 | Чрез нея се ограничаваме сано с `Int` типове. 45 | 46 | Ако махнем сигнатурата и напишем фунцкията само така: 47 | 48 | ```haskell 49 | add a b = a + b 50 | ``` 51 | 52 | И тестваме в __ghci__: 53 | 54 | ```haskell 55 | > add 1 2 56 | 3 57 | > add 1.0 2.0 58 | 3.0 59 | ``` 60 | 61 | Функцията работи! 62 | 63 | Haskell се е сетил, че тази функция може да има по-обща сигнатура, така че да нямаме проблем с `Int` и `Double`. 64 | 65 | Но как може да разберем тази сигнатура? За щастие, в ghci, има командата `:t`, която ако дадем име на функция, ще върне сигнатурата, която: 66 | 67 | * Ние сме написали 68 | * Ако не сме написали сигнатура, ще ни даде сигнатурата, която Haskell сам се е сетил! 69 | 70 | Нека да видим нашият `add` как изглежда: 71 | 72 | ```haskell 73 | > :t add 74 | add :: Num a => a -> a -> a 75 | ``` 76 | 77 | Уау! Нека да видим тази сигнатура отблизо: 78 | 79 | ```haskell 80 | add :: Num a => a -> a -> a 81 | ``` 82 | 83 | В нея има две нови неща: 84 | 85 | * `Num a =>` 86 | * Самото `a` 87 | 88 | ### Type Variables или функции с a -> a в сигнатурата 89 | 90 | В книгата тук - http://learnyouahaskell.com/types-and-typeclasses#type-variables 91 | 92 | Когато функцията ни може да работи с всякакъв тип, без да се интересуваме от неговата контретика, може да напишем `a` като име на този тип. 93 | 94 | Например, функцията, която намира дължина на списък има следната сигнатура: 95 | 96 | ```haskell 97 | length :: [a] -> Int 98 | ``` 99 | 100 | Функцията се чете така: __"Length е функция, която приема списък от нещо и връща цяло число".__ 101 | 102 | Това ни позволява да намерим дължина на списък, без да се интересуваме от какви елементи е съставен този списък! 103 | 104 | ### Типови класове и Num 105 | 106 | В книгата тук - http://learnyouahaskell.com/types-and-typeclasses#typeclasses-101 107 | 108 | Ако обаче имаме `Num a =>` в сигнатурата, това означава, че нашият тип `a`, какъвто и да е той, трябва да приднадлежи на типовия клас `Num`. 109 | 110 | Тук казваме, че типът трябва да е в кошницата на `Num`. 111 | 112 | `Num` е кошница, в която се намират типове като `Int` и `Double` и тази кошница, действа като интерфейс. 113 | 114 | Типовете, които се намират в нея, трябва да имплементират функцията `(+)` между две инстанции от дадения тип. 115 | 116 | Самият типов клас `Num`, изглежда така в кода на Haskell: http://hackage.haskell.org/package/base-4.7.0.1/docs/src/GHC-Num.html#Num 117 | 118 | ### Типови класове за сравняване и нареждане 119 | 120 | Ако напишем функцията, която проверява дали елемент се съдържа в списък, тя има следната сигнатура: 121 | 122 | ```haskell 123 | member :: Eq a => a -> [a] -> Bool 124 | ``` 125 | 126 | Я четем по следния начин: 127 | 128 | __member е функция, която взима нещо, списък от това нещо и връща True, ако нещото се съдържа в списъка. Нещото, трябва да приднадлежи на типовия клас `Eq`__ 129 | 130 | Типовият клас `Eq` дефинира две функции: 131 | 132 | * `(==) :: a -> a -> Bool` - проверява дали 2 стойности са равни 133 | * `(/=) :: a -> a -> Bool` - обратната проверка, различно-равно 134 | 135 | И по този начин, ние казваме, че нашата функция работи с всички типове, които знаят как да правят `==` ! 136 | 137 | ## Списъци 138 | 139 | В книгата тук - http://learnyouahaskell.com/starting-out#an-intro-to-lists 140 | 141 | ### Функции за списъци 142 | 143 | Списъците в Haskell са много сладки: 144 | 145 | * Може да създадем списък директно с литерал - `[1, 2, 3, 4]` 146 | * Може да създадем списък с range оператора в Haskell: `[1 .. 10]` - това ще създаде списъка от 1 до 10 147 | * Може да създадем списък с конструктура за списъци - `1 : []` - това ще създаде `[1]`. `1:2:3:4:[]` - ще създаде `[1,2,3,4]` 148 | * Главата на списъка може да вземем с функцията `head`, а опашката с `tail` 149 | * Списък се проверява дали е празен с функцията `null` 150 | 151 | ### Pattern matching за списъци 152 | 153 | В Haskell, имаме много силен Pattern Matching за списъци, който прави животът ни много по-лесен! 154 | 155 | Например, нека имаме следния код, написан в ghci: 156 | 157 | ```haskell 158 | > let (x:xs) = [1,2,3,4] 159 | > x 160 | 1 161 | > xs 162 | [2, 3, 4] 163 | ``` 164 | 165 | Може да разбием списък на две части - главата - `x` и опашката - `xs`. По този начин е много удобно да правим рекурсия със списъци, без да се налага да ползваме `head` и `tail` 166 | 167 | Нека да си напишем функция, която проверява дали списък е празен: 168 | 169 | ```haskell 170 | null' :: [a] -> Bool 171 | null' [] = True 172 | null' _ = False 173 | ``` 174 | 175 | На първия ред, pattern match-ваме по празния списък. Тоест - тази дефиниция на функцията ще се изпълни, когато списъкът, подаден като аргумент е празен. 176 | 177 | Това ни дава много сила, защото по този начин покрива дъното на много рекурсии, които боравят със списъци. 178 | 179 | Двете неща се виждат много добре, в примерната функция, която намира сумата на всички елементи в списък: 180 | 181 | ```haskell 182 | sum' :: Num a => [a] -> a 183 | sum' [] = 0 184 | sum' (x:xs) = x + sum xs 185 | ``` 186 | 187 | -------------------------------------------------------------------------------- /week11/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 11 2 | 3 | ## Задача 1 4 | 5 | Напишете функция `listToNumber`, която приема един аргумент - списък от цели числа и връща числото, което се получава, когато разгледаме всеки един елемент от списъцка като цифра. 6 | 7 | Например, ако имаме списъкът `[1, 2, 3, 4, 5]`, `listToNumber` трябва да върне числото `12345` 8 | 9 | **Сигнатура:** 10 | 11 | ```Haskell 12 | listToNumber :: [Int] -> Int 13 | ``` 14 | 15 | **Примери:** 16 | 17 | ```Haskell 18 | listToNumber [1, 2, 3] -- 123 19 | listToNumber [1, 0, 2, 0, 3, 0] -- 102030 20 | listToNumber [0, 0, 0, 0, 0] -- 0 21 | listToNumber [0, 0, 0, 1, 2, 3] -- 123 22 | listToNumber [0, 8, 4, 3, 2] -- 8432 23 | ``` 24 | 25 | ## Задача 2 26 | Напишете функция ``suffix``, която проверява дали списъкът, подаден като първи аргумент е суфикс на списъкът, подаден като втори. 27 | 28 | **Сигнатура:** 29 | 30 | ```haskell 31 | suffix :: (Eq a) => [a] -> [a] -> Bool 32 | ``` 33 | 34 | **Примери:** 35 | 36 | ```haskell 37 | suffix [1, 2, 3] [4, 5, 6] -- False 38 | suffix [1, 2, 3] [4, 5, 6, 1, 2] --False 39 | suffix [1, 2, 3] [4, 5, 6, 1, 2, 3] -- True 40 | suffix [ 6 ] [4, 5, 6] -- True 41 | suffix [1..10] [1..10] -- True 42 | suffix [1..10] [1..11] -- False 43 | ``` 44 | 45 | ## Задача 3 46 | Напишете функция `occurrences`, която взима два списъка от числа като аргумент - l1 и l2. 47 | Функцията връща списък, който се състои от броя срещания на всеки елемент от l1 в l2. 48 | 49 | 50 | **Сигнатура:** 51 | ```haskell 52 | occurrences :: [Int] -> [Int] -> [Int] 53 | ``` 54 | 55 | **Примери:** 56 | 57 | ```haskell 58 | occurrences [1, 2, 3] [1, 2, 4, 1] -- [2, 1, 0] 59 | occurrences [2, 2, 2] [0, 5, 6] -- [0, 0, 0] 60 | occurrences [2, 2, 2] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] -- [12, 12, 12] 61 | occurrences [] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] -- [] 62 | occurrences [2, 3, 4] [] -- [0, 0, 0] 63 | ``` 64 | 65 | ## Задача 4 66 | 67 | Напишете функция `removeAt` която по даден индекс и списък, премахва елемента на индекса и връща новия списък. 68 | 69 | __Броим от 0__. 70 | 71 | Ако индекса е твърде малък или твърде голям, нека функцията да връща грешка. 72 | 73 | Използвайте вградената функция ```error :: [Char] -> a```, за да хвърлите грешката. 74 | 75 | **Сигнатура:** 76 | 77 | ```Haskell 78 | removeAt :: Int -> [a] -> [a] 79 | ``` 80 | 81 | **Примери:** 82 | 83 | ```Haskell 84 | removeAt 0 [1,2,3] -- [2,3] 85 | removeAt 1 [1,2,3] -- [1,3] 86 | removeAt 3 [5,6,7,9] -- [5,6,7] 87 | removeAt (-1) [1,2,3] -- "Index out of bounds" 88 | removeAt 100 [1,2,3] -- "Index out of bounds" 89 | ``` 90 | -------------------------------------------------------------------------------- /week11/lab.hs: -------------------------------------------------------------------------------- 1 | -- Намираме сумата на всички елементи в списък 2 | -- Каква е сигнатурата? 3 | sum' :: Num a => [a] -> a 4 | sum' [] = 0 5 | sum' (x:xs) = x + sum xs 6 | 7 | -- Така бихме го написали на Scheme 8 | -- И е грозно! 9 | sum'' :: Num a => [a] -> a 10 | sum'' [] = 0 11 | sum'' items 12 | | null items = 0 13 | | otherwise = head items + sum'' (tail items) 14 | 15 | -- Правим списък от start към end 16 | -- Тук функцията succ е ключова 17 | -- Сигнатурата казва, че какъвто и тип a да дадем, той трябва да е в кошницата на 18 | -- Enum - да има функция succ 19 | -- Ord - да може да се сравнява 20 | range :: (Enum a, Ord a) => a -> a -> [a] 21 | range start end 22 | | start > end = [] 23 | | otherwise = start : range (succ start) end 24 | 25 | -- Дължина на списък 26 | length' :: [a] -> Int 27 | length' [] = 0 28 | length' (_:xs) = 1 + length' xs 29 | 30 | -- Последен елемент на списък 31 | -- Какво правим ако нямаме последен елемент? 32 | last' :: [a] -> a 33 | last' [] = error "Empty list" 34 | last' [x] = x 35 | last' (x:xs) = last' xs 36 | 37 | member :: Eq a => a -> [a] -> Bool 38 | member _ [] = False 39 | member needle (x:haystack) 40 | | needle == x = True 41 | | otherwise = member needle haystack 42 | 43 | -- функция, която добавя елемент накрая на списъка 44 | addLast :: a -> [a] -> [a] 45 | addLast y [] = [y] 46 | addLast y (x:xs) = x : addLast y xs 47 | 48 | -- Може да я направим и с оператора за конкатениране на списъци 49 | addLast' :: a -> [a] -> [a] 50 | addLast' item items = items ++ [item] 51 | 52 | -- Намиране на обратния списък чрез опашкова рекурсия 53 | reverse'' :: [a] -> [a] 54 | reverse'' items = iter items [] 55 | where 56 | iter [] result = result 57 | iter (x:xs) result = iter xs (x:result) 58 | 59 | -- Функцията map 60 | map' :: (a -> b) -> [a] -> [b] 61 | map' _ [] = [] 62 | map' f (x:xs) = f x : map' f xs 63 | 64 | -- Функцията filter 65 | filter' :: (a -> Bool) -> [a] -> [a] 66 | filter' _ [] = [] 67 | filter' pred (x:xs) 68 | | pred x == True = x : filter' pred xs 69 | | otherwise = filter' pred xs 70 | 71 | -- Филтрираме списъка с предиката 72 | -- Ако има поне 1 елемент в новия списък, то условието е изпълнено 73 | any' :: (a -> Bool) -> [a] -> Bool 74 | any' pred items = filteredLength >= 1 75 | where 76 | filteredLength = length $ filter pred items 77 | 78 | -- Обратната функция - гледаме дали след като филтрираме 79 | -- Получаваме същият списък като дължина 80 | all' :: (a -> Bool) -> [a] -> Bool 81 | all' pred items = filteredLength == actualLength 82 | where 83 | filteredLength = length $ filter pred items 84 | actualLength = length items 85 | -------------------------------------------------------------------------------- /week12/README.md: -------------------------------------------------------------------------------- 1 | # 3то упражнение по Haskell 2 | 3 | ## Материали 4 | 5 | Говорихме си за: 6 | 7 | * `map`, `filter` и `foldl` 8 | * Function Currying в Haskell. Тук може да прочетете повече - http://learnyouahaskell.com/higher-order-functions#curried-functions 9 | * Композиция на функции - `(.)` и идентитет на функции - `($)`. Прочете това - http://lambda.jstolarek.com/2012/03/function-composition-and-dollar-operator-in-haskell/ - кратко и добро обяснение за двете функции / оператора 10 | * Малко по-задълбочено - http://stackoverflow.com/questions/3030675/haskell-function-composition-and-function-application-idioms-correct-us 11 | 12 | От книгата е хубаво да прочетете следните глави, които ще засегнем и следващия път: 13 | 14 | * http://learnyouahaskell.com/syntax-in-functions 15 | * http://learnyouahaskell.com/recursion 16 | * http://learnyouahaskell.com/higher-order-functions 17 | 18 | ## Задачи 19 | 20 | ### Задача 1 - count 21 | 22 | Функцията `count`, със следната сигнатура: 23 | 24 | ```haskell 25 | count :: Eq a => a -> [a] -> Int 26 | ``` 27 | 28 | Трябва да върне броя на срещания на първия аргумент в списъка. 29 | 30 | ```haskell 31 | count 1 [1, 2, 3] == 1 32 | count 1 [] == 0 33 | ``` 34 | 35 | ### Задача 2 - matchLengths 36 | 37 | Напишете предикат `matchLengths`, който приема два непразни списъка `l1` и `l2` с еднаква дължина. 38 | 39 | Нека `l1 = (a1 a2 a3 ... an)` и `l2 = (b1 b2 b3 ... bn)`. Предикатът връща истина, ако разликата между дължините на всяка двойка списъци `(ai, bi)` е еднаква. 40 | 41 | Функцията има следната сигнатура: 42 | 43 | ```haskell 44 | matchLengths :: [[Int]] -> [[Int]] -> Bool 45 | ``` 46 | 47 | **Примери:** 48 | 49 | ```haskell 50 | matchLengths [ [], [1, 2], [3, 4, 5] ] [ [1], [2,3,4], [5,6,7,8] ] -- True 51 | matchLengths [ [], [1,2], [3,4,5] ] [ [], [2,3,4], [5,6,7] ] -- False 52 | matchLengths [ [1..9], [10..19] ] [ [1,2,3], [1,2,3,4] ] -- True 53 | ``` 54 | 55 | ### Задача 3 - trim 56 | 57 | Когато четем потребителски вход, трябва да "окастрим" получения низ. Защото потребителите използват шпациите на места, на които не очакваме. 58 | 59 | Напишете следната фунцкия: 60 | 61 | 62 | ```haskell 63 | trim :: String -> String 64 | ``` 65 | 66 | Функцията трябва да премахне **всяко срещане на whitespace от началото и края на низа**. Махат се излишните whitespaces, докато не се стигне до нормална буква. 67 | 68 | **Две неща:** 69 | 70 | * Може да използвате `isSpace :: Char -> Bool`, но ще трябва да напишете следния код на първия ред - `import Data.Char` 71 | * `String` типът е просто списък от Char-ове - `[Char]`. 72 | 73 | 74 | ### Задача 4 - Run-length encoding 75 | 76 | [Run-Length encoding](http://en.wikipedia.org/wiki/Run-length_encoding) е прост начин за компресия на текст, при който последователните срещания на един елемент се заменят с `<брой на срещанията><елемента>`, а в случай че тази замяна би заела повече символи, отколкото оригиналния текст (например, ако имаме само едно срещане на буквата `"а"` и го заменим с `"1а"`, то променения текст би заемал повече памет от изходния) се запазва оригиналния текст. Да се напише функция `lengthEncode str`, която компресира низ по зададения метод. 77 | 78 | 79 | **Сигнатура:** 80 | 81 | ```haskell 82 | lengthEncode :: String -> String 83 | ``` 84 | 85 | **Примери:** 86 | 87 | ```haskell 88 | lengthEncode "aaabccdefff" -> "3ab2cde3f" 89 | lengthEncode "abcdef" -> "abcdef" 90 | lengthEncode "aaaaaaaaaaaabbb" -> "12a3b" 91 | ``` 92 | 93 | Може да се счита, че входът ще бъде коректен и ще съдържа само букви 94 | 95 | **Hint:** 96 | 97 | Може да използвате функцията [show](http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html#v:show) за конвертиране на числа в низове 98 | 99 | ### Задача 5 - Run-length decoding 100 | 101 | За къде сме без обратната функция? 102 | 103 | Напишете я: 104 | 105 | ```haskell 106 | lengthDecode :: String -> String 107 | ``` 108 | 109 | **Примери:** 110 | 111 | ```haskell 112 | lengthDecode "3ab2cde3f" -> "aaabccdefff" 113 | lengthDecode "abcdef" -> "abcdef" 114 | lengthDecode "12a3b" -> "aaaaaaaaaaaabbb" 115 | ``` 116 | 117 | ### Задача 6 - Quicksort 118 | 119 | Имплементирайте Quicksort: 120 | 121 | ```haskell 122 | quickSort :: Ord a => [a] -> [a] 123 | ``` 124 | 125 | Опитайте се да го направите изразително и по Haskell–ски. 126 | -------------------------------------------------------------------------------- /week12/lab.hs: -------------------------------------------------------------------------------- 1 | import Data.Char 2 | 3 | -- map, filter, foldl 4 | -- Curry functions 5 | -- Function composition - (.) 6 | -- Function identity - ($) 7 | -- Sections - (+1), (==2), (>8) 8 | -- twice 9 | -- trim 10 | -- quickSort 11 | 12 | map' :: (a -> b) -> [a] -> [b] 13 | map' _ [] = [] 14 | map' f (x:xs) = f x : map' f xs 15 | 16 | filter' :: (a -> Bool) -> [a] -> [a] 17 | filter' _ [] = [] 18 | filter' p (x:xs) 19 | | p x = x : filter' p xs 20 | | otherwise = filter' p xs 21 | 22 | foldl' :: (a -> b -> a) -> a -> [b] -> a 23 | foldl' _ z [] = z 24 | foldl' op z (x:xs) = foldl' op z' xs 25 | where 26 | z' = op z x 27 | 28 | -- Това е функция, като ($) в Haskell 29 | fId :: (a -> b) -> a -> b 30 | fId f x = f x 31 | 32 | -- Композиция на функции в Haskell, което е функцията (.) 33 | compose :: (b -> c) -> (a -> b) -> a -> c 34 | compose f g x = f $ g x 35 | 36 | -- Функцията вика f - функция на два аргумента, два пъти 37 | -- Обърнете внимание как сме изпуснали вторият аргумент ;) 38 | twice :: (a -> a) -> a -> a 39 | twice f = f . f 40 | 41 | trim :: String -> String 42 | trim = f . f 43 | where 44 | f = reverse. dropWhile isSpace 45 | 46 | -- имплементация на quickSort 47 | quickSort :: Ord a => [a] -> [a] 48 | quickSort [] = [] 49 | quickSort (x:xs) = quickSort lesser ++ [x] ++ quickSort greater 50 | where 51 | lesser = filter (< x) xs 52 | greater = filter (>= x) xs 53 | -------------------------------------------------------------------------------- /week13/README.md: -------------------------------------------------------------------------------- 1 | # Алгебрични структури от данни 2 | 3 | Говорихме си за различни начини, по които може да се дефинират нови типове в Haskell: 4 | 5 | * Чрез `typе` като типови синоними 6 | * Чрез `data`, като си разказахме какво са value constructors. 7 | 8 | 9 | ##Препоръчани материали 10 | 11 | * [Глава от Learn You a Haskell по тази тема](http://learnyouahaskell.com/making-our-own-types-and-typeclasses) 12 | * [Declaring Types and Classes - TU-Delft, Part 1](http://delftxdownloads.tudelft.nl/FP101x-FunctionalProgramming/Week5/FP101x-chapter9-part1-video.720.mp4) 13 | * [Declaring Types and Classes - TU-Delft, Part 2](http://delftxdownloads.tudelft.nl/FP101x-FunctionalProgramming/Week5/FP101x-chapter9-part2-video.720.mp4) 14 | * [Declaring Types and Classes - TU-Delft, Part 3](http://delftxdownloads.tudelft.nl/FP101x-FunctionalProgramming/Week5/FP101x-chapter9-part3-video.720.mp4) 15 | * [Functional Programming - 101, TU-Delft](https://github.com/fptudelft/FP101x-Content) 16 | 17 | 18 | ## Задачи 19 | 20 | ### Product чрез type 21 | 22 | Използвайки следната типова дефиниция: 23 | 24 | ```haskell 25 | type Product = (String, Double) 26 | ``` 27 | 28 | Напишете следните функции: 29 | 30 | * `getName :: Product -> String` 31 | * `getPrice :: Product -> Double` 32 | * `discount :: Double -> [Product] -> [Product]`, където първият аргумент е процент, между 0 и 100. Функцията трябва да върне нов списък с продукти, в който всички са намалени с дадения процент 33 | 34 | ### Shapes 35 | 36 | Използвайки следната дефиниция: 37 | 38 | ```haskell 39 | data Shape = Circle | Triangle | Rectangle 40 | ``` 41 | 42 | Добавете типът `Shape` в следните типови класове: 43 | 44 | * `Show` 45 | * `Eq` 46 | 47 | Да се напишат следните функции: 48 | 49 | * `isCircle :: Shape -> Bool` 50 | * `isTriangle :: Shape -> Bool` 51 | * `isRectangle :: Shape -> Bool` 52 | 53 | ### Vector2D 54 | 55 | Използвайки следната дефиниция: 56 | 57 | ```haskell 58 | data Vector2D = Vector2D Double Double 59 | ``` 60 | 61 | Добавете типът `Vector2D` в следните типови класове: 62 | 63 | * `Show` 64 | * `Eq` 65 | 66 | Да се напишат следните функции: 67 | 68 | * `addVectors :: Vector2D -> Vector2D -> Vector2D` 69 | * `vectorLength :: Vector2D -> Double` - дължина на вектор 70 | 71 | ### Библиотека 72 | 73 | Използвайки следните дефиници на типът книга: 74 | 75 | ```haskell 76 | data Book = Paper String Int | Online String Int Int 77 | type Library = [Book] 78 | ``` 79 | 80 | Където типът книга има 2 value construcrors: 81 | 82 | * `Paper` с два аргумента - името на книгата и броят страници 83 | * `Online` с три аргумента - името на книгата, цената и броят на страниците 84 | 85 | Така сме описали книга, която може да е хартиена или пък онлайн. 86 | 87 | В нашата библиотека, само онлайн книгите имата цена. 88 | 89 | **А библиотеката представлява списък от книги.** 90 | 91 | Да се напишат следните функции: 92 | 93 | * `getPages :: Book -> Int` - връща броя на страниците на дадена книга 94 | * `getTotalPages :: Library -> Int` - връща броя на страниците на всички книги в една библиотека 95 | * `getOnlineCount :: Library -> Int` - връща броя на книгите в дадена библиотека, които са online 96 | * `getPaperCount :: Library -> Int` - връща броя на книгите в дадена библиотека, които са paper 97 | * `getBooksLike :: String -> Library -> Library` - връща нов списък от книги, в които низа, подаден като първи аргумент се среща в тяхното заглавие. Един вид, правим търсене на книги по някакъв низ в блиотека. **Търсенето трябва да е case insensitive - т.е главните и малките букви са еднакви**. За това може да ползвате `toLower :: Char -> Char` от `Data.Char` 98 | 99 | -------------------------------------------------------------------------------- /week13/lab.hs: -------------------------------------------------------------------------------- 1 | import Data.List (isInfixOf) 2 | import Data.Char (toLower) 3 | 4 | -- Създаванме типове синоним 5 | type Product = (String, Double) 6 | 7 | getName :: Product -> String 8 | getName (name, _) = name 9 | 10 | getPrice :: Product -> Double 11 | getPrice (_, price) = price 12 | 13 | -- Използваме fromIntegral, за да може да разделим Int на Double 14 | discount :: Int -> [Product] -> [Product] 15 | discount percent products = map discountPrice products 16 | where 17 | discountPrice (name, price) = (name, price - (price * (fromIntegral percent / 100.0))) 18 | 19 | products :: [Product] 20 | products = [("Laptop", 1000.0)] 21 | 22 | data Shape = Circle | Triangle | Rectangle 23 | 24 | -- Добавяме наши тип в типовия клас Show 25 | -- Това е същото като .toString() методите в повечето други езици 26 | -- Важното е да запомни,, че правим Pattern Matching по Value Constructors на типа! 27 | instance Show Shape where 28 | show Circle = "Circle" 29 | show Triangle = "Triangle" 30 | show Rectangle = "Rectangle" 31 | 32 | -- Добавяме нашия нов тип в класа Eq, за да може да сравняваме между различните форми 33 | instance Eq Shape where 34 | (==) Circle Circle = True 35 | (==) Triangle Triangle = True 36 | (==) Rectangle Rectangle = True 37 | (==) _ _ = False -- това е известно като "catch-all pattern", за да не описване всички други False случаи 38 | 39 | 40 | -- Сега тази функция ще може да работи и за нашият нов Shape тип, тъй като го добавихме в Eq класа! 41 | -- Пробвайте search Circle [Circle, Rectangle, Triangle] 42 | search :: (Eq a) => a -> [a] -> Bool 43 | search _ [] = False 44 | search needle (x:xs) 45 | | x == needle = True 46 | | otherwise = search needle xs 47 | 48 | -- Задачата с векторите 49 | -- Тук имаме value constructor, който е със същото име като името на типа 50 | data Vector2D = Vector2D Double Double 51 | 52 | instance Show Vector2D where 53 | show (Vector2D x y) = "Vector2D::" ++ show (x,y) 54 | 55 | instance Eq Vector2D where 56 | (==) (Vector2D x1 y1) (Vector2D x2 y2) = x1 == x2 && y1 == y2 57 | 58 | addVectors :: Vector2D -> Vector2D -> Vector2D 59 | addVectors (Vector2D x1 y1) (Vector2D x2 y2) = Vector2D (x1 + y1) (x2 + y2) 60 | 61 | vectorLength :: Vector2D -> Double 62 | vectorLength (Vector2D x y) = x^2 + y^2 63 | 64 | data Book = Paper String Int | Online String Int Int 65 | type Library = [Book] 66 | 67 | instance Show Book where 68 | show (Paper title pages) = "Paper book::" ++ title ++ " with pages: " ++ show pages 69 | show (Online title price pages) = "Online book::" ++ title ++ " with pages: " ++ show pages ++ " and cost: " ++ show price 70 | 71 | getPages :: Book -> Int 72 | getPages (Paper _ pages) = pages 73 | getPages (Online _ _ pages) = pages 74 | 75 | getTitle :: Book -> String 76 | getTitle (Paper title _) = title 77 | getTitle (Online title _ _) = title 78 | 79 | getTotalPages :: Library -> Int 80 | getTotalPages library = sum $ map getPages library 81 | 82 | isPaper :: Book -> Bool 83 | isPaper (Paper _ _) = True 84 | isPaper _ = False 85 | 86 | isOnline :: Book -> Bool 87 | isOnline (Online _ _ _) = True 88 | isOnline _ = False 89 | 90 | getOnlineCount :: Library -> Int 91 | getOnlineCount library = length $ filter isOnline library 92 | 93 | getPaperCount :: Library -> Int 94 | getPaperCount library = length $ filter isPaper library 95 | 96 | -- Използваме вградената функция isInfixOf 97 | -- http://hackage.haskell.org/package/base-4.7.0.2/docs/Data-List.html#v%3aisInfixOf 98 | isBookLike :: String -> Book -> Bool 99 | isBookLike subtitle book = isInfixOf lowerNeedle lowerHaystack 100 | where 101 | lowerNeedle = map toLower subtitle 102 | lowerHaystack = map toLower (getTitle book) 103 | 104 | getBooksLike :: String -> Library -> Library 105 | getBooksLike searchTerm library = filter (isBookLike searchTerm) library 106 | 107 | library :: Library 108 | library = [Paper "Harry Potter and the Half Blood Prince" 100, Paper "Pragmatic Thinking and Learning" 100, 109 | Online "The Healthy Programmer" 30 100, Paper "Hogfather" 100] 110 | 111 | 112 | indexOf :: Eq a => a -> [a] -> Maybe Int 113 | indexOf needle xs = helper needle (index xs) 114 | where 115 | index xs = zip [0 .. ((length xs) - 1)] xs 116 | helper _ [] = Nothing 117 | helper needle ((index, x):xs) 118 | | needle == x = Just index 119 | | otherwise = helper needle xs 120 | -------------------------------------------------------------------------------- /week14/README.md: -------------------------------------------------------------------------------- 1 | # Рекурсивни Типове 2 | 3 | В Haskell, дефинираме рекурсивен тип по следния начин: 4 | 5 | ```haskell 6 | data Tree a = Empty | Node a (Tree a) (Tree a) 7 | ``` 8 | 9 | Това е примерна дефиниция за двоично дърво, което има два value constructors: 10 | 11 | * Empty - с което ще бележим празно дърво (или край). 12 | * Node - с което ще бележим даден възел. Node-a има стойност `a` и два наследника, които са от тип дърво. 13 | 14 | Тоест, едно примерно дърво: 15 | 16 | ![](https://www.ocf.berkeley.edu/~shidi/cs61a/w/images/d/df/Binary_tree.png) 17 | 18 | Би изглеждало така: 19 | 20 | ```haskell 21 | data Tree a = Empty | Node a (Tree a) (Tree a) 22 | 23 | exampleTree :: Tree String 24 | exampleTree = Node "F" 25 | (Node "B" 26 | (Node "A" Empty Empty) 27 | (Node "D" 28 | (Node "C" Empty Empty) 29 | (Node "E" Empty Empty))) 30 | (Node "G" 31 | Empty 32 | (Node "I" 33 | (Node "H" Empty Empty) Empty)) 34 | ``` 35 | 36 | ## Задачи върху дървета 37 | 38 | Използвайки горните дефиниции, решете съответните задачи: 39 | 40 | * `treeElementsCount :: Tree a -> Int` - връща броя на възлите в едно дърво 41 | * `treeHeight :: Tree a -> Int` - връща височината на едно дърво 42 | * `flattenTree :: Tree a -> [a]` - връща дървото, изгладено до списък. Нека обхождането да бъде ляво-корен-дясно 43 | * `treeFind :: Eq a => a -> Tree a -> Bool` - проверява дали елемент се среща в дървото. Това дърво не е двоично дърво за търсене! 44 | * `bstInsert :: Ord a => a -> Tree a -> Tree a` - вмъква елемент в двоично дърво за търсене 45 | * `bstFind :: Ord a => a -> Tree a -> Bool` - проверява дали елемент се съдържа в двоично дъво за търсене 46 | * `treeLevel :: Int -> Tree a -> [a]` - връща списък от всички елементи на търсеното ниво в дървото. Нивата започват от 1. 47 | * `treeLevels :: Tree a -> [[a]]` - връща списък от списъци от всички елементи на всички нива на дървото, започвайки от първо ниво. 48 | * `isEmpty :: Tree a -> Bool` - предикат, който проверява дали дадено дърво е празно 49 | * `isLeaf :: Tree a -> Bool` - предикат, който проверява дали дадено дърво е листо (няма наследници) 50 | * `countLeaves :: Tree a -> Int` - връща броя на всички листа в нашето дърво 51 | * `successorsCount :: Tree a -> Int` - връща броя на преките наследниците на едно дърво (Може да 0, 1 или 2) 52 | * `treeMap :: (a -> b) -> Tree a -> Tree b` - това е `map` функцията в/у дърво - трансформира всеки елемент и прави ново дърво. 53 | 54 | Ето примерно вкарване на дървото в `Show`, ако имате функцията `flattenTree`: 55 | 56 | ```haskell 57 | data Tree a = Empty | Node a (Tree a) (Tree a) 58 | 59 | exampleTree :: Tree Int 60 | exampleTree = (Node 5 (Node 4 (Node 10 Empty Empty) Empty) (Node 3 (Node 11 Empty Empty) Empty)) 61 | 62 | instance Show a => Show (Tree a) where 63 | show tree = show $ flattenTree tree 64 | ``` 65 | 66 | -------------------------------------------------------------------------------- /week14/exam-preparation/README.md: -------------------------------------------------------------------------------- 1 | # Подготовка за второ конторлно 2 | 3 | ## Задача 1 4 | 5 | Напишете функцията `mergeAndSortDigits :: Int -> Int -> Int`, която взима две цели числа, `X` и `Y` и връща ново число `N`, такова че: 6 | 7 | * То се състои от цифрите на `X` и `Y`, без повторения в цифрите (само уникални цифри) 8 | * Цифрите на `N` трябва да са сортирани във възходящ или низходящ ред, спрямо следното условие: 9 | * Ако `sumDigits X <= sumDigits Y`, то трябва да са във възходящ ред 10 | * Otherwise - във низходящ ред. 11 | * Ако в някое от числата `X` или `Y` се среща нула, тя се премахва и изобщо не участва в схемата! 12 | 13 | Примери: 14 | 15 | ```haskell 16 | > mergeAndSortDigits 11 111 17 | 1 -- Защото искаме резултатното число да няма повтарящи се цифри. 18 | > mergeAndSortDigits 123 456 19 | 123456 -- сумата на 1+2+3 е <= от сумата на 4+5+6 20 | > mergeAndSortDigits 456 123 21 | 654321 -- тук имаме обратния вариант 22 | ``` 23 | 24 | ## Задача 2 25 | 26 | Напишете функция `balance :: Int -> [Int] -> Int`, която взима число `N`, списък с числа `numbers` и връща **минималния брой** на премахванията на елементи от списъка, такъв че сумата на числата в списъка да стане `<= N` или пък списъкът не остане празен. 27 | 28 | `N` няма да бъде отрицателно число. 29 | 30 | Примери: 31 | 32 | ```haskell 33 | balance 50 [45, 5, 100] 34 | 1 -- Трябва да махнем 100, за да получим сума 50 35 | balance 3 [2, 10, 15] 36 | 2 -- Тук трябва да махнем 10 и 15 37 | balance 1 [5, 10, 15, 36] 38 | 4 -- Тук трябва да махнем всички елементи от списъка 39 | ``` 40 | 41 | ## Задача 3 42 | 43 | Напишете функция `repeater :: String -> (Int -> String -> String)`, която взима низ `str` и връща функция на два аргумента. 44 | 45 | Когато извикате върнатата функция с число `count` и низ `glue`, се получава низ, който представлява `count`- кратно повтаряне на низа `str`, при което между всеки две съседни повторения на `str` стои низът `glue`. 46 | 47 | Примери: 48 | 49 | ```haskell 50 | > let haskellRepeat = repeater "I love Haskell" 51 | > haskellRepeat 3 " " 52 | "I love Haskell I love Haskell I love Haskell" 53 | > repeater "Quack" 5 "!" 54 | "Quack!Quack!Quack!Quack!Quack" 55 | ``` 56 | 57 | ## Задача 4 58 | 59 | Да се дефинира тип `Order`, който моделира поръчка на продукт. 60 | 61 | Типът да има `2` `value` констуктора - `Online Float Int Int` (цена на стоката, номер на поръчката и брой часове до доставка) и `Offline Float` (цена на стоката) 62 | 63 | - да се дефинира функция `isOnline :: Order -> Bool`, която връща дали дадена поръчка е направена онлайн. 64 | - да се дефинира функция `timeUntilReceiving :: Order -> Int`, която връща след колко часа поръчката ще бъде получена 65 | - да се дефинира функция `totalPrice :: [Order] -> Float`, която връща общата цена на списък от поръчки 66 | - да се дефинира функция `onlineOrders :: [Order] -> Int`, която връща броят на онлайн поръчки в даден списък. 67 | - да се дефинира функция `isExpensive :: Order -> Bool`, която проверява дали цената на поръчаната стока надвишава 100. 68 | - да се направи `Order` инстанция на `Show` и `Eq`. 69 | 70 | ## Задачa 5 71 | 72 | Имаме следното представяне на двоично дърво: 73 | 74 | ```haskell 75 | data Tree a = Empty | Node a (Tree a) (Tree a) 76 | ``` 77 | 78 | Казваме, че едно двоично дърво е конус, ако сумата на елементите на всяко ниво в дървото е по-голяма от сумата на елементите на предишното ниво. 79 | 80 | a) Да се дефинира функция `levelSum :: Int -> Tree Int -> Int`, която намира сумата на елементите на ниво *level* в дървото *tree*. 81 | 82 | b) Да се дефинира функция `cone :: Tree Int -> Bool`, която проверява дали дървото *tree* е конус. 83 | 84 | Приемаме, че коренът на дървото е на ниво 1. Да се използва следната дефиниция на двоично дърво: 85 | 86 | **Примери:** 87 | 88 | ```haskell 89 | levelSum Empty 0 -> 0 90 | levelSum (Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)) 0 -> 1 91 | levelSum (Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)) 1 -> 5 92 | levelSum (Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)) 2 -> 0 93 | 94 | cone Empty -> True 95 | cone (Node 1 (Node 2 Empty Empty) (Node 3 Empty Empty)) -> True 96 | cone (Node 5 (Node 2 Empty Empty) (Node 3 Empty Empty)) -> False 97 | cone (Node 1 (Node 2 Empty Empty) (Node 3 Empty (Node 4 Empty Empty))) -> False 98 | ``` 99 | -------------------------------------------------------------------------------- /week14/exam-preparation/balance.hs: -------------------------------------------------------------------------------- 1 | import Data.List 2 | 3 | maxList :: Ord a => [a] -> a 4 | maxList = foldl1 max 5 | 6 | step :: Int -> [Int] -> Bool 7 | step n xs = sum xs <= n 8 | 9 | balance :: Int -> [Int] -> Int 10 | balance n xs = helper n sorted 11 | where 12 | sorted = reverse $ sort xs 13 | helper _ [] = 0 14 | helper limit (y:ys) 15 | | step n (y:ys) = 0 16 | | otherwise = 1 + helper n ys 17 | -------------------------------------------------------------------------------- /week14/exam-preparation/cone.hs: -------------------------------------------------------------------------------- 1 | data Tree a = Empty | Node a (Tree a) (Tree a) 2 | 3 | exampleTree :: Tree Int 4 | exampleTree = (Node 5 (Node 4 (Node 10 Empty Empty) Empty) (Node 3 (Node 11 Empty Empty) Empty)) 5 | 6 | -- inorder / preorder / postorder traversals 7 | flattenTree :: Tree a -> [a] 8 | flattenTree Empty = [] 9 | flattenTree (Node x left right) = (flattenTree left) ++ [x] ++ (flattenTree right) 10 | 11 | instance Show a => Show (Tree a) where 12 | show tree = show $ flattenTree tree 13 | 14 | countNodes :: Tree a -> Int 15 | countNodes Empty = 0 16 | countNodes (Node x left right) = 1 + (countNodes left) + (countNodes right) 17 | 18 | treeHeight :: Tree a -> Int 19 | treeHeight Empty = 0 20 | treeHeight (Node x left right) = 1 + max (treeHeight left) (treeHeight right) 21 | 22 | treeLevel :: Int -> Tree a -> [a] 23 | treeLevel level tree = helper 1 level tree 24 | where 25 | helper _ _ Empty = [] 26 | helper currentLevel desiredLevel (Node x left right) 27 | | currentLevel == desiredLevel = [x] 28 | | otherwise = (helper (currentLevel + 1) desiredLevel left) ++ (helper (currentLevel + 1) desiredLevel right) 29 | 30 | -- Връща списък от всички нива на дървото 31 | treeLevels :: Tree a -> [[a]] 32 | treeLevels tree = map (flip treeLevel $ tree) [1 .. (treeHeight tree)] 33 | 34 | -- без flip магията 35 | treeLevels' :: Tree a -> [[a]] 36 | treeLevels' tree = map (\level -> treeLevel level tree) [1 .. (treeHeight tree)] 37 | 38 | treeFind :: Eq a => a -> Tree a -> Bool 39 | treeFind _ Empty = False 40 | treeFind needle (Node x left right) 41 | | needle == x = True 42 | | otherwise = (treeFind needle left) || (treeFind needle right) 43 | 44 | bstInsert :: Ord a => a -> Tree a -> Tree a 45 | bstInsert element Empty = (Node element Empty Empty) 46 | bstInsert element (Node x left right) 47 | | element == x = (Node x left right) 48 | | element < x = (Node x (bstInsert element left) right) 49 | | otherwise = (Node x left (bstInsert element right)) 50 | 51 | bstFind :: Ord a => a -> Tree a -> Bool 52 | bstFind _ Empty = False 53 | bstFind element (Node x left right) 54 | | element == x = True 55 | | element < x = bstFind element left 56 | | otherwise = bstFind element right 57 | 58 | isEmpty :: Tree a -> Bool 59 | isEmpty Empty = True 60 | isEmpty _ = False 61 | 62 | isLeaf :: Tree a -> Bool 63 | isLeaf Empty = False 64 | isLeaf (Node _ left right) = isEmpty left && isEmpty right 65 | 66 | treeMap :: (a -> b) -> Tree a -> Tree b 67 | treeMap _ Empty = Empty 68 | treeMap f (Node x left right) = Node (f x) (treeMap f left) (treeMap f right) 69 | 70 | countLeaves :: Tree a -> Int 71 | countLeaves Empty = 0 72 | countLeaves (Node _ Empty Empty) = 1 73 | countLeaves (Node _ left right) = (countLeaves left) + (countLeaves right) 74 | 75 | -- 0, 1 or 2 76 | successorsCount :: Tree a -> Int 77 | successorsCount Empty = 0 78 | successorsCount (Node _ (Node _ _ _) Empty) = 1 79 | successorsCount (Node _ Empty (Node _ _ _)) = 1 80 | successorsCount (Node _ (Node _ _ _) (Node _ _ _)) = 2 81 | successorsCount _ = 0 82 | 83 | levelSum :: Int -> Tree Int -> Int 84 | levelSum level tree = sum $ treeLevel level tree 85 | 86 | isIncreasing [] = True 87 | isIncreasing [x] = True 88 | isIncreasing (x:y:xs) = x < y && isIncreasing (y:xs) 89 | 90 | cone :: Tree Int -> Bool 91 | cone Empty = False 92 | cone tree = isIncreasing $ map sum $ treeLevels tree 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /week14/exam-preparation/mergeandsortdigits.hs: -------------------------------------------------------------------------------- 1 | import Data.List 2 | 3 | sortAsc :: Ord a => [a] -> [a] 4 | sortAsc = sort 5 | 6 | sortDesc :: Ord a => [a] -> [a] 7 | sortDesc = reverse . sort 8 | 9 | intToList :: Int -> [Int] 10 | intToList n 11 | | n < 10 = [n] 12 | | otherwise = intToList (n `div` 10) ++ [n `mod` 10] 13 | 14 | listToInt :: [Int] -> Int 15 | listToInt xs = foldl glue 0 xs 16 | where 17 | glue a b = a*10 + b 18 | 19 | sumDigits :: Int -> Int 20 | sumDigits = sum . intToList 21 | 22 | unique :: Ord a => [a] -> [a] 23 | unique = (map head) . group . sort 24 | 25 | removeZeroes :: [Int] -> [Int] 26 | removeZeroes = filter (/=0) 27 | 28 | mergeAndSortDigits :: Int -> Int -> Int 29 | mergeAndSortDigits x y 30 | | sumDigits x <= sumDigits y = listToInt $ sortAsc merged 31 | | otherwise = listToInt $ sortDesc merged 32 | where 33 | merged = removeZeroes . unique $ (intToList x ++ intToList y) 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /week14/exam-preparation/order.hs: -------------------------------------------------------------------------------- 1 | data Order = Online Float Int Int | Offline Float 2 | type Orders = [Order] 3 | 4 | isOnline :: Order -> Bool 5 | isOnline (Online _ _ _) = True 6 | isOnline _ = False 7 | 8 | timeUntilReceiving :: Order -> Int 9 | timeUntilReceiving (Online _ _ deliveryTime) = deliveryTime 10 | timeUntilReceiving _ = error "No time for offline orders" 11 | 12 | getPrice :: Order -> Float 13 | getPrice (Online price _ _) = price 14 | getPrice (Offline price) = price 15 | 16 | totalPrice :: Orders -> Float 17 | totalPrice = sum . (map getPrice) 18 | 19 | onlineOrders :: Orders -> Int 20 | onlineOrders = length . (filter isOnline) 21 | 22 | isExpensive :: Order -> Bool 23 | isExpensive = (>= 100) . getPrice 24 | 25 | instance Show Order where 26 | show (Online price id deliveryTime) = "Online::" ++ show (price, id, deliveryTime) 27 | show (Offline price) = "Offline::" ++ show price 28 | 29 | instance Eq Order where 30 | (==) (Online _ id1 _) (Online _ id2 ) = id1 == id2 31 | (==) (Offline price1) (Offline price) = price == price 32 | -------------------------------------------------------------------------------- /week14/exam-preparation/repeater.hs: -------------------------------------------------------------------------------- 1 | import Data.List 2 | 3 | join = intercalate 4 | 5 | repeater :: String -> (Int -> String -> String) 6 | repeater what = helper 7 | where 8 | helper times glue = join glue $ replicate times what 9 | 10 | -------------------------------------------------------------------------------- /week14/example.hs: -------------------------------------------------------------------------------- 1 | data Tree a = Empty | Node a (Tree a) (Tree a) 2 | 3 | exampleTree :: Tree String 4 | exampleTree = Node "F" 5 | (Node "B" 6 | (Node "A" Empty Empty) 7 | (Node "D" 8 | (Node "C" Empty Empty) 9 | (Node "E" Empty Empty))) 10 | (Node "G" 11 | Empty 12 | (Node "I" 13 | (Node "H" Empty Empty) Empty)) 14 | -------------------------------------------------------------------------------- /week14/lab.hs: -------------------------------------------------------------------------------- 1 | maxFunction :: Ord a => [(a -> a)] -> a -> a 2 | maxFunction fs x = maximum $ map (\f -> f x) fs 3 | -------------------------------------------------------------------------------- /week14/trees.hs: -------------------------------------------------------------------------------- 1 | data Tree a = Empty | Node a (Tree a) (Tree a) 2 | 3 | exampleTree :: Tree Int 4 | exampleTree = (Node 5 (Node 4 (Node 10 Empty Empty) Empty) (Node 3 (Node 11 Empty Empty) Empty)) 5 | 6 | -- inorder / preorder / postorder traversals 7 | flattenTree :: Tree a -> [a] 8 | flattenTree Empty = [] 9 | flattenTree (Node x left right) = (flattenTree left) ++ [x] ++ (flattenTree right) 10 | 11 | instance Show a => Show (Tree a) where 12 | show tree = show $ flattenTree tree 13 | 14 | countNodes :: Tree a -> Int 15 | countNodes Empty = 0 16 | countNodes (Node x left right) = 1 + (countNodes left) + (countNodes right) 17 | 18 | treeHeight :: Tree a -> Int 19 | treeHeight Empty = 0 20 | treeHeight (Node x left right) = 1 + max (treeHeight left) (treeHeight right) 21 | 22 | treeLevel :: Int -> Tree a -> [a] 23 | treeLevel level tree = helper 1 level tree 24 | where 25 | helper _ _ Empty = [] 26 | helper currentLevel desiredLevel (Node x left right) 27 | | currentLevel == desiredLevel = [x] 28 | | otherwise = (helper (currentLevel + 1) desiredLevel left) ++ (helper (currentLevel + 1) desiredLevel right) 29 | 30 | -- Връща списък от всички нива на дървото 31 | treeLevels :: Tree a -> [[a]] 32 | treeLevels tree = map (flip treeLevel $ tree) [1 .. (treeHeight tree)] 33 | 34 | -- без flip магията 35 | treeLevels' :: Tree a -> [[a]] 36 | treeLevels' tree = map (\level -> treeLevel level tree) [1 .. (treeHeight tree)] 37 | 38 | treeFind :: Eq a => a -> Tree a -> Bool 39 | treeFind _ Empty = False 40 | treeFind needle (Node x left right) 41 | | needle == x = True 42 | | otherwise = (treeFind needle left) || (treeFind needle right) 43 | 44 | bstInsert :: Ord a => a -> Tree a -> Tree a 45 | bstInsert element Empty = (Node element Empty Empty) 46 | bstInsert element (Node x left right) 47 | | element == x = (Node x left right) 48 | | element < x = (Node x (bstInsert element left) right) 49 | | otherwise = (Node x left (bstInsert element right)) 50 | 51 | bstFind :: Ord a => a -> Tree a -> Bool 52 | bstFind _ Empty = False 53 | bstFind element (Node x left right) 54 | | element == x = True 55 | | element < x = bstFind element left 56 | | otherwise = bstFind element right 57 | 58 | isEmpty :: Tree a -> Bool 59 | isEmpty Empty = True 60 | isEmpty _ = False 61 | 62 | isLeaf :: Tree a -> Bool 63 | isLeaf Empty = False 64 | isLeaf (Node _ left right) = isEmpty left && isEmpty right 65 | 66 | treeMap :: (a -> b) -> Tree a -> Tree b 67 | treeMap _ Empty = Empty 68 | treeMap f (Node x left right) = Node (f x) (treeMap f left) (treeMap f right) 69 | 70 | countLeaves :: Tree a -> Int 71 | countLeaves Empty = 0 72 | countLeaves (Node _ Empty Empty) = 1 73 | countLeaves (Node _ left right) = (countLeaves left) + (countLeaves right) 74 | 75 | -- 0, 1 or 2 76 | successorsCount :: Tree a -> Int 77 | successorsCount Empty = 0 78 | successorsCount (Node _ (Node _ _ _) Empty) = 1 79 | successorsCount (Node _ Empty (Node _ _ _)) = 1 80 | successorsCount (Node _ (Node _ _ _) (Node _ _ _)) = 2 81 | successorsCount _ = 0 82 | -------------------------------------------------------------------------------- /week2/README.md: -------------------------------------------------------------------------------- 1 | # Week 2 - Tail Call Recursion 2 | 3 | * [Code from lab is here](lab.rkt) 4 | * [2nd homework is here](homework/) 5 | * [Tail Call Recursion as Iteration Method](http://blogs.msdn.com/b/ashleyf/archive/2010/02/06/recursion-is-the-new-iteration.aspx) 6 | * [What is Tail Recursion?](http://stackoverflow.com/questions/33923/what-is-tail-recursion) 7 | 8 | -------------------------------------------------------------------------------- /week2/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 2 2 | 3 | **Use tail call recursion whenever is applicable!** 4 | 5 | ## Series 6 | 7 | In a file called `series.rkt` implement the following functions: 8 | 9 | ### `(series a b n)` 10 | 11 | This function should return the nth member of the series that follow the pattern: 12 | 13 | ``` 14 | A1 = a 15 | A2 = b 16 | An = An-1 + An-2 17 | ``` 18 | 19 | For example, if `a = 1` and `b = 1`, we are going to get the Fibonacci series. If `a = 2` and `b = 1`, we are going to get the Lucas series. 20 | 21 | 22 | Example usage in xREPL: 23 | 24 | ```racket 25 | -> (series 1 1 10) 26 | 55 27 | -> (series 2 3 10) 28 | 144 29 | -> (series 2 1 10) 30 | 76 31 | ``` 32 | 33 | ### `(lucas n)` and `(fibonacci n)` 34 | 35 | Using the `series` function, implement the following functions: 36 | 37 | * `(lucas n)` which returns the nth member of the Lucas series. 38 | * `(fibonacci n)` which returns the nth member of the Fibonacci series. 39 | 40 | ### `(summed-member n)` 41 | 42 | Next, implement the `(summed-member n)` function which returns the sum of the nth member of Lucas and nth member of Fibonacci series. 43 | 44 | This means the following equation: 45 | 46 | ``` 47 | summed-member(n) = lucas(n) + fibonacci(n) 48 | ``` 49 | 50 | Examples: 51 | 52 | ```racket 53 | -> (summed-member 1) 54 | 3 55 | -> (summed-member 10) 56 | 131 57 | -> (summed-member 666) 58 | 16338755146995029054736715627911135966526116743682459603796713422641127612695004111733680782579831725266145312163877373516554992667286371099 59 | ``` 60 | 61 | ### Nth sums 62 | 63 | Next, implement the two functions: 64 | 65 | * `(nth-lucas-sum n)` 66 | * `(nth-fibonacci-sum n)` 67 | 68 | Both functions should return the sum of all members from the first to the nth one. 69 | 70 | For example, `(nth-lucas-sum 10)` means that we want to sum all Lucas numbers from the 1st to the 10th, inclusive. 71 | 72 | Examples: 73 | 74 | ```racket 75 | -> (nth-fibonacci-sum 10) 76 | 143 77 | -> (nth-lucas-sum 10) 78 | 198 79 | ``` 80 | 81 | ### The difference series 82 | 83 | And finally, implement a function called `(lucas-fib-diff n)` which returns the nth member of the special series, which is formed like this: 84 | 85 | ``` 86 | A1 = lucas(1) - fib(1) 87 | ... 88 | AN = lucas(n) - fib(n) 89 | ``` 90 | 91 | The nth member of the desired series is the difference between the nth member of the Lucas series adn the nth member of Fibonacci. 92 | 93 | Examples: 94 | 95 | ```racket 96 | -> (lucas-fib-diff 1) 97 | 1 98 | -> (lucas-fib-diff 10) 99 | 21 100 | ``` 101 | 102 | ## Number fencing 103 | 104 | In a file, called `fence.rkt` implement the following functions: 105 | 106 | ### `(string-repeat str n)` 107 | 108 | This function should take a string and concatenate it with itself `n` times. 109 | 110 | Examples: 111 | 112 | ```racket 113 | -> (string-repeat "-" 1) 114 | "-" 115 | -> (string-repeat "-" 2) 116 | "--" 117 | -> (string-repeat "racket" 3) 118 | "racketracketracket" 119 | ``` 120 | 121 | In order to achieve your goal, you will have to use `(string-append a b)` from the standard library: 122 | 123 | ```racket 124 | -> (string-append "ra" "cket") 125 | "racket" 126 | ``` 127 | 128 | ### `(fence n)` 129 | 130 | We are going to put number in fences. Fences represent a string that looks something like this: 131 | 132 | ``` 133 | {-->n<--} 134 | ``` 135 | 136 | All fences must start with `{` and end with `}`. Around the number `n` there must always be `>` and `<`. 137 | 138 | The only thing of variable length are the dashes. 139 | 140 | For a number `n`, we know that the left and right fence must have the following number of dashes: 141 | 142 | ```racket 143 | (round (+ 1 (log n))) 144 | ``` 145 | 146 | Build the fence and return it as a string. Here are some examples: 147 | 148 | ```racket 149 | -> (fence 1) 150 | "{->1<-}" 151 | -> (fence 2) 152 | "{-->2<--}" 153 | -> (fence 10) 154 | "{--->10<---}" 155 | -> (fence 100) 156 | "{------>100<------}" 157 | ``` 158 | 159 | ## Binary stuff 160 | 161 | In a file, called `binary.rkt`, implement the following functions: 162 | 163 | ### `(string-reverse str)` 164 | 165 | Implement the generic string reverse function. 166 | 167 | In order to achieve that, you will have to use few string functions from the standard library: 168 | 169 | ```racket 170 | -> (string-length "racket") 171 | 6 172 | -> (string-append "ra" "cket") 173 | "racket" 174 | -> (string-ref "racket" 0) 175 | #\r 176 | -> (string-ref "racket" 1) 177 | #\a 178 | -> (string-append "a" (string-ref "a" 0)) 179 | ; string-append: contract violation 180 | ; expected: string? 181 | ; given: #\a 182 | ; argument position: 2nd 183 | ; [,bt for context] 184 | -> (~a (string-ref "a" 0)) 185 | "a" 186 | -> (string-append "a" (~a (string-ref "a" 0))) 187 | "aa" 188 | ``` 189 | 190 | Examples for string reverse: 191 | 192 | ```racket 193 | -> (string-reverse "racket") 194 | "tekcar" 195 | -> (string-reverse "kapak") 196 | "kapak" 197 | ``` 198 | 199 | ### `(to-binary-string n)` 200 | 201 | Implement a function, that takes an integer `n` and returns the binary representation of `n` as a string. 202 | 203 | If you want to turn a number into string, you can use `(~a n)` 204 | 205 | Examples: 206 | 207 | ```racket 208 | -> (to-binary-string 1) 209 | "1" 210 | -> (to-binary-string 10) 211 | "1010" 212 | -> (to-binary-string 8) 213 | "1000" 214 | -> (to-binary-string 111) 215 | "1101111" 216 | ``` 217 | 218 | ### `(from-binary-string binary-str)` 219 | 220 | Implement a function, that takes a binary string representation and returns the integer number from it. 221 | 222 | If you want to turn a string into a number, you can use the function from the standard library: 223 | 224 | ```racket 225 | -> (string->number "111") 226 | 111 227 | ``` 228 | 229 | Here are examples: 230 | 231 | ```racket 232 | -> (from-binary-string "1101111") 233 | 111 234 | -> (from-binary-string "0") 235 | 0 236 | -> (from-binary-string "1") 237 | 1 238 | -> (from-binary-string "10") 239 | 2 240 | -> (from-binary-string "11") 241 | 3 242 | -> (from-binary-string "100") 243 | 4 244 | -> (from-binary-string "101010") 245 | 42 246 | ``` 247 | -------------------------------------------------------------------------------- /week2/homework/binary.rkt: -------------------------------------------------------------------------------- 1 | (define (string-reverse str) 2 | (define (iter n str result) 3 | (cond 4 | [(zero? n) result] 5 | [else (iter (- n 1) str (string-append result (~a (string-ref str (- n 1)))))])) 6 | (iter (string-length str) str "")) 7 | 8 | (define (to-binary-string n) 9 | (define (iter n result) 10 | (cond 11 | [(zero? n) (string-reverse result)] 12 | [else (iter (quotient n 2) (string-append result (~a (remainder n 2))))])) 13 | (iter n "")) 14 | 15 | (define (from-binary-string binary-str) 16 | (define (iter n index result) 17 | (cond 18 | [(zero? n) result] 19 | [else (iter (quotient n 10) (+ index 1 ) (+ result (* (remainder n 10) (expt 2 index))))])) 20 | (iter (string->number binary-str) 0 0)) 21 | -------------------------------------------------------------------------------- /week2/homework/fence.rkt: -------------------------------------------------------------------------------- 1 | (define (string-repeat str n) 2 | (define (iter str n result) 3 | (cond 4 | [(zero? n) result] 5 | [else (iter str (- n 1) (string-append result str))])) 6 | (iter str n "")) 7 | 8 | (define (dash-count n) 9 | (round (+ 1 (log n)))) 10 | 11 | (define (fence-dash n) 12 | (string-repeat "-" (dash-count n))) 13 | 14 | (define (fence n) 15 | (string-append "{" (fence-dash n) ">" (~a n) "<" (fence-dash n) "}")) 16 | -------------------------------------------------------------------------------- /week2/homework/series.rkt: -------------------------------------------------------------------------------- 1 | (define (series a b n) 2 | (cond 3 | [(= n 1) a] 4 | [(= n 2) b] 5 | [(zero? n) a] 6 | [else (series b (+ a b) (- n 1))])) 7 | 8 | (define (fibonacci n) 9 | (series 1 1 n)) 10 | 11 | (define (lucas n) 12 | (series 2 1 n)) 13 | 14 | (define (summed-member n) 15 | (+ (fibonacci n) (lucas n))) 16 | 17 | (define (nth-fibonacci-sum n) 18 | (define (iter n result) 19 | (cond 20 | [(zero? n) result] 21 | [else (iter (- n 1) (+ result (fibonacci n)))])) 22 | (iter n 0)) 23 | 24 | (define (nth-lucas-sum n) 25 | (define (iter n result) 26 | (cond 27 | [(zero? n) result] 28 | [else (iter (- n 1) (+ result (lucas n)))])) 29 | (iter n 0)) 30 | 31 | (define (lucas-fib-diff n) 32 | (define (iter nth-member current n) 33 | (cond 34 | [(= current n) nth-member] 35 | [else (iter (- (lucas (+ current 1)) (fibonacci (+ current 1))) (+ current 1) n)])) 36 | (iter 1 1 n)) 37 | -------------------------------------------------------------------------------- /week2/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ; Да се изчисли факториелът на даденото число n. 4 | ; Обикновеното рекурсивно решение заема много памет във 5 | ; функционалния стек, докато опашковата рекурсия 6 | ; ни я спестява - дори да не води до подобрения в скоростта. 7 | (define (fact n) 8 | ;(if (< n 2) 1 9 | ; (+ n (fact (- n 1)))) 10 | 11 | (define (fact-iter1 i result) 12 | (if (> i n) result 13 | (fact-iter1 (+ i 1) (* result i))) 14 | ) 15 | 16 | (define (fact-iter2 n result) 17 | (if (< n 2) result 18 | (fact-iter2 (- n 1) (* result n))) 19 | ) 20 | 21 | ;(fact-iter1 1 1) 22 | (fact-iter2 n 1) 23 | ) 24 | 25 | ; Да се изчисли n-тото число на Фибоначи. 26 | ; Тук рекурсивното решение е ужасно бавно, освен че 27 | ; заема много памет. Итеративното решение решава и 28 | ; двата проблема и е вторият най-бърз начин за решаване 29 | ; на тази задача. (hint: бързо степенуване на матрици) 30 | (define (fib n) 31 | ;(if (< n 3) 1 32 | ; (+ (fib (- n 1)) (fib (- n 2)))) 33 | 34 | (define (fib-iter a b i) 35 | (if (> i n) b 36 | (fib-iter b (+ a b) (+ i 1))) 37 | ) 38 | (fib-iter 1 1 3) 39 | ) 40 | 41 | ; Да се намери сумата от цифрите на дадено число. 42 | (define (sum-of-digits n) 43 | ;(if (< n 10) n 44 | ; (+ (remainder n 10) (sum-of-digits (quotient n 10)))) 45 | 46 | (define (sum-iter n result) 47 | (if (< n 10) (+ n result) 48 | ;(if (= n 0) result <- алтернативно дъно 49 | (sum-iter (quotient n 10) (+ result (remainder n 10)))) 50 | ) 51 | (sum-iter n 0) 52 | ) 53 | 54 | ; Да се намери сумата на целите числа в целочислен интервал. 55 | (define (sum-in-range a b) 56 | ;(if (> a b) 0 (+ a (sum-in-range (+ a 1) b))) 57 | 58 | (define (sum-iter i result) 59 | (if (> i b) result 60 | (sum-iter (+ i 1) (+ i result))) 61 | ) 62 | (sum-iter a 0) 63 | ) 64 | 65 | ; Да се намери сборът на всички делители на дадено число. 66 | (define (sum-divisors n) 67 | (define (sum-iter i result) 68 | (cond [(> i n) result] 69 | [(= (remainder n i) 0) (sum-iter (+ i 1) (+ result i))] 70 | [else (sum-iter (+ i 1) result)]) 71 | ) 72 | (sum-iter 1 0) 73 | ) 74 | 75 | ; Да се провери дали дадено число е просто 76 | ; Почти винаги трябва да внимаваме за единицaта и, 77 | ; ако трябва, да я изолираме в отделна проверка - 78 | ; тя не е нито просто, нито съставно число! 79 | ; 80 | ; Можем и съществено да подобрим скоростта, като 81 | ; ограничим търсенето на делители до (sqrt n), тъй като 82 | ; ако съществуват делители, то един от тях със сигурност 83 | ; ще бъде <= (sqrt n). 84 | (define (prime? n) 85 | ;(= (sum-divisors n) (+ n 1)) 86 | 87 | (define (prime-iter i) 88 | (cond [(>= i n) #t] 89 | [(= (remainder n i) 0) #f] 90 | [else (prime-iter (+ i 1))]) 91 | ) 92 | (if (= n 1) #f (prime-iter 2)) ; почти винаги трябва да внимаваме за единицата 93 | ) 94 | 95 | ; Да се обърнат цифрите на дадено число. 96 | ; Изненадващо, работи вярно и за отрицателни числа! 97 | (define (reverse-int n) 98 | (define (rev-iter n result) 99 | (if (= n 0) result 100 | (rev-iter (quotient n 10) (+ (* result 10) (remainder n 10)))) 101 | ) 102 | (rev-iter n 0) 103 | ) 104 | -------------------------------------------------------------------------------- /week3/README.md: -------------------------------------------------------------------------------- 1 | # Week3 - Problem Solving 2 | 3 | * [Code from lab is here](lab.rkt) 4 | * [Homework is here](homework/) 5 | -------------------------------------------------------------------------------- /week3/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 3 2 | 3 | Before diving into lists and higher order functions, we are going to do some final things with numbers. 4 | 5 | Use everything you know up until now. Use either standard or tail-call recursion. Reuse functions that you have already implemented. 6 | 7 | It is a good idea to reuse code by using [Racket modules](http://docs.racket-lang.org/guide/module-basics.html) 8 | 9 | With modules, we can load racket file from another Racket file and use the functions defined there. 10 | 11 | Here is an example (code provided in the homework folder) 12 | 13 | Lets have `math.rkt` file: 14 | 15 | ```racket 16 | #lang racket 17 | 18 | (provide 19 | square) 20 | 21 | (define (square x) (* x x)) 22 | ``` 23 | 24 | The magic here is the call to the `provide` function, coming from the Racket language. We **export** our square to whoever requires us. 25 | 26 | Lets have a `program.rkt` file, located in the same folder as `math.rkt`: 27 | 28 | ```racket 29 | #lang racket 30 | 31 | (require "math.rkt") 32 | 33 | (define (main) 34 | (println (square 2))) 35 | 36 | (main) 37 | ``` 38 | 39 | As you can see, we use the `require` function to get our file. This gives us all **provided** functions. 40 | 41 | Now, if we run the program: 42 | 43 | ``` 44 | $ racket program.rkt 45 | 4 46 | ``` 47 | 48 | Try using modules to solve this homework. 49 | 50 | ## Nth Beast Number 51 | 52 | 666 is the first number of the beast. 53 | 54 | If we want to get the next number, we must repeat the three 6's again. So the second number of the beast is 666666 55 | 56 | Implement a function, called `(nth-beast-number n)`, which returns the nth number of the beast. 57 | 58 | Examples: 59 | 60 | ```racket 61 | -> (nth-beast-number 1) 62 | 666 63 | -> (nth-beast-number 2) 64 | 666666 65 | -> (nth-beast-number 3) 66 | 666666666 67 | ``` 68 | 69 | ## Hack Numbers 70 | 71 | A hack number is an integer, that matches the following criteria: 72 | 73 | * The number, represented in binary, is a palindrome 74 | * The number, represented in binary, has an odd number of 1's in it 75 | 76 | Example of hack numbers: 77 | 78 | * 1 is `1` in binary 79 | * 7 is `111` in binary 80 | * 7919 is `1111011101111` in binary 81 | 82 | Implement a function, called `(next-hack n)` that takes an integer and returns the next hack number, that is bigger than `n` 83 | 84 | 85 | Few examples: 86 | 87 | ```racket 88 | -> (next-hack 0) 89 | 1 90 | -> (next-hack 10) 91 | 21 92 | -> (next-hack 8031) 93 | 8191 94 | ``` 95 | 96 | ## Palindrome Score 97 | 98 | We denote the "Palindrome score" of an integer by the following function: 99 | 100 | * `P(n) = 1`, if `n` is palindrome 101 | * `P(n) = 1 + P(s)` where `s` is the sum of `n` and the `reverse of n` 102 | 103 | Implement a function, called `(p-score n)`, which finds the palindrome score for `n`. 104 | 105 | Lets see two examples: 106 | 107 | * `p_score(121) = 1`, because `121` is a palindrome. 108 | * `p_score(48) = 3`, because: 109 | 110 | 1. `P(48) = 1 + P(48 + 84) = 132` 111 | 2. `P(132) = 1 + 1 + P(132 + 321 = 363)` 112 | 3. `P(363) = 1`. 113 | 4. When we return from the recursion, we get 3. 114 | 115 | Here are function call examples: 116 | 117 | ```racket 118 | -> (p-score 121) 119 | 1 120 | -> (p-score 48) 121 | 3 122 | -> (p-score 198) 123 | 6 124 | ``` 125 | 126 | -------------------------------------------------------------------------------- /week3/homework/math.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide 4 | square) 5 | 6 | (define (square x) (* x x)) 7 | -------------------------------------------------------------------------------- /week3/homework/program.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require "math.rkt") 4 | 5 | (define (main) 6 | (println (square 2))) 7 | 8 | (main) 9 | -------------------------------------------------------------------------------- /week3/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | ; Обръщане на цифрите на числото n 3 | (define (reverse-int n) 4 | (define (rev-iter n result) 5 | (cond [(= n 0) result] 6 | [else (rev-iter (quotient n 10) (+ (* result 10) (last-digit n)))])) 7 | (rev-iter n 0)) 8 | 9 | ; Най-простата проверка дали едно число е палиндром - 10 | ; дали е равно на своето "обърнато" 11 | (define (palindrome? n) 12 | (= n (reverse-int n))) 13 | 14 | ; Бърза и проста функция за повдигане на квадрат 15 | (define (sq x) (* x x)) 16 | 17 | ; "Бърза" експонента - извършваща изчисленията за 18 | ; O(logn) време. Итеративно решение съществува, но е 19 | ; твърде грозно и сложно за разписване. 20 | 21 | ; От друга страна, програма, която изразходва O(logn) 22 | ; памет в стека, не би трябвало да доведе до stack overflow 23 | ; в никакви ситуации, което обезсмисля донякъде 24 | ; итеративните реализации и на функции като reverse-int, 25 | ; increasing?, occurences и contains-digits? - 26 | ; освен за образователни цели, естествено ;) 27 | (define (fast-exponent x n) 28 | (cond [(= n 0) 1] 29 | [(odd? n) (* x (fast-exponent (sq x) (quotient n 2)))] 30 | [else (fast-exponent (sq x) (quotient n 2))])) 31 | 32 | ; Доработка на функцията за бърза експонента, която 33 | ; подобно на вградената expt изчислява коректно всякакви степени: 34 | ; (fast-exponent* 2 5) -> 32 35 | ; (fast-exponent* 2 -5) -> 1/32 36 | ; (fast-exponent* 2 1/2) -> 1.4142135623730951 37 | ; (fast-exponent* 2 -1/2) -> 0.7071067811865475 38 | ; (fast-exponent* 2 0) -> 1 39 | ; (fast-exponent* 2 (exp 1)) -> 6.5808859910179205 40 | ; 41 | ; --- ВАЖНО: тази функция е само за демонстративни цели и за 42 | ; "любопитните", по никакъв начин не бива да я считате за 43 | ; примерно написана функция. Малко е overkill и е далеч от 44 | ; целите на този курс. --- 45 | ; 46 | (define (fast-exponent* x n) 47 | (cond [(negative? n) (/ (fast-exponent* x (- n)))] 48 | [(integer? n) (fast-exponent x n)] 49 | [else (* (fast-exponent x (floor n)) 50 | (expt x (- n (floor n))))])) 51 | 52 | ; Сбор на всички делители на дадено число 53 | (define (sum-divisors n) 54 | (define (sum-iter i result) 55 | (cond [(> i n) result] 56 | [(= (remainder n i) 0) (sum-iter (+ i 1) (+ result i))] 57 | [else (sum-iter (+ i 1) result)])) 58 | (sum-iter 1 0)) 59 | 60 | ; Проверка дали дадено число е съвършено, 61 | ; т.е. сбора на всичките му делители (без себе си) 62 | ; е равен на самото число 63 | ; 6 = 1+2+3 -> #t 64 | ; 28 = 1+2+4+7+14 -> #t 65 | ; 33550336 = 1+2+...+8387584+16775168 -> #t 66 | (define (perfect? n) 67 | (= (sum-divisors n) (* n 2))) 68 | 69 | ; Малки, порсти функции за взимане на последните 70 | ; две цифри на дадено число 71 | (define (last-digit n) (remainder n 10)) 72 | (define (second-last-digit n) (last-digit (quotient n 10))) 73 | 74 | ; Три примера за функции, които нямат нужда от помощ, 75 | ; за да реализират опашкова рекурсия. Естествено, това 76 | ; са само примерни реализации, на първо място е важна коректността. 77 | (define (increasing? n) 78 | (cond [(< n 10) #t] 79 | [(< (second-last-digit n) (last-digit n)) (increasing? (quotient n 10))] 80 | [else #f])) 81 | 82 | (define (occurrences a n) 83 | (define (occ-iter n result) 84 | (cond [(= n 0) result] 85 | [(= (remainder n 10) a) (occ-iter (quotient n 10) (+ result 1))] 86 | [else (occ-iter (quotient n 10) result)])) 87 | (occ-iter n 0)) 88 | 89 | (define (contains-digits? x y) 90 | (cond [(< x 10) (positive? (occurrences x y))] ; трябва да внимаваме за (contains-digits? 0 123) 91 | [(positive? (occurrences (last-digit x) y)) (contains-digits? (quotient x 10) y)] 92 | [else #f])) 93 | -------------------------------------------------------------------------------- /week4/README.md: -------------------------------------------------------------------------------- 1 | # Week 4 - Higher order functions, lambdas and lists 2 | 3 | First, we will introduce the concept of a higher order function: 4 | 5 | * A function that takes function as an argument 6 | * Function is a type - first class citizen in the language 7 | * This gives us powerful abstraction tools to use 8 | 9 | The idea is that we write 1 function that is abstract enough and give the user the power to provide the required data via function. 10 | 11 | When we start working with lists, we will see the full strength of this concept. 12 | 13 | The simplest example: 14 | 15 | ```racket 16 | (define (calculate operation a b) 17 | (operation a b)) 18 | ``` 19 | 20 | Here, `operation` is a function. 21 | 22 | ```racket 23 | -> (calculate + 1 2) 24 | 3 25 | -> (calculate - 1 2) 26 | -1 27 | -> (calculate square-sum 1 2) 28 | 5 29 | ``` 30 | 31 | In Racket, `+` and `-` are functions! 32 | 33 | **A function that takes another function as an argument is called higher order function.** 34 | 35 | When calling higher-order functions, we can provide the required function by its name. 36 | 37 | Lets see another example: 38 | 39 | ```racket 40 | (define (twice f x) 41 | (f (f x))) 42 | ``` 43 | 44 | Here, `twice` is taking a function and one argument and it is applying that function to the given argument twice. 45 | 46 | For example: 47 | 48 | ```racket 49 | -> (define (inc x) (+ x 1)) 50 | -> (twice inc 1) 51 | 3 52 | ``` 53 | 54 | ## Lambdas 55 | 56 | Now, the first really powerful tool in our arsenal is going to be the lambda functions. 57 | 58 | Lambda is a function without name. Also known as anonymous function. 59 | 60 | When we are using a higher order function, we have two choices: 61 | 62 | 1. Define the function that we want to pass and pass it by name 63 | 2. Pass a lambda function 64 | 65 | If we need to define an entirely new function, just to use our higher order function to get some job done, we **should use a lambda function instead** 66 | 67 | Lets see what a lambda is: 68 | 69 | ```racket 70 | -> (lambda (x) (+ x 1)) 71 | # 72 | ``` 73 | 74 | Okay, so a `lambda` is a function. So far so good. But there is no name? How can we call the lambda function after we have defined it? Well, there is no way. 75 | 76 | So far, lambdas are pretty useless. 77 | 78 | How can we use them? 79 | 80 | One of the options are to call them directly after defining them. Woah! Lets see: 81 | 82 | ```racket 83 | -> ((lambda (x) (+ x 1)) 1) 84 | 2 85 | ``` 86 | 87 | Wait, what? What kind of sorcery is this? 88 | 89 | Well, we are calling our lambda right after we have defined it. And this is completely legal. We get our desired result. 90 | 91 | Imagine it like this: 92 | 93 | 1. First, we create something. Something that will go away and cannot be used. 94 | 2. But right before it goes away, we use it. 95 | 96 | Kind of zen. But still pretty useless, right? Right. 97 | 98 | The real power of lambdas come in combination with higher order functions. 99 | 100 | When a function requires another function as an argument, we can just pass a lambda! YEAH! 101 | 102 | Lets see: 103 | 104 | ```racket 105 | -> (twice (lambda (x) (string-append x "!")) "Racket") 106 | "Racket!!" 107 | -> (twice (lambda (y) (* y y y y y)) 10) 108 | 10000000000000000000000000 109 | ``` 110 | 111 | Pretty powerful stuff. We will use this a lot when we go to lists. 112 | 113 | Now lets see functions that return functions. They are higher-order function too. Things are about to get crazy! 114 | 115 | ```racket 116 | (define (get-operation oper) 117 | (cond 118 | [(equal? oper "+") +] 119 | [(equal? oper "-") -] 120 | [(equal? oper "*") *] 121 | [(equal? oper "/") /] 122 | [else +])) 123 | ``` 124 | 125 | So right now, as a result, we are returning functions. 126 | 127 | Lets see: 128 | 129 | ```racket 130 | -> (get-operation "+") 131 | # 132 | -> ((get-operation "+") 1 2) 133 | 3 134 | ``` 135 | 136 | So we can return function from our function. But what about lambdas? 137 | 138 | Lets try to implement a function that negates a predicate function. 139 | 140 | For example, imagine we have the `even?` predicate. What if we want to get another function, that is the opposite of even? 141 | 142 | ```racket 143 | -> (not even?) 144 | #f 145 | ``` 146 | 147 | We cannot just say `not` to a function. We need to implement our negate: 148 | 149 | ```racket 150 | (define (negate p) 151 | (lambda (x) (not (p x)))) 152 | ``` 153 | 154 | Lambda does the magic. We return a function of one argument. And when that function is called, it returns the opposite of the result of `(p x)` 155 | 156 | ```racket 157 | -> ((negate even) 3) 158 | #t 159 | ``` 160 | -------------------------------------------------------------------------------- /week4/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 4 2 | 3 | Homework on higher order functions. 4 | 5 | ## Highest order 6 | 7 | Implement a function `f` that takes 3 arguments - a predicate `p` and two more functions - `g` and `h`. All function arguments are functions with one argument. 8 | 9 | `f` should return a function with a single argument. 10 | 11 | When the returned function is called, it should return `#t` if both `p(g(x))` and `p(h(x))` are true. `#f` should be returned otherwise. 12 | 13 | `x` is the argument of the returned from `f` function. 14 | 15 | Examples: 16 | 17 | ```racket 18 | -> (f even? add1 add1) 19 | 20 | -> ((f even? add1 add1) 1) 21 | #t 22 | -> ((f prime? (lambda (x) x) square) 7) 23 | #f 24 | ``` 25 | 26 | ## Fractions 27 | 28 | [Using Pairs in Racket](http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_Syntax.html), implement the following functions to work with fractions. 29 | 30 | We are going to represent a fraction with a pair. 31 | 32 | Here is how you can create a pair: 33 | 34 | ```racket 35 | -> (cons 1 2) 36 | '(1 . 2) 37 | -> (define a (cons 1 2)) 38 | -> a 39 | '(1 . 2) 40 | ``` 41 | 42 | In order to get the first and the second element, and for better naming, you can use the following functions: 43 | 44 | ```racket 45 | (define (fst pair) 46 | (car pair)) 47 | 48 | (define (snd pair) 49 | (cdr pair)) 50 | ``` 51 | 52 | You can use them like that: 53 | 54 | ```racket 55 | -> (define a (cons 1 2)) 56 | -> (fst a) 57 | 1 58 | -> (snd a) 59 | 2 60 | ``` 61 | 62 | With that in mind, give implementation to the following functions to work with fractions: 63 | 64 | ```racket 65 | ; You should add two fractions together and return a new fraction 66 | (define (add-frac frac1 frac2) 67 | (void)) 68 | 69 | ; You should subtract two fractions together and return a new fraction 70 | (define (substract-frac frac1 frac2) 71 | (void)) 72 | 73 | ; You know the drill by now. Multiply and return a new fraction 74 | (define (mult-frac frac1 frac2) 75 | (void)) 76 | 77 | ; Simplify, if possible, the given fraction 78 | ; https://www.mathsisfun.com/simplifying-fractions.html 79 | (define (simplify-frac frac) 80 | (void)) 81 | ``` 82 | 83 | Everywhere, `(void)` means that the function does nothing. Replace `(void)` with proper implementation. 84 | 85 | ## Prepare for lists 86 | 87 | The last problem is to read and play with the materials about lists in Racket - 88 | 89 | Be ready. Try everything out. 90 | -------------------------------------------------------------------------------- /week4/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (provide 4 | call-n-times 5 | compose 6 | calculate 7 | twice 8 | twice2 9 | add2 10 | add3 11 | get-operation 12 | negate) 13 | 14 | (define (calculate operation a b) 15 | (operation a b)) 16 | 17 | (define (twice f x) 18 | (f (f x))) 19 | 20 | ; Викаме n поредни пъти функцията f с първи аргумент x 21 | (define (call-n-times n f x) 22 | (cond 23 | [(zero? n) x] 24 | [else (call-n-times (- n 1) f (f x))])) 25 | 26 | ; Функции, които връщат функции 27 | (define (get-operation oper) 28 | (cond 29 | [(equal? oper "+") +] 30 | [(equal? oper "-") -] 31 | [(equal? oper "*") *] 32 | [(equal? oper "/") /] 33 | [else +])) 34 | 35 | (define (add2 x) 36 | (lambda (y) 37 | (+ x y))) 38 | 39 | (define (add3 x) 40 | (lambda (y) 41 | (lambda (z) 42 | (+ x y z)))) 43 | 44 | (define (negate p) 45 | (lambda (x) (not (p x)))) 46 | 47 | (define (compose f g) 48 | (lambda (x) (f (g x)))) 49 | 50 | (define (twice2 f x) 51 | ((compose f f) x)) 52 | 53 | -------------------------------------------------------------------------------- /week5/README.md: -------------------------------------------------------------------------------- 1 | # Week 5 - Lists & Higher order functions with lists - map 2 | 3 | For lists, take a look at the following guides: 4 | 5 | * 6 | * 7 | 8 | Your homework is to complete all functions, located in [list-problems.rkt](list-problems.rkt). 9 | -------------------------------------------------------------------------------- /week5/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 5 2 | 3 | Your homework is to complete all functions, located in [list-problems.rkt](../list-problems.rkt). 4 | -------------------------------------------------------------------------------- /week5/list-problems.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ; Намира сумата на всички числа в numbers 4 | ; -> (sum (list)) 5 | ; 0 6 | ; -> (sum (list 1 2 3)) 7 | ; 6 8 | (define (sum numbers) 9 | (void)) 10 | 11 | ; Проверява дали x се среща в items 12 | ; -> (member? 1 (list 1 2 3)) 13 | ; #t 14 | ; -> (member? "asdf" (list "asd")) 15 | ; #f 16 | ; Разгледайте http://docs.racket-lang.org/reference/booleans.html 17 | (define (member? x items) 18 | (void)) 19 | 20 | ; -> (length2 (range2 1 10)) 21 | ; 9 22 | ; В Racket има такава функция, наречена length 23 | (define (length2 items) 24 | (void)) 25 | 26 | ; Връща n-тия елемент от items при 0лево базиран индекс 27 | ; -> (list-ref2 (list 1 2 3) 0) 28 | ; 1 29 | ; В Racket има такава функция, наречена list-ref 30 | (define (list-ref2 items n) 31 | (void)) 32 | 33 | ; -> (range2 1 10) 34 | ; '(1 2 3 4 5 6 7 8 9) 35 | ; В Racket съществува такава функция, наречена range 36 | (define (range2 a b) 37 | (void)) 38 | 39 | ; Строи списък от числата между 0 и n, включително, като прилага f върху всяко число 40 | ; i-тия елемент на този списък е (f i) 41 | ; -> (build-list2 3 id) 42 | ; '(0 1 2) 43 | ; -> (build-list2 3 (lambda (x) (* x x))) 44 | ; '(0 1 4) 45 | ; В Racket има такава функция, наречена build-list 46 | 47 | (define (build-list2 n f) 48 | (void)) 49 | ; Конкатенира два списъка в нов списък 50 | ; -> (append2 (list 1 2 3) (list 4 5 6)) 51 | ; '(1 2 3 4 5 6) 52 | ; В Racket има такава фунцкия, наречена append 53 | (define (append2 l1 l2) 54 | (void)) 55 | 56 | ; Обръща списъка наобратно 57 | ; -> (reverse2 (list 1 2 3)) 58 | ; '(3 2 1) 59 | ; В Racket има такава функция, наречена reverse 60 | (define (reverse2 items) 61 | (void)) 62 | 63 | ; Взима първите n елемента от даден списък 64 | ; Ако (> n (length items)), тогава връща items 65 | ; -> (take2 3 (list 1 2 3 4 5)) 66 | ; '(1 2 3) 67 | (define (take2 n items) 68 | (void)) 69 | 70 | ; Маха първите n елемента от даден списък 71 | ; Ако (> n (length items)) връща '() 72 | ; -> (drop2 3 (list 1 2 3 4 5)) 73 | ; '(4 5) 74 | (define (drop2 n items) 75 | (void)) 76 | 77 | ; Функция от по-висок ред. Взима поредни елементи от items докато предиката p за тях дава истина 78 | ; -> (take-while zero? (list 0 0 0 1 2 3)) 79 | ; '(0 0 0) 80 | ; -> (take-while even? (list 2 4 5 7 8 3 2)) 81 | ; '(2 4) 82 | ; -> (take-while (lambda (x) (> x 3)) (list 1 1 1 1 1)) 83 | ; '() 84 | (define (take-while p items) 85 | (void)) 86 | 87 | ; Функция от по-висок ред. Маха поредните елементи от items докато предикатa p дава лъжа за тях 88 | ; -> (drop-while zero? (list 0 0 0 1 2 3)) 89 | ; '(1 2 3) 90 | ; -> (drop-while even? (list 2 4 5 7 8 3 2)) 91 | ; '(5 7 8 3 2) 92 | ; -> (drop-while (lambda (x) (> x 3)) (list 1 1 1 1 1)) 93 | ; '(1 1 1 1 1) 94 | (define (drop-while p items) 95 | (void)) 96 | 97 | ; Функцията взима число и връща списък от цифрите му 98 | ; -> (number->list 123) 99 | ; '(1 2 3) 100 | (define (number->list n) 101 | (void)) 102 | 103 | ; Функцията взима списък от цифри и връща числото 104 | ; -> (list->number (list 1 2 3)) 105 | ; 123 106 | (define (list->number ns) 107 | (void)) 108 | -------------------------------------------------------------------------------- /week6/README.md: -------------------------------------------------------------------------------- 1 | # Week 6 - Filter & Reduce. Solving list problems 2 | 3 | * [Code from lab is here](lab.rkt) 4 | * [You can find homework here](homework/) 5 | -------------------------------------------------------------------------------- /week6/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 6 2 | 3 | ## group 4 | 5 | Implement the following Racket function, that groups consecutive equal elements into sub-lists: 6 | 7 | ```racket 8 | -> (group (list 1 1 1 2 3 1 2 2)) 9 | '((1 1 1) (2) (3) (1) (2 2)) 10 | -> (group (list 1 2 3 4 5 6)) 11 | '((1) (2) (3) (4) (5) (6)) 12 | -> (group (list 1 1 1 1 1 1)) 13 | '((1 1 1 1 1 1)) 14 | -> (group (list)) 15 | '() 16 | ``` 17 | 18 | ## Run-length encoding and decoding 19 | 20 | Implement the simple [run-length encoding algorithm for string compresion](https://en.wikipedia.org/wiki/Run-length_encoding) 21 | 22 | The algorithm groups consecutive equal chars in one string into the format `{N}{C}` where `{N}` is the number of consequtive repeats of the char `{C}`. 23 | 24 | If `{N}` is equal to 1, skip this `{N}` and don't add it in the encoding' 25 | 26 | For example, if we have the string `"aaaa"`, the run-legth encoding is `"4a"`. 27 | 28 | Here are examples: 29 | 30 | ```racket 31 | -> (run-length-encode "Racket") 32 | "Racket" 33 | -> (run-length-encode "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" 34 | "12W1B12W3B24W1B14W" 35 | -> (run-length-encode "baaabaaa") 36 | "b3ab3a" 37 | ``` 38 | 39 | After this, implement the decoding function, that takes an already encoded string and returns the original value: 40 | 41 | ```racket 42 | -> (run-length-decode "Racket") 43 | "Racket" 44 | -> (run-length-decode "12W1B12W3B24W1B14W") 45 | "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWBWWWWWWWWWWWWWW" 46 | -> (run-length-decode "b3ab3a") 47 | "baaabaaa" 48 | ``` 49 | 50 | -------------------------------------------------------------------------------- /week6/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (define (filter2 p xs) 4 | (cond 5 | [(empty? xs) (list)] 6 | [(p (first xs)) (cons (first xs) (filter2 p (rest xs)))] 7 | [else (filter2 p (rest xs))])) 8 | 9 | ; Reduce using standard recursion 10 | ; Also known as foldr 11 | (define (reduce-r op z xs) 12 | (cond 13 | [(empty? xs) z] 14 | [else (reduce op (op z (first xs)) (rest xs))])) 15 | 16 | ; Reduce using tail-call recursion 17 | ; Also known as foldl 18 | (define (reduce op z xs) 19 | (cond 20 | [(empty? xs) z] 21 | [else (reduce op (op z (first xs)) (rest xs))])) 22 | 23 | (define (sum ns) 24 | (reduce + 0 ns)) 25 | 26 | (define (concat words) 27 | (reduce string-append "" words)) 28 | 29 | (define (join glue xs) 30 | (reduce (lambda (acc x) 31 | (string-append acc glue x)) 32 | (first xs) 33 | (rest xs))) 34 | 35 | (define (negate p) 36 | (lambda (x) 37 | (not (p x)))) 38 | 39 | (define (reject p xs) 40 | (filter (negate p) xs)) 41 | 42 | (define (all? p xs) 43 | (= (length xs) 44 | (length (filter p xs)))) 45 | 46 | (define (any? p xs) 47 | (>= 1 48 | (length (filter p xs)))) 49 | 50 | (define (count y xs) 51 | (length 52 | (filter (lambda (x) (equal? x y)) xs))) 53 | 54 | (define (count2 y xs) 55 | (sum (map (lambda (x) 56 | (if (equal? x y) 57 | 1 58 | 0)) 59 | xs))) 60 | 61 | (define (zip xs ys) 62 | (cond 63 | [(empty? xs) (list)] 64 | [(empty? ys) (list)] 65 | [else (cons (cons (first xs) (first ys)) 66 | (zip (rest xs) (rest ys)))])) 67 | 68 | ; Използвайки знанието за това как работи map в Racket 69 | (define (zip2 xs ys) 70 | (map cons xs ys)) 71 | 72 | -------------------------------------------------------------------------------- /week7/README.md: -------------------------------------------------------------------------------- 1 | # Week 7 2 | 3 | Deep lists & Binary Trees. 4 | 5 | * [Code from lab is here](lab.rkt) 6 | * [Homework is here](homework/) 7 | -------------------------------------------------------------------------------- /week7/homework/README.md: -------------------------------------------------------------------------------- 1 | # Homework 7 - Tree problems 2 | 3 | ## Tree levels 4 | 5 | Implement the following Racket functions: 6 | 7 | ```racket 8 | (define (tree-level level tree) 9 | (void)) 10 | 11 | (define (tree-levels tree) 12 | (void)) 13 | ``` 14 | 15 | The first function takes a `level`, a binary `tree` and returns a list of all emenents that are located on that same level of the tree. Start counting levels from 1. 16 | 17 | The second function takes a binary `tree` and returns a list of lists of all elemenets on every level of the tree. The `ith` element of the returned list should be the elements on the `ith` level of the `tree` 18 | 19 | Examples: 20 | 21 | ```racket 22 | (define t 23 | (make-tree 1 24 | (make-tree 2 25 | (make-leaf 5) 26 | (make-leaf 6)) 27 | (make-leaf 3))) 28 | 29 | -> (tree-level 1 t) 30 | '(1) 31 | -> (tree-level 2 t) 32 | '(2 3) 33 | -> (tree-level 3 t) 34 | '(5 6) 35 | -> (tree-levels t) 36 | '((1) (2 3) (5 6)) 37 | ``` 38 | 39 | ## Map a Tree 40 | 41 | Implement the following Racket function: 42 | 43 | ```racket 44 | (define (tree-map f tree) 45 | (void)) 46 | ``` 47 | 48 | The function takes a single argument funciton `f` and ` binary `tree` and returns a new tree, where every node is transformed by `f` 49 | 50 | Example: 51 | 52 | ```racket 53 | (define t 54 | (make-tree 1 55 | (make-tree 2 56 | (make-leaf 5) 57 | (make-leaf 6)) 58 | (make-leaf 3))) 59 | 60 | -> (tree-map add1 t) 61 | 62 | '(2 63 | (3 64 | (6 () ()) 65 | (7 () ())) 66 | (4 () ())) 67 | ``` 68 | 69 | ## Binary Search Tree 70 | 71 | Implement the following functions that work on [binary search trees](https://en.wikipedia.org/wiki/Binary_search_tree) 72 | 73 | ```racket 74 | ; Inserts x in the tree, returning a new BST with the proper structure 75 | (define (bst-insert x tree) 76 | (void)) 77 | 78 | ; Checks if x is an element of tree 79 | (define (bst-element? x tree) 80 | (void)) 81 | 82 | ; Traverse the tree in a such way that the list should contain sorted elements 83 | (define (bst->list tree) 84 | (void)) 85 | 86 | ; Checks if the given binary tree is a binary search tree 87 | (define (bst? tree) 88 | (void)) 89 | ``` 90 | -------------------------------------------------------------------------------- /week7/homework/solutions.rkt: -------------------------------------------------------------------------------- 1 | (define (tree-map f tree) 2 | (cond 3 | [(empty? tree) tree] 4 | [else (make-tree (f (root tree)) (tree-map f (left tree)) (tree-map f (right tree)))])) 5 | 6 | 7 | (define (tree-level level tree) 8 | (cond 9 | [(empty? tree) (list)] 10 | [(= level 1) (list (root tree))] 11 | [else (append (tree-level (- level 1) (left tree)) (tree-level (- level 1) (right tree)))])) 12 | 13 | (define (all-levels tree) 14 | (map (lambda (level) (tree-level level tree)) 15 | (range 1 (add1 (max-level tree))))) 16 | 17 | (define (bst-insert x tree) 18 | (cond 19 | [(empty? tree) (make-leaf x)] 20 | [(= x (root tree)) tree] 21 | [(< x (root tree)) (make-tree (root tree) (bst-insert x (left tree)) (right tree))] 22 | [(> x (root tree)) (make-tree (root tree) (left tree) (bst-insert x (right tree)))])) 23 | -------------------------------------------------------------------------------- /week7/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (define (atom? x) 4 | (not (or 5 | (pair? x) 6 | (null? x) 7 | (vector? x)))) 8 | 9 | ; Тук обхождаме дълбок списък с идея за странични ефекти 10 | ; Не е задължително да гледате функцията, просто е хубав пример при нужда 11 | (define (walk-deep-list f l) 12 | (cond 13 | [(null? l) (void)] 14 | [(atom? (first l)) (begin 15 | (f (first l)) 16 | (walk-deep-list f (rest l)))] 17 | [else (begin 18 | (walk-deep-list f (first l)) 19 | (walk-deep-list f (rest l)))])) 20 | 21 | (define (count-atoms l) 22 | (cond 23 | [(null? l) 0] 24 | [(atom? (first l)) (+ 1 (count-atoms (rest l)))] 25 | [else (+ (count-atoms (first l)) (count-atoms (rest l)))])) 26 | 27 | (define (deep-map f l) 28 | (cond 29 | [(null? l) (list)] 30 | [(atom? (first l)) (cons (f (first l)) (deep-map f (rest l)))] 31 | [else (cons (deep-map f (first l)) (deep-map f (rest l)))])) 32 | 33 | (define (flatten l) 34 | (cond 35 | [(null? l) (list)] 36 | [(atom? (first l)) (cons (first l) (flatten (rest l)))] 37 | [else (append (flatten (first l)) (flatten (rest l)))])) 38 | 39 | (define (sum-atoms l) 40 | (apply + (flatten l))) 41 | 42 | (define (sum-atoms2 l) 43 | (cond 44 | [(null? l) 0] 45 | [(atom? (first l)) (+ (first l) (sum-atoms2 (rest l)))] 46 | [else (+ (sum-atoms2 (first l)) (sum-atoms2 (rest l)))])) 47 | 48 | (define (make-tree node left right) 49 | (list node left right)) 50 | 51 | (define (make-leaf node) 52 | (make-tree node '() '())) 53 | 54 | (define (empty-tree? tree) 55 | (null? tree)) 56 | 57 | (define (root tree) 58 | (first tree)) 59 | 60 | (define (left tree) 61 | (first (rest tree))) 62 | 63 | (define (right tree) 64 | (first (rest (rest tree)))) 65 | 66 | (define (count-nodes tree) 67 | (cond 68 | [(empty? tree) 0] 69 | [else (+ 1 (count-nodes (left tree)) (count-nodes (right tree)))])) 70 | 71 | (define (height tree) 72 | (cond 73 | [(empty? tree) 0] 74 | [else (+1 (max (height (left tree)) (height (right tree))))])) 75 | 76 | ; 1 77 | ; / \ 78 | ; 2 3 79 | ; / \ 80 | ;5 6 81 | (define t 82 | (make-tree 1 83 | (make-tree 2 84 | (make-leaf 5) 85 | (make-leaf 6)) 86 | (make-leaf 3))) 87 | -------------------------------------------------------------------------------- /week8/README.md: -------------------------------------------------------------------------------- 1 | # Week 8 - First Exam Prepration 2 | 3 | * [Code from lab can be found here](lab.rkt) 4 | * [Your homework is located here](homework/) 5 | * [You can find more problems to prepare yourself in Moodle](http://moodle.openfmi.net/pluginfile.php/91977/mod_resource/content/2/%D0%97%D0%B0%D0%B4%D0%B0%D1%87%D0%B8%20%D0%B7%D0%B0%20%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%BA%D0%B0%20%D0%B7%D0%B0%20%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%BD%D0%BE.pdf) 6 | -------------------------------------------------------------------------------- /week8/homework/README.md: -------------------------------------------------------------------------------- 1 | # Week 8 - Homework 2 | 3 | Your homework is to solve the last year Racket's exam - [group 1](https://github.com/fmi/fp2014/blob/master/exam/exam1/group1.md) and [group 2](https://github.com/fmi/fp2014/blob/master/exam/exam1/group2.md) and [the two Racket/Scheme problems from the final exam](https://github.com/fmi/fp2014/tree/master/exam/final-exam) 4 | -------------------------------------------------------------------------------- /week8/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (define (compose f g) 4 | (lambda (x) 5 | (f (g x)))) 6 | 7 | (define (id x) x) 8 | 9 | (define (compose-list fs) 10 | (cond 11 | [(empty? fs) id] 12 | [else (compose (first fs) (compose-list (rest fs)))])) 13 | 14 | (define (take n items) 15 | (cond 16 | [(empty? items) (list)] 17 | [(= n 0) (list)] 18 | [else (cons (first items) (take (- n 1) (rest items)))])) 19 | 20 | (define (drop n items) 21 | (cond 22 | [(empty? items) (list)] 23 | [(= n 0) items] 24 | [else (drop (- n 1) (rest items))])) 25 | 26 | (define (sublists-at index items) 27 | (define (inner items-to-take items) 28 | (cond 29 | [(> items-to-take (length items)) (list)] 30 | [else (cons (take items-to-take items) (inner (add1 items-to-take) items))])) 31 | (inner 1 (drop index items))) 32 | 33 | ; This is called remove-duplicates in Racket 34 | (define (dedup xs) 35 | (define (iter xs rs) 36 | (cond 37 | [(empty? xs) rs] 38 | [(member (first xs) rs) (iter (rest xs) rs)] 39 | [else (iter (rest xs) (append rs (list (first xs))))])) 40 | (iter xs '())) -------------------------------------------------------------------------------- /week9/lab.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (define (sum ns) 4 | (apply + ns)) 5 | 6 | (define (zip-with f xs ys) 7 | (map f xs ys)) 8 | 9 | (define (zip xs ys) 10 | (zip-with cons xs ys)) 11 | 12 | (define (enumerate l) 13 | (zip-with list (range 0 (length l)) l)) 14 | 15 | (define (row index matrix) 16 | (list-ref matrix index)) 17 | 18 | (define (column index matrix) 19 | (map (lambda (row) 20 | (list-ref row index)) 21 | matrix)) 22 | 23 | (define (main-diagonal matrix) 24 | (map (lambda (enum-row) 25 | (list-ref (second enum-row) (first enum-row))) 26 | (enumerate matrix))) 27 | 28 | (define (sum-matrix matrix) 29 | (sum (map sum matrix))) 30 | 31 | (define (cartesian-product xs ys) 32 | (map (lambda (x) 33 | (map (lambda (y) 34 | (cons x y)) 35 | ys)) 36 | xs)) 37 | 38 | (define matrix 39 | '((1 2 3) 40 | (4 5 6) 41 | (7 8 9))) --------------------------------------------------------------------------------