From f3644b0dae6b0a98e4af0299751449d5db27e2ea Mon Sep 17 00:00:00 2001 From: gcnew Date: Wed, 2 Nov 2016 14:21:00 +0200 Subject: [PATCH 1/8] Added lists exercise --- exercises/lists/Lists.hs | 93 +++++++++ exercises/lists/Lists.md | 370 ++++++++++++++++++++++++++++++++++ exercises/lists/ListsTests.hs | 197 ++++++++++++++++++ exercises/lists/Testing.hs | 72 +++++++ 4 files changed, 732 insertions(+) create mode 100644 exercises/lists/Lists.hs create mode 100644 exercises/lists/Lists.md create mode 100644 exercises/lists/ListsTests.hs create mode 100644 exercises/lists/Testing.hs diff --git a/exercises/lists/Lists.hs b/exercises/lists/Lists.hs new file mode 100644 index 0000000..9ae96fe --- /dev/null +++ b/exercises/lists/Lists.hs @@ -0,0 +1,93 @@ +{-# OPTIONS_GHC -Wall #-} + +module Lists where + +import qualified Data.List () + +import Prelude hiding ( + head, tail, null, length, reverse, repeat, replicate, + concat, sum, maximum, take, drop, elem, (++), (!!) + ) + + +head :: [Int] -> Int +head = undefined + + +tail :: [Int] -> [Int] +tail = undefined + + +append :: [Int] -> [Int] -> [Int] +append = undefined + + +elementAt :: Int -> [Int] -> Int +elementAt = undefined + + +null :: [Int] -> Bool +null = undefined + + +length :: [Int] -> Int +length = undefined + + +take :: Int -> [Int] -> [Int] +take = undefined + + +drop :: Int -> [Int] -> [Int] +drop = undefined + + +elem :: Int -> [Int] -> Bool +elem = undefined + + +reverseHelper :: [Int] -> [Int] -> [Int] +reverseHelper = undefined + +reverse :: [Int] -> [Int] +reverse = undefined + + +concat :: [[Int]] -> [Int] +concat = undefined + + +replicate :: Int -> Int -> [Int] +replicate = undefined + + +interleave :: [Int] -> [Int] -> [Int] +interleave = undefined + + +sum :: [Int] -> Int +sum = undefined + + +maximum :: [Int] -> Int +maximum = undefined + + +nub :: [Int] -> [Int] +nub = undefined + + +delete :: Int -> [Int] -> [Int] +delete = undefined + + +difference :: [Int] -> [Int] -> [Int] +difference = undefined + + +union :: [Int] -> [Int] -> [Int] +union = undefined + + +intersect :: [Int] -> [Int] -> [Int] +intersect = undefined diff --git a/exercises/lists/Lists.md b/exercises/lists/Lists.md new file mode 100644 index 0000000..28d381d --- /dev/null +++ b/exercises/lists/Lists.md @@ -0,0 +1,370 @@ + + +Работа със списъци +====== + +Списъкът е основна структура данни във функционалните езици. Затова и съществуват множество функции, които правят работата с тях по-лесна. Познаването и използването на тези функции спомага за по-лесната взаимо-работа с други хора и по-малко време отделено в преоткриване на колелото. + +Ще дефинираме следните функции върху списъци от числа, но алтернативи със същите имена се намират в пакета `Data.List` и работят върху списъци от произволен тип. Най-често ползваните от тях са налична дори без импортирането на `Data.List`, тъй като са толкова основни, че са включени в `Prelude` - модулът, предоставящ "вградените" типове и операции. + +[Документация на `Data.List`](https://hackage.haskell.org/package/base-4.9.0.0/docs/Data-List.html) + +## Няколко думи за тестване + +Тази задача има базови Unit tests. Те се намират в `ListTests.hs` и следват като конвенция името на фунцкията с добавено `Test` отзад. + +Пр: `null` -> `nullTest`. + +За да ги пуснете, заредете файла `ListTests.hs` в _ghci_ и използвайте: + +`putStrLn (runTests [nullTest, lengthTest])` за да изпълните тестовете за `null` и `length` съответно. + +`putStrLn (runTests allTests)` изпълнява всички тестове. + +## Основни Операции + +### head +```hs +head :: [Int] -> Int +``` + +Взима първия елемент на списъка. Ако списъкът е празен, хвърля грешка. + +```hs +> head [1, 2, 3] +1 + +> head [] +** Exception: empty list +``` + + +### tail +```hs +tail :: [Int] -> [Int] +``` + +Връща опашката на списъка. Ако списъкът е празен, хвърля грешка. + +```hs +> tail [1, 2, 3] +[2, 3] + +> tail [] +** Exception: empty list +``` + + +### append +```hs +append :: [Int] -> [Int] -> [Int] +``` + +Слепва два списъка. Стандартната операция за `append` от `Prelude` е операторът `++`. + +```hs +> append [1, 2, 3] [4, 5, 6] +[1, 2, 3, 4, 5, 6] + +> [1, 2, 3] ++ [4, 5, 6] +[1, 2, 3, 4, 5, 6] +``` + + +### elementAt +```hs +elementAt :: Int -> [Int] -> Int +``` + +Връща `n`-тия елемент от списък. Ако списъкът е по-къс или индексът е отрицателен, хвърля грешка. Стандартният оператор от `Prelude` е `!!`. + +```hs +> elementAt 0 [1, 2, 3] +1 + +> elementAt 4 [1, 2, 3] +** Exception: index too large + +> [1, 2, 3, 4] !! 1 +2 +``` + + +### null +```hs +null :: [Int] -> Bool +``` + +Проверява дали подаденият списък е празен. + +```hs +> null [1, 2, 3] +False + +> null [] +True +``` + + +### length +```hs +length :: [Int] -> Int +``` + +Връща големината на списъка. + +```hs +> length [1, 2, 3] +3 + +> length [] +0 +``` + + +## Подсписъци + +### take +```hs +take :: Int -> [Int] -> [Int] +``` + +Взима първите `n` елемента от списък. Ако `n` е отрицателно връща празен списък. Ако списъкът е по-къс, взима възможно най-голям брой елементи, без да хвърля грешки. + +```hs +> take 2 [1, 2, 3] +[1, 2] + +> take 5 [] +[] +``` + + +### drop +```hs +drop :: Int -> [Int] -> [Int] +``` + +Премахва първите `n` елемента от списък. Ако `n` е отрицателно връща списъка непроменен. Ако списъкът е по-къс, премахва възможно най-голям брой елементи, без да хвърля грешки. + +```hs +> drop 2 [1, 2, 3] +[3] + +> drop 5 [] +[] +``` + + +## Търсене на елемент в списък + +### elem +```hs +elem :: Int -> [Int] -> Bool +``` + +Проверява дали подаденият елемент е част от списъка. Връща `True` ако да. + +```hs +> elem 2 [1, 2, 3] +True + +> elem 5 [] +False +``` + + +## Трансформации + +### reverse +```hs +reverse :: [Int] -> [Int] +``` + +Връща нов списък с елементите от първия в обратен ред. + +```hs +> reverse [1, 2, 3] +[3, 2, 1] +``` + + +### concat +```hs +concat :: [[Int]] -> [Int] +``` + +"Опростява" списък от списъци до списък, като слепва вложените списъци. + +```hs +> concat [] +[] + +> concat [[1, 2, 3], [4, 5, 6]] +[1, 2, 3, 4, 5, 6] +``` + + +## Други често използвани операции + +### replicate +```hs +replicate :: Int -> Int -> [Int] +``` + +Създава списък от даден елемент `x` повторен `n` пъти. + +```hs +> replicate 1 5 +[5] + +> replicate 4 1 +[1, 1, 1, 1] +``` + + +### interleave +```hs +interleave :: [Int] -> [Int] -> [Int] +``` + +Кръстосва два списъка. Резултатният списък взима елемент първо от първия, после от втория, докато не изчерпи елементите на по-късият списък. + +```hs +> interleave [1, 2, 3] [4, 5, 6] +[1, 4, 2, 5, 3, 6] + +> interleave [1, 2] [3, 4, 5] +[1, 3, 2, 4] +``` + + +### sum +```hs +sum :: [Int] -> Int +``` + +Връща сумата на числата в подадения списък. + +```hs +> sum [] +0 + +> sum [1, 2, 3] +6 +``` + + +### maximum +```hs +maximum :: [Int] -> Int +``` + +Връща стойността на най-голямото число в списъка. + +```hs +> maximum [1, 2, 3] +3 + +> maximum [] +** Exception: empty list +``` + + +## Списъци като множества + +### nub +```hs +nub :: [Int] -> [Int] +``` + +Премахва повтарящите се елементи от подадения списък. + +```hs +> Data.List.sort (nub [1, 2, 3, 2, 4]) +[1, 2, 3, 4] + +> Data.List.sort (nub []) +[] +``` + + +### delete +```hs +delete :: Int -> [Int] -> [Int] +``` + +Премахва първото срещане на подадения елемент от списъка. + +```hs +> delete 2 [1, 2, 2, 3, 4] +[1, 2, 3, 4] + +> delete 5 [1, 2, 3, 4] +[1, 2, 3, 4] +``` + + +### difference +```hs +difference :: [Int] -> [Int] -> [Int] +``` + +Връща всички елементи от първия списък `xs`, които не са налични във втория `ys`. + +**ВАЖНО:** Двата списъка не са стриктно `множества`, a могат да имат и повтарящи се елементи. В такъв случай, ако елемента `x` се повтаря `n` пъти в `xs`, то в резултата `x` трябва да присъства `n - m` пъти, където `m` е срещанията на `x` в `ys`. + +```hs +> difference [1, 2, 3] [1, 2] +[3] + +> difference [1, 2, 2, 2, 3, 4] [1, 2, 2, 3, 3] +[2, 4] +``` + + +### union +```hs +union :: [Int] -> [Int] -> [Int] +``` + +Връща всички елементи на първия списък `xs`, като към тях са добавени и елементите на втория `ys`, които `xs` не съдържа. + +**ВАЖНО:** Ако има повторения на елементи в `xs`, те присъстват в изхода непроменени. Ако има повторения в `ys` те се добавят еднократно към резултата. + +```hs +> Data.List.sort (union [1, 2, 3] [1, 2, 3, 4]) +[1, 2, 3, 4] + +> Data.List.sort (union [1, 2, 2] [3, 3]) +[1, 2, 2, 3] +``` + + +### intersect +```hs +intersect :: [Int] -> [Int] -> [Int] +``` + +Връща всички елементи от първия списък `xs`, които са налични и във втория `ys`. + +**ВАЖНО:** Ако даден елемент в `xs` се повтаря и е също наличен в `ys`, то той се повтаря и в изхода. + +```hs +> intersect [1, 2, 3] [1, 2, 3, 4] +[1, 2, 3] + +> intersect [1, 2, 2] [2, 3] +[2, 2] +``` diff --git a/exercises/lists/ListsTests.hs b/exercises/lists/ListsTests.hs new file mode 100644 index 0000000..2f76056 --- /dev/null +++ b/exercises/lists/ListsTests.hs @@ -0,0 +1,197 @@ +{-# OPTIONS_GHC -Wall #-} + +module ListTests where + +import Testing +import Lists as L +import Data.List as DL + + +headTest :: Test +headTest = Test "head" (ap testEq L.head) [ + ([1, 2, 3], 1), + ([10], 10), + ([], throwsError) + ] + +tailTest :: Test +tailTest = Test "tail" (ap testEq L.tail) [ + ([1, 2, 3], [2, 3]), + ([10], []), + ([], throwsError) + ] + +appendTest :: Test +appendTest = Test "append" (ap2 testEq L.append) [ + ([1, 2, 3], [4, 5, 6], [1, 2, 3, 4, 5, 6]), + ([1, 2, 3], [], [1, 2, 3]), + ([], [1, 2, 3], [1, 2, 3]), + ([], [], []) + ] + +elementAtTest :: Test +elementAtTest = Test "elementAt" (ap2 testEq L.elementAt) [ + (0, [1, 2, 3], 1), + (1, [1, 2, 3], 2), + (2, [1, 2, 3], 3), + (4, [1, 2, 3], throwsError), + (-1, [1, 2, 3], throwsError) + ] + +nullTest :: Test +nullTest = Test "null" (ap testEq L.null) [ + ([], True), + ([0], False), + ([1,2,3], False) + ] + +lengthTest :: Test +lengthTest = Test "length" (ap testEq L.length) [ + ([], 0), + ([0], 1), + ([1,2,3], 3) + ] + +reverseTest :: Test +reverseTest = Test "reverse" (ap testEq L.reverse) [ + ([], []), + ([0], [0]), + ([1,2,3], [3, 2, 1]) + ] + +replicateTest :: Test +replicateTest = Test "replicate" (ap2 testEq L.replicate) [ + (-1, 1, []), + (0, 1, []), + (1, 1, [1]), + (5, 2, [2, 2, 2, 2, 2]) + ] + +interleaveTest :: Test +interleaveTest = Test "interleave" (ap2 testEq L.interleave) [ + ([], [1, 2, 3], []), + ([1, 2, 3], [], [1]), + ([1, 2, 3], [4, 5, 6, 7], [1, 4, 2, 5, 3, 6]), + ([1, 2, 3, 4], [5, 6, 7], [1, 5, 2, 6, 3, 7, 4]) + ] + +concatTest :: Test +concatTest = Test "concat" (ap testEq L.concat) [ + ([], []), + ([[1, 2]], [1, 2]), + ([[1, 2], [3, 4]], [1, 2, 3, 4]), + ([[1, 2], [3], []], [1, 2, 3]) + ] + +sumTest :: Test +sumTest = Test "sum" (ap testEq L.sum) [ + ([], 0), + ([1, 2], 3), + ([1, 2, 4, -10], -3), + ([-20], -20) + ] + +maximumTest :: Test +maximumTest = Test "maximum" (ap testEq L.maximum) [ + ([], throwsError), + ([1, 2], 2), + ([1, 2, 4, -10], 4), + ([-20, -10, -5], -5) + ] + +takeTest :: Test +takeTest = Test "take" (ap2 testEq L.take) [ + ( 10, [], []), + (-10, [1, 2], []), + ( 2, [1, 2, 3, 4], [1, 2]), + ( 0, [1, 2, 3, 4], []), + ( 10, [1, 2, 3, 4], [1, 2, 3, 4]) + ] + +dropTest :: Test +dropTest = Test "drop" (ap2 testEq L.drop) [ + (10, [], []), + (-10, [1, 2], [1, 2]), + ( 2, [1, 2, 3, 4], [3, 4]), + ( 0, [1, 2, 3, 4], [1, 2, 3, 4]), + (10, [1, 2, 3, 4], []) + ] + +elemTest :: Test +elemTest = Test "elem" (ap2 testEq L.elem) [ + (10, [], False), + ( 1, [1, 2], True), + ( 4, [1, 2, 3, 4], True), + ( 5, [1, 2, 3, 4], False) + ] + +nubTest :: Test +nubTest = Test "nub" (ap (before testEq DL.sort) L.nub) [ + ([], []), + ([1, 2, 3], [1, 2, 3]), + ([1, 2, 2, 1, 3, 5, 5], [1, 2, 3, 5]) + ] + +deleteTest :: Test +deleteTest = Test "delete" (ap2 testEq L.delete) [ + (10, [], []), + ( 1, [1, 2, 3], [2, 3]), + ( 2, [1, 2, 2, 3], [1, 2, 3]), + ( 5, [1, 2, 2, 3], [1, 2, 2, 3]) + ] + +differenceTest :: Test +differenceTest = Test "difference" (ap2 (before testEq DL.sort) L.difference) [ + ([], [], []), + ([1, 2, 3], [], [1, 2, 3]), + ([], [1, 2, 3], []), + ([1, 2, 3], [4, 5, 6], [1, 2, 3]), + ([1, 2, 3], [1, 2, 3], []), + ([1, 2, 2, 3, 3], [1, 3, 4], [2, 2, 3]), + ([1, 2, 2, 3, 3], [1, 3, 3], [2, 2]) + ] + +unionTest :: Test +unionTest = Test "union" (ap2 (before testEq DL.sort) L.union) [ + ([], [], []), + ([1, 2, 3], [], [1, 2, 3]), + ([], [1, 2, 3], [1, 2, 3]), + ([1, 2, 3], [4, 5, 6], [1, 2, 3, 4, 5, 6]), + ([1, 2, 3], [1, 2, 3], [1, 2, 3]), + ([1, 2, 2, 3, 3], [1, 3, 3], [1, 2, 2, 3, 3]), + ([1, 2, 2, 3, 3], [1, 3, 4], [1, 2, 2, 3, 3, 4]) + ] + +intersectTest :: Test +intersectTest = Test "intersect" (ap2 (before testEq DL.sort) L.intersect) [ + ([], [], []), + ([1, 2, 3], [], []), + ([], [1, 2, 3], []), + ([1, 2, 3], [4, 5, 6], []), + ([1, 2, 3], [1, 2, 3], [1, 2, 3]), + ([1, 2, 2, 3, 3], [1, 3, 3], [1, 3, 3]), + ([1, 2, 2, 3, 3], [1, 3, 4], [1, 3, 3]) + ] + +allTests :: [Test] +allTests = [ headTest + , tailTest + , appendTest + , elementAtTest + , nullTest + , lengthTest + , reverseTest + , replicateTest + , interleaveTest + , concatTest + , sumTest + , maximumTest + , takeTest + , dropTest + , elemTest + , nubTest + , deleteTest + , differenceTest + , unionTest + , intersectTest + ] diff --git a/exercises/lists/Testing.hs b/exercises/lists/Testing.hs new file mode 100644 index 0000000..940995e --- /dev/null +++ b/exercises/lists/Testing.hs @@ -0,0 +1,72 @@ +{-# OPTIONS_GHC -Wall #-} + +{-# LANGUAGE MagicHash #-} +{-# LANGUAGE BangPatterns #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ExistentialQuantification #-} + +module Testing + ( runTests + , ap + , ap2 + , before + , throwsError + , testEq + , Test(..) + ) where + +import Data.Maybe +-- import Control.Arrow +import Control.Exception +import System.IO.Unsafe ( unsafePerformIO ) +import GHC.Exts ( isTrue#, reallyUnsafePtrEquality# ) + + +data Test = forall a. Show a => Test String (a -> Bool) [a] +data Failure = forall a. Show a => Fail String [a] + +instance Show Failure where + show (Fail s as) = "Failed Test \"" ++ s + ++ "\" on inputs " ++ show as + +ptrEq :: a -> a -> Bool +ptrEq x y = isTrue# (reallyUnsafePtrEquality# x y) + +throwsError :: a +throwsError = error "throwsError" + +testEq :: Eq a => a -> a -> Bool +testEq arg res + | not (res `ptrEq` throwsError) = arg == res + + | otherwise = unsafePerformIO $ + handle (\(_ :: SomeException) -> return True) $ do + let !_ = arg == res + return False + +runTest :: Test -> Maybe Failure +runTest (Test s f as) = case filter (not . f) as of + [] -> Nothing + fs -> Just $ Fail s fs + +printCase :: Show a => a -> String +printCase c = " " ++ show c + +printFail :: Failure -> String +printFail (Fail d cases) = "Failed: " ++ d ++ "\n" ++ unlines (map printCase cases) + +runTests :: [Test] -> String +runTests tests = message + where fails = catMaybes (map runTest tests) + + message | null fails = "Success!" + | otherwise = unlines (map printFail fails) + +before :: (a -> a -> Bool) -> (a -> a) -> a -> a -> Bool +before f g x y = f (g x) y + +ap :: (r -> r -> Bool) -> (a -> r) -> (a, r) -> Bool +ap test f (a, b) = test (f a) b + +ap2 :: (r -> r -> Bool) -> (a -> b -> r) -> (a, b, r) -> Bool +ap2 test f (a, b, c) = test (f a b) c From cf45236005ab32304e625c8e7b9679e437103a70 Mon Sep 17 00:00:00 2001 From: Georgi Nakov Date: Wed, 2 Nov 2016 15:54:45 +0200 Subject: [PATCH 2/8] Colorize implementation done in class --- exercises/colorize/Colorize.hs | 41 +++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/exercises/colorize/Colorize.hs b/exercises/colorize/Colorize.hs index 52b0f1b..7e5066a 100644 --- a/exercises/colorize/Colorize.hs +++ b/exercises/colorize/Colorize.hs @@ -26,33 +26,44 @@ clear = 0 -- Exercise 1 getLastDigit :: Int -> Int -getLastDigit = undefined +getLastDigit x = mod x 10 dropLastDigit :: Int -> Int -dropLastDigit = undefined +dropLastDigit x = quot x 10 -- Exercise 2 getReverseDigits :: Int -> [Int] -getReverseDigits = undefined +getReverseDigits x | x < 10 = [x] + | otherwise = getLastDigit x : getReverseDigits (dropLastDigit x) -- Exercise 3 toChar :: Int -> Char -toChar = undefined +toChar 0 = '0' +toChar 1 = '1' +toChar 2 = '2' +toChar 3 = '3' +toChar 4 = '4' +toChar 5 = '5' +toChar 6 = '6' +toChar 7 = '7' +toChar 8 = '8' +toChar 9 = '9' +toChar _ = error "Not a digit" -- Exercise 4 itoaLoop :: String -> [Int] -> String -itoaLoop = undefined - +itoaLoop acc [] = acc +itoaLoop acc (x:xs) = itoaLoop (toChar x : acc) xs itoa :: Int -> String -itoa = undefined +itoa num = itoaLoop [] (getReverseDigits num) -- Exercise 5 @@ -66,8 +77,16 @@ mkTextStyle color = mkStyle (color + textStyle) getStyle :: String -> String -getStyle = undefined - +getStyle "blk" = mkTextStyle black +getStyle "red" = mkTextStyle red +getStyle "grn" = mkTextStyle green +getStyle "ylw" = mkTextStyle yellow +getStyle "blu" = mkTextStyle blue +getStyle "mgt" = mkTextStyle magenta +getStyle "cyn" = mkTextStyle cyan +getStyle "wht" = mkTextStyle white +getStyle "clr" = mkStyle clear +getStyle xs = "<" ++ xs ++ ">" -- Exercise 6 @@ -82,7 +101,9 @@ bleach [] = [] colorize :: String -> String -colorize = undefined +colorize ('<':x:y:z:'>':rest) = getStyle [x, y, z] ++ colorize rest +colorize (x:xs) = x : colorize xs +colorize [] = [] -- Extra From fb51e26890ae2279a0971d6dac275607f40343c3 Mon Sep 17 00:00:00 2001 From: Georgi Nakov Date: Wed, 2 Nov 2016 15:55:18 +0200 Subject: [PATCH 3/8] List functions implemented in class --- exercises/lists/Lists.hs | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/exercises/lists/Lists.hs b/exercises/lists/Lists.hs index 9ae96fe..7656510 100644 --- a/exercises/lists/Lists.hs +++ b/exercises/lists/Lists.hs @@ -6,36 +6,50 @@ import qualified Data.List () import Prelude hiding ( head, tail, null, length, reverse, repeat, replicate, - concat, sum, maximum, take, drop, elem, (++), (!!) + concat, sum, maximum, take, drop, elem, (!!) ) head :: [Int] -> Int -head = undefined +head [] = error "empty list" +head (x:_) = x tail :: [Int] -> [Int] -tail = undefined +tail [] = error "empty list" +tail (_:xs) = xs append :: [Int] -> [Int] -> [Int] -append = undefined +append [] ys = ys +append (x:xs) ys = x : (append xs ys) elementAt :: Int -> [Int] -> Int -elementAt = undefined +elementAt 0 (x:_) = x +elementAt _ [] = error "index greater than length" +elementAt n (_:xs) = elementAt (n - 1) xs null :: [Int] -> Bool -null = undefined - +null [] = True +null _ = False length :: [Int] -> Int length = undefined take :: Int -> [Int] -> [Int] -take = undefined +take _ [] = [] +take 1 (x:xs) = [x] +take n _ | n < 1 = [] +take n (x:xs) = x : take (n-1) xs + +take' :: Int -> [Int] -> [Int] +take' 0 _ = [] +take' n [] = [] +take' n (x:xs) | n < 0 = [] + | otherwise = x : take' (n-1) xs drop :: Int -> [Int] -> [Int] @@ -47,11 +61,19 @@ elem = undefined reverseHelper :: [Int] -> [Int] -> [Int] -reverseHelper = undefined +reverseHelper acc [] = acc +reverseHelper acc (x:xs) = reverseHelper (x:acc) xs reverse :: [Int] -> [Int] -reverse = undefined +reverse xs = reverseHelper [] xs + +reverseStringHelper::String->String->String +reverseStringHelper acc [] = acc +reverseStringHelper acc (x:xs) = (reverseStringHelper (x:acc) xs) + +reverseString :: String -> String +reverseString str = "This is the reversed string: " ++ (reverseStringHelper [] str) concat :: [[Int]] -> [Int] concat = undefined From a5d7cbdae95acc686191049f83a4edddb530f329 Mon Sep 17 00:00:00 2001 From: gcnew Date: Thu, 3 Nov 2016 02:47:17 +0200 Subject: [PATCH 4/8] Improved testing logic - throwing functions are handled better now --- exercises/lists/Testing.hs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/exercises/lists/Testing.hs b/exercises/lists/Testing.hs index 940995e..1c8b230 100644 --- a/exercises/lists/Testing.hs +++ b/exercises/lists/Testing.hs @@ -35,15 +35,18 @@ ptrEq x y = isTrue# (reallyUnsafePtrEquality# x y) throwsError :: a throwsError = error "throwsError" -testEq :: Eq a => a -> a -> Bool -testEq arg res - | not (res `ptrEq` throwsError) = arg == res - - | otherwise = unsafePerformIO $ +isError :: a -> Bool +isError x = unsafePerformIO $ handle (\(_ :: SomeException) -> return True) $ do - let !_ = arg == res + let !_ = x return False +testEq :: Eq a => a -> a -> Bool +testEq res xpect = unsafePerformIO $ + handle (\(_ :: SomeException) -> return (xpect `ptrEq` throwsError && isError res)) $ do + let !rtv = res == xpect + return rtv + runTest :: Test -> Maybe Failure runTest (Test s f as) = case filter (not . f) as of [] -> Nothing From f308d9c374461c9d8420271a4d9b56cf83db2038 Mon Sep 17 00:00:00 2001 From: gcnew Date: Fri, 4 Nov 2016 22:28:39 +0200 Subject: [PATCH 5/8] Added homework #2 assignment --- README.md | 7 +++++++ exercises/lists/Lists.md | 13 +++++++++++++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index 05d895b..ebf7a46 100644 --- a/README.md +++ b/README.md @@ -1 +1,8 @@ +Домашно №2 +==== + +**ВАЖНО:** Вижте [lists](./exercises/lists/Lists.md) за Домашно №2 + +--- + # fp-haskell diff --git a/exercises/lists/Lists.md b/exercises/lists/Lists.md index 28d381d..6ee3de5 100644 --- a/exercises/lists/Lists.md +++ b/exercises/lists/Lists.md @@ -11,6 +11,19 @@ import Prelude hiding ( ``` --> +Домашно №2 +===== + +Имплементирайте следните функции: + - [elem](#elem) + - [sum](#sum) + - [replicate](#replicate) + - [interleave](#interleave) + +Бонус: всички останли. Започнте с тези за множества - [Списъци като множества](#Списъци-като-множества) + +--- + Работа със списъци ====== From 13c924f7f8d19a234ed150342d8babec1886db31 Mon Sep 17 00:00:00 2001 From: gcnew Date: Mon, 7 Nov 2016 13:48:43 +0200 Subject: [PATCH 6/8] Fixed examples - the examples now follow the check_lecture convention - fixed typos and other mistakes - Colorize.md now successfully passes check_lecture --- exercises/colorize/Colorize.md | 126 ++++++++++++++++++--------------- 1 file changed, 68 insertions(+), 58 deletions(-) diff --git a/exercises/colorize/Colorize.md b/exercises/colorize/Colorize.md index 1da1121..26bad02 100644 --- a/exercises/colorize/Colorize.md +++ b/exercises/colorize/Colorize.md @@ -1,3 +1,11 @@ + + Хаки МакХакер, син на най-уважаваните програмисти в Ламбда Ленд, тамън започва да се учи да програмира. Амбициозните му родители са му дали за задача да принтира цветен текст по терминала на Линукската му машина. Хаки, незнаейки как да се справи, е решил да потърси решение в интернет форумите. След любезно запитаване в БезкрайнаРекурсия, Ти си видял въпроса му. Вече преминал обичайните чудения дали да го downvote-неш, че пита нещо очевидно, или че е поредният ученик търсещ бързо решение на домашното си, виждаш че той е положил усиля, като е прочел в WikiLambdia и се е опитал да достигне до решение, но без успех. От прочита на WikiLambdia, Хаки е дефинирал няколко цветови константи и е написал заготовки ([stubs](https://en.wikipedia.org/wiki/Stub)) на функциите `colorize` и `bleach`. @@ -5,7 +13,7 @@ От описанието на Хаки и приложения [линк към статията](https://en.wikipedia.org/wiki/ANSI_escape_code), ставя ясно, че оформянето на текст работи по прост начин, с инструктиране на терминала посредством запазен символ (`Escape`) и последваща кодова комбинация (число). Още начинаещ в Haskell, нашият младеж не знае функцията за обърщане на число към `String` (низ от символи), затова е дефинирал следните две функции: -```hs +```hs getLastDigit :: Int -> Int dropLastDigit :: Int -> Int ``` @@ -13,14 +21,14 @@ dropLastDigit :: Int -> Int `getLastDigit` връща последната цифра на подадено дадено число. Или погледнато математически, остатъкът след деление на 10. Пр: ```hs -getLastDigit 12345 -> 5 +> getLastDigit 12345 +5 -getLastDigit 7 -> 7 +> getLastDigit 7 +7 -getLastDigit 0 -> 0 +> getLastDigit 0 +0 ``` Hint: В Haskell няма оператор, както в C-образните езици, за операцията `modulo`. За целта се ползва функцията `mod`. @@ -28,14 +36,14 @@ Hint: В Haskell няма оператор, както в C-образните `dropLastDigit` има точно противоположна функция на `getLastDigit` - тя връща число с премахната последна цифра. Т.е. цялата част при деление на 10. Обичайният оператор за деление `\` няма да свърши работа, понеже за разлика от в C, в Haskell той работи само върху рационални числа. ```hs -dropLastDigit 12345 -> 1234 +> dropLastDigit 12345 +1234 -dropLastDigit 7 -> 0 +> dropLastDigit 7 +0 -dropLastDigit 0 -> 0 +> dropLastDigit 0 +0 ``` @@ -48,17 +56,17 @@ dropLastDigit 0 Очакваното поведение е: ```hs -getReverseDigits 12345 -> [5, 4, 3, 2, 1] +> getReverseDigits 12345 +[5, 4, 3, 2, 1] -getReverseDigits (-12345) -> [5, 4, 3, 2, 1] +> getReverseDigits (-12345) +[5, 4, 3, 2, 1] -getReverseDigits 7 -> [7] +> getReverseDigits 7 +[7] -getReverseDigits 0 -> [0] +> getReverseDigits 0 +[0] ``` @@ -67,20 +75,20 @@ getReverseDigits 0 Трябва ни функция, която от обръща дадена цифра към символа й. Решението не е много елегантно, но Haskell не позволява събирането на число и буква. Затова се спираме на по-простия вариант с pattern-matching по цифрите и връщане на съответния символ. Така дефинирана функцията няма да е валидана за всеки произволен вход, което не е хубаво свойство. В литературата такива функции се наричат _"непълни"_ (_partial_) и Haskell ни предупреждава с warning. Решението е да наравим функцията _"пълна"_ (_total_), като добавим pattern, който приема всички останали стойности и хвърля грешка. Струва си да отбележим, че функцията продължава да не е добре дефинирана за произволен вход (т.е. нецифри), но от гледна точка на компилатора всички възможности са обработени. Използвайте следния pattern, като крайно условие: -```hs +```hs toChar _ = error "Not a digit" ``` Резултат от изпълнението: ```hs -toChar 1 -> '1' +> toChar 1 +'1' -toChar 9 -> '9' +> toChar 9 +'9' -toChar 10 -> *** Exception: Not a digit +> toChar 10 +*** Exception: Not a digit ``` @@ -88,13 +96,13 @@ toChar 10 Вече имаме всички нужни подфункции, за да напишем -```hs +```hs itoa :: Int -> String ``` Тук е времето и мястото да се справим с обратния ред на цифрите. За целта ще ползваме спомагателна функция: -```hs +```hs itoaLoop :: String -> [Int] -> String ``` @@ -104,23 +112,23 @@ itoaLoop :: String -> [Int] -> String Функцията `itoa` се дефинира тривиално посредством `itoaLoop`. Този прийом е много често използван във функционалното програмиране. Не е задължително спомагателната функция да използва акумулатор - понякога целта и е само емулиране на цикъл, а понякога акумулатори се ползват и извън рекурсивни функции :). -```hs -itoaLoop "" [3, 2, 1] +```hs +> itoaLoop "" [3, 2, 1] -> itoaLoop "3" [2, 1] -> itoaLoop "23" [1] -> itoaLoop "123" [] -> "123" +"123" ``` ```hs -itoa 12345 -> "12345" +> itoa 12345 +"12345" -itoa 7 -> "7" +> itoa 7 +"7" -itoa 0 -> "0" +> itoa 0 +"0" ``` @@ -140,7 +148,7 @@ mkTextStyle color = mkStyle (color + textStyle) ``` Вашата задача е да дефинирате функцията -```hs +```hs getStyle :: String -> String ``` @@ -164,17 +172,17 @@ getStyle :: String -> String Пример: ```hs -getStyle "bkl" -> "\x1B[30m" +> getStyle "blk" +"\x1B[30m" -getStyle "blu" -> "\x1B[34m" +> getStyle "blu" +"\x1B[34m" -getStyle "clr" -> "\x1B[0m" +> getStyle "clr" +"\x1B[0m" -getStyle "other" -> "" +> getStyle "other" +"" ``` @@ -183,19 +191,19 @@ getStyle "other" Всички нужни части от пъзела са вече налице. Един от ветераните съфорумец е написал функцията за премахване на стиловете - `bleach`, но не и на `colorize`. Всички са на мнение, че Хаки трябва да извърви последната миля сам. Дадена е подсказка, че `colorize` прилича много на `bleach`, но има някои съществени разлики. `colorize` приема `String` като входен аргумент и заменя всички обръщения към кодови думи с тяхната стойност. Кодовите думи са обградени с `<>`. Така например, ако входът е `"hello"`, то изходът трябва да бъде `"\x1B[30mhello"`. Ако ли пък е непозната комбинация, то тя трябва да остане непроменена. Обърнете внимание, че всички ключови думи са с точно три букви и са обградени със знаци. `colorize` не се нуждае от спомагателни функции, всичко нужно е вече налично. ```hs -colorize "hello" -> "\x1B[30mhello" +> colorize "hello" +"\x1B[31mhello\x1B[0m" -colorize "hello world" -> "\x1B[30mhello \x1B[34mworld\x1B[0m" +> colorize "hello world" +"\x1B[31mhello \x1B[34mworld\x1B[0m" -colorize "" -> "" +> colorize "" +"" ``` Принтирането на escape комбинациите не е много забавно. За да видите цветовете иползвайте функцията `putStrLn`, например: -```hs +```hs putStrLn (colorize "hello") ``` @@ -207,7 +215,7 @@ putStrLn (colorize "hello") Дадени са заготовки на функциите: -```hs +```hs mkBackgroundStyle :: Int -> String dropMarkup :: String -> String getMarkup :: String -> String @@ -224,9 +232,11 @@ colorize2 :: String -> String Пр: ```hs -getStyle "bgr-red" -> "\x1B[41m" +> getStyle "bgr-red" +"\x1B[41m" +``` +```hs putStrLn (colorize2 "black white") ``` From 6a5286e80fe95feda2fa08439a669a6a2ee39be8 Mon Sep 17 00:00:00 2001 From: gcnew Date: Wed, 9 Nov 2016 14:17:28 +0200 Subject: [PATCH 7/8] Added new lectures --- .gitignore | 1 + check_lecture.sh | 23 --- exercises/secret-lang/Secret-lang.hs | 4 +- extract.js | 53 ------- lectures/03-polymorphism/lcomprehension.md | 110 +++++++++++++ lectures/03-polymorphism/poly.md | 171 +++++++++++++++++++++ lectures/04-let-where/let-where.md | 103 +++++++++++++ 7 files changed, 387 insertions(+), 78 deletions(-) delete mode 100755 check_lecture.sh delete mode 100644 extract.js create mode 100644 lectures/03-polymorphism/lcomprehension.md create mode 100644 lectures/03-polymorphism/poly.md create mode 100644 lectures/04-let-where/let-where.md diff --git a/.gitignore b/.gitignore index a4ee41a..bfec11b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ cabal.sandbox.config *.eventlog .stack-work/ cabal.project.local +staging/ diff --git a/check_lecture.sh b/check_lecture.sh deleted file mode 100755 index 89e177d..0000000 --- a/check_lecture.sh +++ /dev/null @@ -1,23 +0,0 @@ -set -e - -if [ "$#" -ne 1 ]; then - echo "Syntax: $0 " - exit 1 -fi - -dir=$(dirname "$1") -build_dir="$dir/example_tests" -check_name="$build_dir/check" - -mkdir "$build_dir" || rm -rf "$build_dir/*" - -node --harmony_destructuring extract.js "$1" --tests > "$check_name.hs" - -ghc --make "$check_name.hs" -o "$check_name" - -echo "" -echo "" - -./"$check_name" - -rm -rf "$build_dir" diff --git a/exercises/secret-lang/Secret-lang.hs b/exercises/secret-lang/Secret-lang.hs index 61c1406..987c26d 100644 --- a/exercises/secret-lang/Secret-lang.hs +++ b/exercises/secret-lang/Secret-lang.hs @@ -1,6 +1,5 @@ import Data.Char(toLower) - isChar :: Char -> Bool isChar = undefined @@ -27,6 +26,7 @@ dropN :: Int -> String -> String dropN = undefined ---Assume we'll be decoding only valid words +-- Assume we'll be decoding only valid words decode :: String -> String decode = undefined + diff --git a/extract.js b/extract.js deleted file mode 100644 index 93ad76e..0000000 --- a/extract.js +++ /dev/null @@ -1,53 +0,0 @@ - -const fs = require('fs'); - -function fail(msg) { - console.error(msg || 'Snap! I failed!'); - process.exit(1); -} - -function partition(arr, predicate) { - return [ arr.filter(predicate), arr.filter(x => !predicate(x)) ]; -} - -const [flags, args] = partition(process.argv.slice(2), x => /^--/.test(x)); - -const withTests = flags.indexOf('--tests') !== -1; - -const fileName = args[0]; -fs.existsSync(fileName) || fail(`File "${fileName}" does not exist!`); - -const contents = fs.readFileSync(fileName, "utf-8"); - -const parts = []; -contents.replace(/```hs((?:.|\n)*?)```/g, (_, part) => parts.push(part)); - -const tests = []; - -const hsSrc = parts - .filter(val => /^\s*/.exec(val)[0].length < 5) - .map(val => val.replace(/\s*--.*/g, '')) - .map(val => val.replace(/^> *(.*)\n(.*)\n/gm, (_, expr, res) => { - tests.push([expr, res]); - return ''; - })) - .join('') - .replace(/\n{3,}/g, '\n\n'); - -const main = ` -main :: IO () -main = putStrLn $ printRes testAll - where printRes True = "Success!" - -`; - -const testAll = ` -testAll :: Bool -testAll = ${tests - .map(([expr, res]) => `(${expr}) == ${res} || error ${JSON.stringify(expr + ' == ' + res)}`) - .join('\n && ') -} -`; - -console.log(withTests ? hsSrc + main + testAll - : hsSrc); diff --git a/lectures/03-polymorphism/lcomprehension.md b/lectures/03-polymorphism/lcomprehension.md new file mode 100644 index 0000000..6302abf --- /dev/null +++ b/lectures/03-polymorphism/lcomprehension.md @@ -0,0 +1,110 @@ + + +List comprehension
+== + +
+
+
+ +Георги Наков, [nakov.gl at gmail com](mailto:nakov.gl+tues@gmail.com) +Марин Маринов, [marinov.ms+tues at gmail com](mailto:marinov.ms+tues@gmail.com) + + +Технологично училище "Електронни Системи" +9 Ноември 2016г. + +--- + +## List ranges + +- съкратен запис за списъци от последователни елементи +- работят за списъци от символи и числа +```hs +> [1..5] + [1, 2, 3, 4, 5] + +> ['X'..'Z'] + "XYZ" +``` +
+ +**Важно**: **Не използвайте** този запис, ако списъкът **e с реални** числа. + +--- + +## List Comprehension +- съкратен запис за построяване на *(по-сложни)* списъци +- вдъхновен от начина, по който множествата се описват в математиката +
+```hs +> [ x*2 | x <- [1..4]] + + [2, 4, 6, 8] + ``` + +--- + +## List comprehension +- С list compehension може да прилагаме функция върху всеки един елемент на списъка без явна рекурсия +```hs +> [toUpper c | c <- "hello"] + "HELLO" +``` +- С list compehension може да филтрираме елементите на списъка с произволни условия без явна рекурсия с guards +```hs +> [ x | x <- [1..10], odd x, mod x 3 == 0] + --всички нечетни числа между 1 и 10, кратни на 3 + [3, 9] +``` + Всички условия трябва да са едновременно изпълнени, за да бъде включен елементът в крайния списък. + +--- + +## List comprehension +- Може да взимаме няколко променливи, всяка взета от различен списък +```hs +> [[x, y, z] | x <- [1..3], y <- [10,11], z <- [20,21]] + + [[1, 10, 20], [1, 10, 21], [1, 11, 20], [1, 11, 21], + [2, 10, 20], [2, 10, 21], [2, 11, 20], [2, 11, 21], + [3, 10, 20], [3, 10, 21], [3, 11, 20], [3, 11, 21]] +``` +В този случай list comprehension-a работи като вложени for цикли: +``` + lst = [] + for each x in [1, 2, 3]: + for each y in [10, 11]: + for each z in [20, 21]: + append [x, y, z] to lst +``` + +--- + +## List comprehension +- Когато задаваме повече от един списък, може да използваме всички предишни променливи при дефиницията на списъка +```hs + > [ [x, y] | x <- [1..4], y <-[1..x]] + + [ [1, 1], + [2, 1], [2, 2], + [3, 1], [3, 2], [3, 3] + [4, 1], [4, 2], [4, 3], [4, 4] ] +``` +--- + +## List comprehension +- Може да вгнездваме list comprehension-и +```hs +removeEs :: [String]->[String] +removeEs someWords = + [ [c | c <- word, c /= 'е', c /= 'E'] + | word <- someWords]] + +> removeЕs ["These", "are", "some", "good", "words"] + + ["Ths", "r", "som", "good", "words"] +``` diff --git a/lectures/03-polymorphism/poly.md b/lectures/03-polymorphism/poly.md new file mode 100644 index 0000000..6b772e4 --- /dev/null +++ b/lectures/03-polymorphism/poly.md @@ -0,0 +1,171 @@ + + +Полиморфични функции
+== + +
+
+
+ +Георги Наков, [nakov.gl at gmail com](mailto:nakov.gl+tues@gmail.com) +Марин Маринов, [marinov.ms+tues at gmail com](mailto:marinov.ms+tues@gmail.com) + + +Технологично училище "Електронни Системи" +9 Ноември 2016г. + +--- + +## Полиморфични функции + +Проблем, **oгромен проблем**: + +Досега видяхме, че ако ни се налага да работим с различни видове списъци, трябва да пишем функциите по няколко пъти с различни сигнатури, но еднакви дефиниции. +
+```hs +lenght :: [Int] -> Int +length [] = 0 +length (_:xs) = 1 + length xs + +lengthString :: String -> Int +lengthString [] = 0 +lengthString (_:xs) 1 + lengthString xs +``` + +Това е ужасно! + +--- + +## Полиморфични функции + +Haskell предоставя решение: + +Когато не ни интересува конкретния тип може вместо него да използваме т. нар *типова променлива*. +```hs +length :: [a] -> Int +length [] = 0 +length (_:xs) = 1 + length xs +``` +Тук `a` значи "да е който и да е тип". +Конкретните типове започват с главна буква, *типовите променливи **започват с малка буква** (и по конвенция често са с по една буква)*. +Функциите с поне една типова променлива се наричат *полиморфични*. + +--- + +## Полиморфични функции - примери + +Вградените функции за манипулация на списъци в Haskell работят с списъци от какъвто и да е тип! Да проверим сигнатурите: +```hs +> head [1, 2, 3] +1 +> head "Hi" +'H' + +ghci>:t head +head :: [a] -> a + +ghci>:t reverse +reverse :: [a] -> [a] + +ghci>:t (++) +(++) :: [a] -> [a] -> [a] +``` +--- + +## Полиморфични функции - начин на работа + +Не може да предполагаме нищо за аргументите или променливите, които са от тип `a`. Не може да извършваме операции с тях. +```hs +bad :: a -> Int +bad x = x + 1 +-- грешка +-- Couldn't match expected type ‘Int’ +-- with actual type ‘a’ +``` +--- + +## Полиморфични функции - начин на работа + +В рамките на едно извикване на полиморфичните функции, `a` заема конкретен тип, който не се сменя по време на изпълнението на функцията. `a` има само един конкретен тип в рамките на извикването *(тоест не може при първия аргумент да е `Char`, вторият - `Int` и тн)*. + +```hs +ghci> :t (++) +(++) :: [a] -> [a] -> [a] + +ghci> :t ("Hi" ++ " all") +"Hi" ++ " all" :: [Char] +``` +--- + +## Полиморфични функции - абстракции + +Въпреки, че полиморфичните функции повишават нивото на абстракция, те често правят нещата по-очевидни и ясни. +Пример: + +Какво може да кажем за `func :: Int -> String -> Int`? По колко начина може да я имплементираме? +Не знаем нищо - *може да връщаме дължината на низа, може дължината на низа на квардрат, може да връщаме 42, може да взимаме първите n символа и да умножаваме ASCII кодовете* + +--- + +## Полиморфични функции - абстракции +Какво може да кажем за `func :: a -> b -> а`? По колко начина може да я имплементираме? +*Досещаме се за очевидна имплементация : +`func x y = y` - функция, която винаги връща втория си аргумент.* + +--- + +## Ограничения + +В някои случаи искаме да използваме полиморфична функция, но и някакво свойство на `a`. +```hs +elem :: a -> [a] -> Bool +elem _ [] = False +elem e (x:xs) + | x == e = True + | otherwise = elem e xs +``` +За съжаление това е невалидна функция в Haskell, защото сме допуснали, че можем да сравним `e` с елемент от списъка `x`. + +x == e - Невалидно, защото извършваме операции с променливи от тип `a`. + +--- +## Oграничения + +Решение: Може да кажем, че `a` трябва да поддържа сравнение за равенство с `(Eq a) =>` пред сигнатурата. +```hs +elem :: (Eq a) => a -> [a] -> Bool +``` +`(Eq a) =>` наричаме класови ограничение *(class constraint)*. + +--- + +## Oграничения + +Haskell ни дава много готови класови ограничения. +- `Eq a` - може да сравняваме с `==` и `/=` +```hs +equals :: (Eq a) => a -> a -> Bool +equals x y = x == y + +> equals 3 3 +True +> equals "Hi" "hi" +False +``` +--- +## Oграничения + +- `Show a` - типът може да се конвертира към низ с `show` +```hs +toString :: (Show a) => a -> String +toString x = show x + +>toString 4 +"4" +>toString [42,0] +"[42, 0]" +``` +Вградените стандартни типове (числа, низ, симвoли) поддържат и `Eq a` и `Show a`. \ No newline at end of file diff --git a/lectures/04-let-where/let-where.md b/lectures/04-let-where/let-where.md new file mode 100644 index 0000000..ad7d3dd --- /dev/null +++ b/lectures/04-let-where/let-where.md @@ -0,0 +1,103 @@ + + +Локални променливи
let и where +== + +
+ +Георги Наков, [nakov.gl at gmail com](mailto:nakov.gl+tues@gmail.com) +Марин Маринов, [marinov.ms+tues at gmail com](mailto:marinov.ms+tues@gmail.com) + +Технологично училище "Електронни Системи" +19 Октомври 2016г. + +--- + +## Защо локални променливи +
+ +Локалните променливи са нужни за: + - запазване на междинни стойности в сложни функции + - създаване на локални, недостъпни отвън helper functions + +--- + +## Локални променливи - синтаксис + +
Два възможни вариантa: + +**let** синтаксис: +```hs +sumTimesTen x y = let s = x + y + in s * 10 +``` + +**where** синтаксис: +```hs +sumTimesTen' x y = s * 10 + where s = x + y +``` +
**Важно:** В идиоматичния Haskell `where` е по-често срещан `let`. + +--- + +## Локални променливи - guards + +Променливите въведени с `where` могат да бъдат ползвани и в guards на съответната дефиниция. + +
+ +```hs +passwordStrength :: String -> String +passwordStrength [] = "Please enter a password" + +passwordStrength pwd | len < 5 = "Weak" + | len < 8 = "Medium" + | otherwise = "Strong" + + where len = length pwd +``` + +--- + +## Локални променливи - видимост + +Редът на деклариране на променливите не е от значение. Всяка една от въведените променливи, може да реферира всяка друга (също както top-level дефинициите в сорс файла). + +
+ +```hs +multSign :: [Int] -> String +multSign ints + | length ints /= 2 = "Don't know" + | isXNeg == isYNeg = "Positive" + | otherwise = "Negative" + + where isXNeg = x < 0 + isYNeg = y < 0 + + [x, y] = ints +``` + +**Важно:** Подравняването е от критично значение! + +--- + +## Локални променливи - go функция + +
+ +_helper_ функциите са идеален кандидат за локална променлива, тък като в повечето случаи те не са универсално приложими и очакват входа да е предварително валидиран. + +
+ +```hs +maximum' :: [Int] -> Int +maximum' [] = error "empty list" +maximum' xs = go xs + where go [m] = m + go (y:ys) = max y (go ys) +``` From c48c6b3660bd9b1429acba58cb5198a8ec08411d Mon Sep 17 00:00:00 2001 From: gcnew Date: Sat, 12 Nov 2016 13:55:33 +0200 Subject: [PATCH 8/8] Added new Readme, a homewrok exercice and other improvements --- README.md | 24 +- exercises/colorize/Colorize.md | 3 + exercises/list-comp/listcomp.hs | 31 ++ exercises/list-comp/listcomp.md | 28 ++ exercises/lists/Lists.md | 13 - exercises/lists/ListsHW.md | 10 + exercises/permutations/perm.hs | 7 + exercises/permutations/perm.md | 21 + exercises/secret-lang/Secret-lang.md | 13 +- lectures/{Readme.md => 00-setup/extra.md} | 0 lectures/00-setup/setup.md | 6 +- lectures/01-intro/intro.md | 12 +- .../02-syntax-and-types/syntax-and-types.md | 365 ++++++++++++++++++ .../linked-list.png | Bin .../lists-guards-patterns.md} | 353 +---------------- .../lcomprehension.md | 34 +- .../let-where.md | 0 .../poly.md | 42 +- 18 files changed, 548 insertions(+), 414 deletions(-) create mode 100644 exercises/list-comp/listcomp.hs create mode 100644 exercises/list-comp/listcomp.md create mode 100644 exercises/lists/ListsHW.md create mode 100644 exercises/permutations/perm.hs create mode 100644 exercises/permutations/perm.md rename lectures/{Readme.md => 00-setup/extra.md} (100%) create mode 100755 lectures/02-syntax-and-types/syntax-and-types.md rename lectures/{02-basics => 03-lists-guards-patterns}/linked-list.png (100%) rename lectures/{02-basics/basics.md => 03-lists-guards-patterns/lists-guards-patterns.md} (54%) mode change 100755 => 100644 rename lectures/{03-polymorphism => 04-list-comprehension}/lcomprehension.md (88%) rename lectures/{04-let-where => 05-polymorphism-let-where}/let-where.md (100%) rename lectures/{03-polymorphism => 05-polymorphism-let-where}/poly.md (94%) diff --git a/README.md b/README.md index ebf7a46..e4a990a 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,24 @@ -Домашно №2 -==== +- **Седмица 1 (19.10.2016)** +[Относно курса](lectures/00-setup/setup.md) +[Що е то функционално програмиране? История на Haskell](lectures/01-intro/intro.md) +[Въведение в Haskell. Основени типове и синтаксис.](lectures/02-syntax-and-types/syntax-and-types.md) -**ВАЖНО:** Вижте [lists](./exercises/lists/Lists.md) за Домашно №2 +- **Седмица 2 (26.10.2016)** +[Въведение част 2 Lists, Guards and Pattern matching](lectures/03-lists-guards-patterns/lists-guards-patterns.md) +Домашно: [Езика на разбойниците](exercises/secret-lang/Secret-lang.md) ([secret-lang](exercises/secret-lang)) + +- **Седмица 3 (02.11.2016)** +[Работа със списъци](exercises/lists/Lists.md) +Задачи от часа: [Lists.hs](exercises/lists/Lists.hs), [Colorize](exercises/colorize/Colorize.md) ([colorize](exercises/colorize)) +Домашно: [Домашно Lists](exercises/lists/ListsHW.md) ([lists](exercises/lists)) + +- **Седмица 4 (09.11.2016)** +[List comprehension ](lectures/04-list-comprehension/lcomprehension.md) +Задачи от часа: [listcomp.md](exercises/list-comp/listcomp.md) ([list-comp](exercises/list-comp)) +Домашно: [Пермутации](exercises/permutations/perm.md) ([permutations](exercises/permutations)) --- -# fp-haskell +**Допълнителни материали** +[Литература, среда и др.](lectures/00-setup/extra.md) + diff --git a/exercises/colorize/Colorize.md b/exercises/colorize/Colorize.md index 26bad02..a0ea3c7 100644 --- a/exercises/colorize/Colorize.md +++ b/exercises/colorize/Colorize.md @@ -6,6 +6,9 @@ import Colorize hiding (mkStyle, mkTextStyle) ``` --> +Colorize +==== + Хаки МакХакер, син на най-уважаваните програмисти в Ламбда Ленд, тамън започва да се учи да програмира. Амбициозните му родители са му дали за задача да принтира цветен текст по терминала на Линукската му машина. Хаки, незнаейки как да се справи, е решил да потърси решение в интернет форумите. След любезно запитаване в БезкрайнаРекурсия, Ти си видял въпроса му. Вече преминал обичайните чудения дали да го downvote-неш, че пита нещо очевидно, или че е поредният ученик търсещ бързо решение на домашното си, виждаш че той е положил усиля, като е прочел в WikiLambdia и се е опитал да достигне до решение, но без успех. От прочита на WikiLambdia, Хаки е дефинирал няколко цветови константи и е написал заготовки ([stubs](https://en.wikipedia.org/wiki/Stub)) на функциите `colorize` и `bleach`. diff --git a/exercises/list-comp/listcomp.hs b/exercises/list-comp/listcomp.hs new file mode 100644 index 0000000..2c2bc23 --- /dev/null +++ b/exercises/list-comp/listcomp.hs @@ -0,0 +1,31 @@ +{-# OPTIONS_GHC -Wall #-} +module ListComp where + +myLength :: [Int] -> Int +myLength list = sum [ 1 | _ <- list] + +myElem :: Int -> [Int] -> Bool +myElem e list = not (null [x | x <- list, x == e]) + +myElem':: Int -> [Int] -> Bool +myElem' e l = and [True | x <- l, x == e] + +myElem'':: Int -> [Int] -> Bool +myElem'' e l = or [ x == e | x <- l] + +count :: Int -> [Int] -> Int +count e l = myLength [x | x <- l, x == e] + + +arithmeticSeries :: Int -> Int -> Int -> [Int] +arithmeticSeries a incrementator n = [a + x*incrementator | x <- [0..n-1]] + +arithmeticSum :: Int -> Int -> Int -> Int +arithmeticSum a incrementator n = sum (arithmeticSeries a incrementator n) + +geometricSeries :: Double -> Double -> Int -> [Double] +geometricSeries a q n = [a*q^x | x <- [0..n-1]] + +geometricSum :: Double -> Double -> Int -> Double +geometricSum a q n = sum (geometricSeries a q n) + diff --git a/exercises/list-comp/listcomp.md b/exercises/list-comp/listcomp.md new file mode 100644 index 0000000..699874b --- /dev/null +++ b/exercises/list-comp/listcomp.md @@ -0,0 +1,28 @@ +# List comprehensions + +- *Трансформацията с функция* на списък рекурсивно: + ```hs + f :: [Int] -> [Int] + f [] = [] + f (x:xs) = (някаквa-функция x) : f xs + ``` + e равносилна на: + ```hs + [някаква функция x | x <- входния-списък] + ``` +
+- Филтрацията на списък рекурсивно: + ```hs + g :: [Int] -> [Int] + g [] = [] + g (x:xs) + | някакво услове за x = g xs + | otherwise = x : g xs + ``` + е равносилна на: + ```hs + [ x | x<- входен списък, някакво услове за х] + ``` + + +### **[Кодът](listcomp.hs)**, който писахме в клас. diff --git a/exercises/lists/Lists.md b/exercises/lists/Lists.md index 6ee3de5..28d381d 100644 --- a/exercises/lists/Lists.md +++ b/exercises/lists/Lists.md @@ -11,19 +11,6 @@ import Prelude hiding ( ``` --> -Домашно №2 -===== - -Имплементирайте следните функции: - - [elem](#elem) - - [sum](#sum) - - [replicate](#replicate) - - [interleave](#interleave) - -Бонус: всички останли. Започнте с тези за множества - [Списъци като множества](#Списъци-като-множества) - ---- - Работа със списъци ====== diff --git a/exercises/lists/ListsHW.md b/exercises/lists/ListsHW.md new file mode 100644 index 0000000..9a8cf80 --- /dev/null +++ b/exercises/lists/ListsHW.md @@ -0,0 +1,10 @@ +Домашно Lists +===== + +Имплементирайте следните функции: + - [elem](Lists.md#elem) + - [sum](Lists.md#sum) + - [replicate](Lists.md#replicate) + - [interleave](Lists.md#interleave) + +Бонус: всички останли. Започнте с тези за множества - [Списъци като множества](Lists.md#Списъци-като-множества) diff --git a/exercises/permutations/perm.hs b/exercises/permutations/perm.hs new file mode 100644 index 0000000..12547ba --- /dev/null +++ b/exercises/permutations/perm.hs @@ -0,0 +1,7 @@ +module Permutations where + +import Data.List hiding (permutations) + +permutations :: [Int] -> [[Int]] +permutations = undefined + diff --git a/exercises/permutations/perm.md b/exercises/permutations/perm.md new file mode 100644 index 0000000..02e8529 --- /dev/null +++ b/exercises/permutations/perm.md @@ -0,0 +1,21 @@ + +Пермутации +==== + +## Описание + +Имплементерайте функцията `permutations:: [Int] -> [[Int]]`, която приема списък от числа и връща всички възможни [пермутации](https://www.mathsisfun.com/definitions/permutation.html). + + +## Примерен вход и изход + +```hs +>permutations [1,2,3] + +[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] +``` +## Бонус +Напишете функцията, като използвате най-много 200 символа (и без да сменяте името)! + +### Насока +Броят на всички възможни пермутации на лист с `n` елемента е `n!`. Знаем, че `0! = 1`, какво трябва да връщаме при `permutations []`? diff --git a/exercises/secret-lang/Secret-lang.md b/exercises/secret-lang/Secret-lang.md index 21289eb..eeaf73c 100644 --- a/exercises/secret-lang/Secret-lang.md +++ b/exercises/secret-lang/Secret-lang.md @@ -1,4 +1,7 @@ -# Описание +Езика на разбойниците +==== + +## Описание Много малки деца си измислят и използват *супер тайни езици*, за да крият информация и объркват родителите си. По всяка вероятност всеки може да се сети за поне няколко такива (например *пилешки говор* на български); явлението е толкова разпространено, че Wikipedia дори има [списък на най-известните такива езици](https://en.wikipedia.org/wiki/Language_game#List_of_common_language_games).

@@ -9,19 +12,19 @@ Напишете програма, коята кодира низ нормален текст на тайния Rövarspråket. -# Примерен вход и изход +## Примерен вход и изход Вход: `I'm speaking Robber's language!` Изход: `I'mom sospopeakokinongog Rorobobboberor'sos lolanongoguagoge!` -# Бонус #1 +## Бонус #1 Уверете се, че програмата ви работи правилно с главни букви. Така например `Hello` трябва да се преведе до `Hohelollolo`, а не `HoHelollolо`. Вижте функцията `toLower` в модула `Data.Char` и как може да я импортирате. -# Бонус #2** +## Бонус #2** Напишете функция, която декодира низ обратно към нормален език *(допуснете, че даденият текст е във валиден Rövarspråket)*. Замислете се как бихте имплементирали и използвали функция `dropN`, която пропуска първите *n* символа от низ. -# Бележки +## Бележки След като сте разкрили една шведска тайна, е време да преминете към следващата - [Surströmming](https://www.youtube.com/watch?v=wapOib5u8a8) (чудя се това дали изобщо да го има). diff --git a/lectures/Readme.md b/lectures/00-setup/extra.md similarity index 100% rename from lectures/Readme.md rename to lectures/00-setup/extra.md diff --git a/lectures/00-setup/setup.md b/lectures/00-setup/setup.md index d12490c..ae1ba4f 100644 --- a/lectures/00-setup/setup.md +++ b/lectures/00-setup/setup.md @@ -3,8 +3,8 @@ *page_number:false --> -Функционално Програмиране с Haskell -== +Относно курса +==== Георги Наков, [nakov.gl at gmail com](mailto:nakov.gl+tues@gmail.com) Марин Маринов, [marinov.ms+ tues at gmail com](mailto:marinov.ms+tues@gmail.com) @@ -39,4 +39,4 @@ extra-include-dirs: C:\Program Files\Haskell Platform\7.10.3\mingw\include ``` - *(не препоръчваме да използвате Windows)* \ No newline at end of file + *(не препоръчваме да използвате Windows)* diff --git a/lectures/01-intro/intro.md b/lectures/01-intro/intro.md index d3e3365..416eb80 100644 --- a/lectures/01-intro/intro.md +++ b/lectures/01-intro/intro.md @@ -3,10 +3,8 @@ *page_number:false --> -Въведение във -Функционалното Програмиране. -История на Haskell -== +Що е то функционално програмиране?
История на Haskell +====

@@ -82,11 +80,11 @@ - резултатите от функции **не** се изчисляват преди да се необходими - практически неприложимо при функции със странични ефекти ```hs -f x y z = - if x>0 then x+y else x+z +f x y z = if x>0 then x+y else x+z -f (5*6) (10-7) (5/0) +> f (5*6) (10-7) (5/0) -- не се хвърля грешка +33 ``` --- ## Мързеливо оценяване diff --git a/lectures/02-syntax-and-types/syntax-and-types.md b/lectures/02-syntax-and-types/syntax-and-types.md new file mode 100755 index 0000000..c457f58 --- /dev/null +++ b/lectures/02-syntax-and-types/syntax-and-types.md @@ -0,0 +1,365 @@ + + +Въведение в Haskell.
Основени типове и синтаксис. +==== + +
+
+
+ +Георги Наков, [nakov.gl at gmail com](mailto:nakov.gl+tues@gmail.com) +Марин Маринов, [marinov.ms+tues at gmail com](mailto:marinov.ms+tues@gmail.com) + +Технологично училище "Електронни Системи" +19 Октомври 2016г. + +--- + +## Какво е Haskell + + - стриктно типуван + - (lazy) pure functional language + - с кратък, странен, но елегантен и удобен синтаксис + +--- + +## Деклариране на променливи + +```hs +i :: Int +i = 3 + +c :: Char +c = 'c' + +d :: Double +d = 3.0 +``` + +--- + +## Анатомия на дефинициите - сигнатурата + + +```hs +i :: Int +``` + + - деклариране на променлива `i` + - `::` оператор, който означава _има тип_ + - самият тип - Int + +--- + +## Анатомия на дефинициите - променливата + + +```hs +i = 3 +i = 4 -- compile-time error +``` + + - в Haskell променливите не са "променливи", а дефиниция или наименование на стойност - един път зададена такава, тя не може да бъде мутирана или повторно задавана + - операторът за присвояване е `=` + +--- + +## Type Inference + +Haskell сам може да открие типовете, без да му бъдат подадени. Програмата винаги е добре типувана (дори при изпуснати сигнатури) и ако има грешки или двусмислие ще бъдат хванати при компилация. +
+ +```hs +c' = 'e' -- inferred as Char +``` + +
**Добра практика**: винаги пишете сигнатурите на всички не-вгнездени дефиниции + +--- + +## Основни типове + +| Тип | Стойности | +|----------------------------|-------------------------| +| Bool | `True`, `False` | +| Int (32/64 bit) | `0`, `1`, `2`, `3`, .. | +| Double | `0.0`, `0.5`, `1.33` | +| Char | `'a'`, `'b'`, `'c'` | +| Integer - произволно голям | `1234567890123456789..` | + +
**Забележка:** Синтаксисът на езика изисква типовете винаги да започват с главна буква. Променливите (било то константи или имена на функции), задължително започват с малка. + +--- + +## Именоване на променливи + + - винаги започват с малка буква + - състоят се от unicode букви, цифри и символът `'` + - символът `'` (секонд) се използва са повторна/спомагателна дефиниция + - по конвенция се ползва camelCase пред snake_case +
+ +```hs +fairRandom = 4 + +diceRoll = 6 +diceRoll' = 3 + +theZCharacter = 'z' +``` + +--- + +## Булеви алгебра + +Haskell използва познатите оператори за логическите операции. + +`&&` - логическо и +`||` - логическо или +`not` - логическо отрицание, не е оператор, а функция + +
+ +```hs +> True && False +False + +> True || False +True + +> not True +False +``` + +--- + +## Оператори за сравнение + + +| Оператор | Значение | +|----------|---------------------| +| `==` | равно / еднакво | +| `/=` | неравно / различно | +| `<` | по-малко | +| `>` | по-голямо | +|`<=` | по-малко или равно | +|`>=` | по-голямо или равно | + + +
**Забележка:** Сравнителните оператори в Haskell са същите като в повечето други езици, с изключение на различно - `/=`. + +--- + +## Операции върху числа + +Аритметичните операции са в голямата си част както в другите езици и притежават обичайните свойства - `+`, `-`, `*` + +```hs +> 3 + 4 * 5 +-- 3 + (4 * 5) +23 + +> 1 + (-2) - 3 +-4 +``` + +
**Hint:** Обграждайте отрицателните числа в скоби (пр. `(-2)`). В противен случай на много места компилаторът ще се оплаква със странни грешки. + +--- + +## Целочислено деление + +Делението на цели числа става посредством функциите `div` и `quot`: + +```hs +> quot 10 5 +2 + +> quot (-4) 3 +-1 + +> div (-4) 3 +-2 +``` + +
**Hint:** използвайте `quot` за (интуитивно) поведение като в C. Разликата между двата операторa е само при деление на числа с различни знаци. + +--- + +## Нецелочислено деление + +Операторът за нецелочислено деление е познатият `/` +```hs +> 3 / 4 +0.75 + +> (-5) / 2 +-2.5 + +> 10 / 2.5 +4.0 +``` + +--- + +## Работа с функции - извикване + + - извикването на функция става без скоби + - параметрите се подават в реда, в който функцията ги очаква, разделени с празно (whitespace) + - извикването на функция е операцията с най-висок приоритет + - ако е нужна промяна на приоритета се използват скоби + +```hs +> quot 4 2 +2 + +> quot 20 2 * 5 + -- (quot 20 2) * 5 +50 + +> quot 20 (2 * 5) + -- quot 20 10 +2 +``` + +--- + +## Сигнатура на функция + +```hs +quot :: Int -> Int -> Int + | | | | + name arg1 arg2 result +``` + +Сигнатурата на функция представлява изброени аргументите, които тя приема, последвани от резултатния тип, разделени със знака `->` помежду им. + +**Как се чете:** `quot` е функция която приема два аргумента от тип `Int` и връща като резултат `Int`. + +--- +## Сигнатура на функция +
+ +```hs +parseNumber :: String -> Int +``` + +Е функция която приема `String` и връща `Int`. +
+ +```hs +mishmash :: Int -> Bool -> String -> CustomType + +> mishmash 10 False "string" +-- some value of type CustomType +``` + +--- + +## Дефиниране на функция +
+ +Синтаксис: +```hs +funtionName arg1 arg2 argN = definition +``` + +
Пример: +```hs +add3 :: Int -> Int -> Int -> Int +add3 x y z = x + y + z + +> add3 100 1000 (-1) +1099 +``` + +--- + +## GHCi + +Това е интерактивен Haskell REPL. Повечето програми, които ще пишем, няма да компилираме до executables, а ще ги използваме през GHCi с цел по-лесна, интерактивна и бърза работа. Haskell е известен с относително сложния си модел на IO. Нека това не ви безпокои, нито отказва. Ще стигнем до там, но преди това трябва да се заредим с по-базовите средства и концепции :) + +--- + +## GHCi - команди + +`:l ` - служи за зареждане на сорс файла достъпен на съответния път + +```text +:l Demo.hs +[1 of 1] Compiling Demo ( Demo.hs, interpreted ) +Ok, modules loaded: Demo. +``` + +
`:r` - презареждане на вече заредени сорсове +```text +:r +[1 of 1] Compiling Demo ( Demo.hs, interpreted ) +Ok, modules loaded: Demo. +``` + +--- + +## GHCi - команди + +`:t ` - показва типа на съответния _expression_ + +```text +> :t True +True :: Bool + +> :t 10 +10 :: Num a => a + +> :t (2 + 2) +(2 + 2) :: Num a => a + +> :t words +words :: String -> [String] +``` + +--- + +**Важно:** не се притеснявайте, че типът на числата не е `Int`, а `Num a => a`. Това е генерализиран тип за число. Не се плашете и от малките букви! В следващите няколко урока ще стигнем до тях - с времето и вие ще станете тъмни магьосници, но първо трябва да минем базовите неща! + +--- + +## GHCi - команди + +`:i ` - показа информация за дадената променлива с име _identifier_, какъв й е типът и къде е дефинирана + +
+ +```text +> :i words + +words :: String -> [String] + -- Defined in ‘base-4.8.2.0:Data.OldList’ +``` + +--- + +## GHCi - други полезни команди + +Тези команди са общовалидни за терминала и са част от така нареченият emacs mode. Повечето приложения, които има някаква форма на интеракция, използват именно тези shortcuts. + +--- + +| Команда | Използване | +|----------|---------------------------------------------| +| tab | допълване / completion | +| Ctrl + r | reverse search - търси в историята на вече въвежданите команди | +| Ctrl + a | изместване на курсора в началото на реда | +| Ctrl + e | изместване на курсора в края на реда | +| Ctrl + w | изтриване на думата, при/преди курсора | +| Ctrl + d | спира текущия shell / command line програма | +| Ctrl + l | почиства екрана | + +--- + +## GHCi - последни думи + +Не използвайте GHCi за големи дефиниции (примерно на функции). GHCi не работи добре за въвеждане на многоредов код, затова е препоръчително кодът да е във файл, а в GHCi само да бъде презареждан и отделните функции тествани. Разглеждане на типове, малки експерименти и тестове са силните страни на всеки REPL. diff --git a/lectures/02-basics/linked-list.png b/lectures/03-lists-guards-patterns/linked-list.png similarity index 100% rename from lectures/02-basics/linked-list.png rename to lectures/03-lists-guards-patterns/linked-list.png diff --git a/lectures/02-basics/basics.md b/lectures/03-lists-guards-patterns/lists-guards-patterns.md old mode 100755 new mode 100644 similarity index 54% rename from lectures/02-basics/basics.md rename to lectures/03-lists-guards-patterns/lists-guards-patterns.md index b537825..51d663e --- a/lectures/02-basics/basics.md +++ b/lectures/03-lists-guards-patterns/lists-guards-patterns.md @@ -3,8 +3,8 @@ *page_number:false --> -Въведение в Haskell
Основен синтаксис и типове -== +Въведение част 2
Lists, Guards and Pattern matching +====

@@ -14,269 +14,10 @@ Марин Маринов, [marinov.ms+tues at gmail com](mailto:marinov.ms+tues@gmail.com) Технологично училище "Електронни Системи" -19 Октомври 2016г. +26 Октомври 2016г. --- -## Какво е Haskell - - - стриктно типуван - - (lazy) pure functional language - - с кратък, странен, но елегантен и удобен синтаксис - ---- - -## Деклариране на променливи - -```hs -i :: Int -i = 3 - -c :: Char -c = 'c' - -d :: Double -d = 3.0 -``` - ---- - -## Анатомия на дефинициите - сигнатурата - - -```hs -i :: Int -``` - - - деклариране на променлива `i` - - `::` оператор, който означава _има тип_ - - самият тип - Int - ---- - -## Анатомия на дефинициите - променливата - - -```hs -i = 3 -i = 4 -- compile-time error -``` - - - в Haskell променливите не са "променливи", а дефиниция или наименование на стойност - един път зададена такава, тя не може да бъде мутирана или повторно задавана - - операторът за присвояване е `=` - ---- - -## Type Inference - -Haskell сам може да открие типовете, без да му бъдат подадени. Програмата винаги е добре типувана (дори при изпуснати сигнатури) и ако има грешки или двусмислие ще бъдат хванати при компилация. -
- -```hs -c' = 'e' -- inferred as Char -``` - -
**Добра практика**: винаги пишете сигнатурите на всички не-вгнездени дефиниции - ---- - -## Основни типове - -| Тип | Стойности | -|----------------------------|-------------------------| -| Bool | `True`, `False` | -| Int (32/64 bit) | `0`, `1`, `2`, `3`, .. | -| Double | `0.0`, `0.5`, `1.33` | -| Char | `'a'`, `'b'`, `'c'` | -| Integer - произволно голям | `1234567890123456789..` | - -
**Забележка:** Синтаксисът на езика изисква типовете винаги да започват с главна буква. Променливите (било то константи или имена на функции), задължително започват с малка. - ---- - -## Именоване на променливи - - - винаги започват с малка буква - - състоят се от unicode букви, цифри и символът `'` - - символът `'` (секонд) се използва са повторна/спомагателна дефиниция - - по конвенция се ползва camelCase пред snake_case -
- -```hs -fairRandom = 4 - -diceRoll = 6 -diceRoll' = 3 - -theZCharacter = 'z' -``` - ---- - -## Булеви алгебра - -Haskell използва познатите оператори за логическите операции. - -`&&` - логическо и -`||` - логическо или -`not` - логическо отрицание, не е оператор, а функция - -
- -```hs -> True && False -False - -> True || False -True - -> not True -False -``` - ---- - -## Оператори за сравнение - - -| Оператор | Значение | -|----------|---------------------| -| `==` | равно / еднакво | -| `/=` | неравно / различно | -| `<` | по-малко | -| `>` | по-голямо | -|`<=` | по-малко или равно | -|`>=` | по-голямо или равно | - - -
**Забележка:** Сравнителните оператори в Haskell са същите като в повечето други езици, с изключение на различно - `/=`. - ---- - -## Операции върху числа - -Аритметичните операции са в голямата си част както в другите езици и притежават обичайните свойства - `+`, `-`, `*` - -```hs -> 3 + 4 * 5 --- 3 + (4 * 5) -23 - -> 1 + (-2) - 3 -0 -``` - -
**Hint:** Обграждайте отрицателните числа в скоби (пр. `(-2)`). В противен случай на много места компилаторът ще се оплаква със странни грешки. - ---- - -## Целочислено деление - -Делението на цели числа става посредством функциите `div` и `quot`: - -```hs -> quot 10 5 -2 - -> quot (-4) 3 --1 - -> div (-4) 3 --2 -``` - -
**Hint:** използвайте `quot` за (интуитивно) поведение като в C. Разликата между двата операторa е само при деление на числа с различни знаци. - ---- - -## Нецелочислено деление - -Операторът за нецелочислено деление е познатият `/` -```hs -> 3 / 4 -0.75 - -> (-5) / 2 --2.5 - -> 10 / 2.5 -4.0 -``` - ---- - -## Работа с функции - извикване - - - извикването на функция става без скоби - - параметрите се подават в реда, в който функцията ги очаква, разделени с празно (whitespace) - - извикването на функция е операцията с най-висок приоритет - - ако е нужна промяна на приоритета се използват скоби - -```hs -> quot 4 2 -2 - -> quot 20 2 * 5 - -- (quot 20 2) * 5 -50 - -> quot 20 (2 * 5) - -- quot 20 10 -2 -``` - ---- - -## Сигнатура на функция - -```hs -quot :: Int -> Int -> Int - | | | | - name arg1 arg2 result -``` - -Сигнатурата на функция представлява изброени аргументите, които тя приема, последвани от резултатния тип, разделени със знака `->` помежду им. - -**Как се чете:** `quot` е функция която приема два аргумента от тип `Int` и връща като резултат `Int`. - ---- -## Сигнатура на функция -
- -```hs -parseNumber :: String -> Int -``` - -Е функция която приема `String` и връща `Int`. -
- -```hs -mishmash :: Int -> Bool -> String -> CustomType - -> mishmash 10 False "string" --- some value of type CustomType -``` - ---- - -## Дефиниране на функция -
- -Синтаксис: -```hs -funtionName arg1 arg2 argN = definition -``` - -
Пример: -```hs -add3 :: Int -> Int -> Int -> Int -add3 x y z = x + y + z - -> add3 100 1000 (-1) -1099 -``` - ---- ## Дефиниране на частни случаи Може да имаме отделни случаи (с различно тяло) за конкретни стойности на аргументите на функция. @@ -674,91 +415,3 @@ reverseKeys [] = [] > reverseKeys "This is a Killer key" "This is a liKler yek" ``` - ---- - -## GHCi - -Това е интерактивен Haskell REPL. Повечето програми, които ще пишем, няма да компилираме до executables, а ще ги използваме през GHCi с цел по-лесна, интерактивна и бърза работа. Haskell е известен с относително сложния си модел на IO. Нека това не ви безпокои, нито отказва. Ще стигнем до там, но преди това трябва да се заредим с по-базовите средства и концепции :) - ---- - -## GHCi - команди - -`:l ` - служи за зареждане на сорс файла достъпен на съответния път - -```text -:l Demo.hs -[1 of 1] Compiling Demo ( Demo.hs, interpreted ) -Ok, modules loaded: Demo. -``` - -
`:r` - презареждане на вече заредени сорсове -```text -:r -[1 of 1] Compiling Demo ( Demo.hs, interpreted ) -Ok, modules loaded: Demo. -``` - ---- - -## GHCi - команди - -`:t ` - показва типа на съответния _expression_ - -```text -> :t True -True :: Bool - -> :t 10 -10 :: Num a => a - -> :t (2 + 2) -(2 + 2) :: Num a => a - -> :t words -words :: String -> [String] -``` - ---- - -**Важно:** не се притеснявайте, че типът на числата не е `Int`, а `Num a => a`. Това е генерализиран тип за число. Не се плашете и от малките букви! В следващите няколко урока ще стигнем до тях - с времето и вие ще станете тъмни магьосници, но първо трябва да минем базовите неща! - ---- - -## GHCi - команди - -`:i ` - показа информация за дадената променлива с име _identifier_, какъв й е типът и къде е дефинирана - -
- -```text -> :i words - -words :: String -> [String] - -- Defined in ‘base-4.8.2.0:Data.OldList’ -``` - ---- - -## GHCi - други полезни команди - -Тези команди са общовалидни за терминала и са част от така нареченият emacs mode. Повечето приложения, които има някаква форма на интеракция, използват именно тези shortcuts. - ---- - -| Команда | Използване | -|----------|---------------------------------------------| -| tab | допълване / completion | -| Ctrl + r | reverse search - търси в историята на вече въвежданите команди | -| Ctrl + a | изместване на курсора в началото на реда | -| Ctrl + e | изместване на курсора в края на реда | -| Ctrl + w | изтриване на думата, при/преди курсора | -| Ctrl + d | спира текущия shell / command line програма | -| Ctrl + l | почиства екрана | - ---- - -## GHCi - последни думи - -Не използвайте GHCi за големи дефиниции (примерно на функции). GHCi не работи добре за въвеждане на многоредов код, затова е препоръчително кодът да е във файл, а в GHCi само да бъде презареждан и отделните функции тествани. Разглеждане на типове, малки експерименти и тестове са силните страни на всеки REPL. diff --git a/lectures/03-polymorphism/lcomprehension.md b/lectures/04-list-comprehension/lcomprehension.md similarity index 88% rename from lectures/03-polymorphism/lcomprehension.md rename to lectures/04-list-comprehension/lcomprehension.md index 6302abf..4a7468e 100644 --- a/lectures/03-polymorphism/lcomprehension.md +++ b/lectures/04-list-comprehension/lcomprehension.md @@ -2,9 +2,15 @@ page_number:true *page_number:false --> + List comprehension
-== +====

@@ -42,9 +48,8 @@ List comprehension

```hs > [ x*2 | x <- [1..4]] - [2, 4, 6, 8] - ``` +``` --- @@ -68,7 +73,6 @@ List comprehension
- Може да взимаме няколко променливи, всяка взета от различен списък ```hs > [[x, y, z] | x <- [1..3], y <- [10,11], z <- [20,21]] - [[1, 10, 20], [1, 10, 21], [1, 11, 20], [1, 11, 21], [2, 10, 20], [2, 10, 21], [2, 11, 20], [2, 11, 21], [3, 10, 20], [3, 10, 21], [3, 11, 20], [3, 11, 21]] @@ -87,24 +91,22 @@ List comprehension
## List comprehension - Когато задаваме повече от един списък, може да използваме всички предишни променливи при дефиницията на списъка ```hs - > [ [x, y] | x <- [1..4], y <-[1..x]] - - [ [1, 1], - [2, 1], [2, 2], - [3, 1], [3, 2], [3, 3] - [4, 1], [4, 2], [4, 3], [4, 4] ] +> [ [x, y] | x <- [1..4], y <-[1..x]] + [ [1, 1], + [2, 1], [2, 2], + [3, 1], [3, 2], [3, 3], + [4, 1], [4, 2], [4, 3], [4, 4] ] ``` --- ## List comprehension - Може да вгнездваме list comprehension-и ```hs -removeEs :: [String]->[String] +removeEs :: [String] -> [String] removeEs someWords = - [ [c | c <- word, c /= 'е', c /= 'E'] - | word <- someWords]] + [ [c | c <- word, c /= 'e', c /= 'E'] + | word <- someWords] -> removeЕs ["These", "are", "some", "good", "words"] - - ["Ths", "r", "som", "good", "words"] +> removeEs ["These", "are", "some", "good", "words"] + ["Ths", "ar", "som", "good", "words"] ``` diff --git a/lectures/04-let-where/let-where.md b/lectures/05-polymorphism-let-where/let-where.md similarity index 100% rename from lectures/04-let-where/let-where.md rename to lectures/05-polymorphism-let-where/let-where.md diff --git a/lectures/03-polymorphism/poly.md b/lectures/05-polymorphism-let-where/poly.md similarity index 94% rename from lectures/03-polymorphism/poly.md rename to lectures/05-polymorphism-let-where/poly.md index 6b772e4..22245d7 100644 --- a/lectures/03-polymorphism/poly.md +++ b/lectures/05-polymorphism-let-where/poly.md @@ -2,7 +2,12 @@ page_number:true *page_number:false --> + Полиморфични функции
== @@ -26,13 +31,13 @@ Досега видяхме, че ако ни се налага да работим с различни видове списъци, трябва да пишем функциите по няколко пъти с различни сигнатури, но еднакви дефиниции.
```hs -lenght :: [Int] -> Int -length [] = 0 -length (_:xs) = 1 + length xs +lengthInt :: [Int] -> Int +lengthInt [] = 0 +lengthInt (_:xs) = 1 + lengthInt xs lengthString :: String -> Int lengthString [] = 0 -lengthString (_:xs) 1 + lengthString xs +lengthString (_:xs) = 1 + lengthString xs ``` Това е ужасно! @@ -60,17 +65,20 @@ length (_:xs) = 1 + length xs Вградените функции за манипулация на списъци в Haskell работят с списъци от какъвто и да е тип! Да проверим сигнатурите: ```hs > head [1, 2, 3] -1 +1 + > head "Hi" 'H' +``` -ghci>:t head +```text +ghci> :t head head :: [a] -> a -ghci>:t reverse +ghci> :t reverse reverse :: [a] -> [a] -ghci>:t (++) +ghci> :t (++) (++) :: [a] -> [a] -> [a] ``` --- @@ -78,7 +86,7 @@ ghci>:t (++) ## Полиморфични функции - начин на работа Не може да предполагаме нищо за аргументите или променливите, които са от тип `a`. Не може да извършваме операции с тях. -```hs +```hs bad :: a -> Int bad x = x + 1 -- грешка @@ -91,7 +99,7 @@ bad x = x + 1 В рамките на едно извикване на полиморфичните функции, `a` заема конкретен тип, който не се сменя по време на изпълнението на функцията. `a` има само един конкретен тип в рамките на извикването *(тоест не може при първия аргумент да е `Char`, вторият - `Int` и тн)*. -```hs +```text ghci> :t (++) (++) :: [a] -> [a] -> [a] @@ -120,7 +128,7 @@ ghci> :t ("Hi" ++ " all") ## Ограничения В някои случаи искаме да използваме полиморфична функция, но и някакво свойство на `a`. -```hs +```hs elem :: a -> [a] -> Bool elem _ [] = False elem e (x:xs) @@ -135,7 +143,7 @@ elem e (x:xs) ## Oграничения Решение: Може да кажем, че `a` трябва да поддържа сравнение за равенство с `(Eq a) =>` пред сигнатурата. -```hs +```hs elem :: (Eq a) => a -> [a] -> Bool ``` `(Eq a) =>` наричаме класови ограничение *(class constraint)*. @@ -152,6 +160,7 @@ equals x y = x == y > equals 3 3 True + > equals "Hi" "hi" False ``` @@ -163,9 +172,10 @@ False toString :: (Show a) => a -> String toString x = show x ->toString 4 +> toString 4 "4" ->toString [42,0] -"[42, 0]" + +> toString [42,0] +"[42,0]" ``` -Вградените стандартни типове (числа, низ, симвoли) поддържат и `Eq a` и `Show a`. \ No newline at end of file +Вградените стандартни типове (числа, низ, симвoли) поддържат и `Eq a` и `Show a`.