diff --git a/Lab1/src/FirstSteps.hs b/Lab1/src/FirstSteps.hs index 8b0a43b..bf432ff 100644 --- a/Lab1/src/FirstSteps.hs +++ b/Lab1/src/FirstSteps.hs @@ -8,7 +8,7 @@ import Data.Word (Word8) -- используйте сопоставление с образцом xor :: Bool -> Bool -> Bool -xor x y = error "todo" +xor x y = if x == y then False else True -- max3 x y z находит максимум из x, y и z -- max3 1 3 2 == 3 @@ -17,9 +17,9 @@ xor x y = error "todo" -- median3 1 3 2 == 2 -- median3 5 2 5 == 5 max3, median3 :: Integer -> Integer -> Integer -> Integer -max3 x y z = error "todo" +max3 x y z = if max x y >= z then max x y else z -median3 x y z = error "todo" +median3 x y z = if max x y >= z then (if x >= y then max y z else max x z) else max x y -- Типы данных, описывающие цвета в моделях -- RGB (https://ru.wikipedia.org/wiki/RGB), компоненты от 0 до 255 @@ -36,8 +36,24 @@ data CMYK = CMYK { cyan :: Double, magenta :: Double, yellow :: Double, black :: -- Заметьте, что (/) для Int не работает, и неявного преобразования Int в Double нет. -- Это преобразование производится с помощью функции fromIntegral. -rbgToCmyk :: RGB -> CMYK -rbgToCmyk color = error "todo" +minim [] = 0 +minim [x] = x +minim (x:xs) = min x (minim xs) + +rgbToCmyk :: RGB -> CMYK +rbgToCmyk (RGB 0 0 0) = CMYK 0.0 0.0 0.0 1.0 +rgbToCmyk color = CMYK { black = toBlack + , magenta = toMagenta + , yellow = toYellow + , cyan = toCyan + } + where toBlack = minim [1 - fromIntegral (red color) / 255.0, 1 - fromIntegral (green color) / 255.0, 1 - fromIntegral (blue color) / 255.0] + toMagenta = (1 - fromIntegral (green color) / 255.0 - toBlack) / (1 - toBlack) + toYellow = (1 - fromIntegral (blue color) / 255.0 - toBlack) / (1 - toBlack) + toCyan = (1 - fromIntegral (red color) / 255.0 - toBlack) / (1 - toBlack) + + + -- geomProgression b q n находит n-й (считая с 0) член -- геометрической прогрессии, нулевой член которой -- b, @@ -47,7 +63,10 @@ rbgToCmyk color = error "todo" -- используйте рекурсию -- не забудьте случаи n < 0 и n == 0. geomProgression :: Double -> Double -> Integer -> Double -geomProgression b q n = error "todo" +geomProgression b q n + | n < 0 = error "n must be non-negative" + | n == 0 = b + | otherwise = q * geomProgression b q (n-1) -- coprime a b определяет, являются ли a и b взаимно простыми -- (определение: Целые числа называются взаимно простыми, @@ -64,4 +83,10 @@ geomProgression b q n = error "todo" -- обрабатываете отрицательные числа) -- https://hackage.haskell.org/package/base-4.9.0.0/docs/Prelude.html coprime :: Integer -> Integer -> Bool -coprime a b = error "todo" +coprime a b + | c == 0 || d == 0 = False + | c == 1 || d == 1 = True + | c > d = coprime (c - d) d + | otherwise = coprime (d - c) c + where c = abs a + d = abs b diff --git a/Lab1/src/Lists.hs b/Lab1/src/Lists.hs index 75f8517..b461208 100644 --- a/Lab1/src/Lists.hs +++ b/Lab1/src/Lists.hs @@ -1,4 +1,5 @@ module Lists where +import Data.List (transpose) -- вектор задаётся списком координат newtype Point = Point [Double] deriving (Eq, Show, Read) @@ -10,7 +11,10 @@ newtype Point = Point [Double] deriving (Eq, Show, Read) -- используйте рекурсию и сопоставление с образцом distance :: Point -> Point -> Double -distance x y = error "todo" +distance (Point []) (Point []) = 0 +distance (Point _) (Point []) = error "dims must match" +distance (Point []) (Point _) = error "dims must match" +distance (Point (x:xs)) (Point (y:ys)) = sqrt (x^2 + y^2 + distance (Point xs) (Point ys)) -- intersect xs ys возвращает список, содержащий общие элементы двух списков. -- intersect [1, 2, 4, 6] [5, 4, 2, 5, 7] == [2, 4] (или [4, 2]!) @@ -18,14 +22,21 @@ distance x y = error "todo" -- используйте рекурсию и сопоставление с образцом intersect :: [Integer] -> [Integer] -> [Integer] -intersect xs ys = error "todo" +intersect _ [] = [] +intersect [] _ = [] +intersect (x:xs) (y:ys) + | x == y = [x] ++ intersect xs ys + | otherwise = intersect xs ys -- zipN принимает список списков и возвращает список, который состоит из -- списка их первых элементов, списка их вторых элементов, и так далее. -- zipN [[1, 2, 3], [4, 5, 6], [7, 8, 9]] == [[1, 4, 7], [2, 5, 8], [3, 6, 9]] -- zipN [[1, 2, 3], [4, 5], [6]] == [[1, 4, 6], [2, 5], [3]] zipN :: [[a]] -> [[a]] -zipN xss = error "todo" +zipWith' :: ([a] -> b) -> [[a]] -> [b] +zipWith' _ [] = [] +zipWith' f xss = map f . transpose $ xss +zipN = zipWith' id -- Нижеперечисленные функции можно реализовать или рекурсивно, или с помощью -- стандартных функций для работы со списками (map, filter и т.д.) @@ -38,22 +49,30 @@ zipN xss = error "todo" -- findLast (> 0) [-1, 2, -3, 4] == Just 4 -- find (> 0) [-1, -2, -3] == Nothing find, findLast :: (a -> Bool) -> [a] -> Maybe a -find f xs = error "todo" -findLast f xs = error "todo" +find f [] = Nothing +find f (x:xs) + | f x = Just x + | otherwise = find f xs +findLast f xs + | null filtered = Nothing + | otherwise = Just (last (filtered)) + where filtered = filter f xs -- mapFuncs принимает список функций fs и возвращает список результатов -- применения всех функций из fs к x. -- mapFuncs [\x -> x*x, (1 +), \x -> if even x then 1 else 0] 3 == [9, 4, 0] mapFuncs :: [a -> b] -> a -> [b] -mapFuncs fs x = error "todo" +mapFuncs [] x = [] +mapFuncs (fs:fss) x = (fs x):(mapFuncs fss x) -- satisfiesAll принимает список предикатов (функций, возвращающих Bool) preds -- и возвращает True, если все они выполняются (т.е. возвращают True) для x. -- Полезные стандартные функции: and, all. --- satisfiesAll [even, \x -> x rem 5 == 0] 10 == True --- satisfiesAll [] 4 == True (кстати, почему?) +-- satisfiesAll [even, \x -> x `rem` 5 == 0] 10 == True +-- satisfiesAll [] 4 == True (кстати, почему?) -- потому что это нейтральный элемент, отсутствие условий не должно нарушать истинность результата, иначе бы не работала рекурсия satisfiesAll :: [a -> Bool] -> a -> Bool -satisfiesAll preds x = error "todo" +satisfiesAll [] x = True +satisfiesAll (pred:preds) x = pred x && satisfiesAll preds x -- Непустой список состоит из первого элемента (головы) -- и обычного списка остальных элементов @@ -62,8 +81,27 @@ data NEL a = NEL a [a] deriving (Eq, Show, Read) -- Запишите правильный тип (т.е. такой, чтобы функция имела результат для любых аргументов -- без вызовов error) и реализуйте функции на NEL, аналогичные tail, last и zip --- tailNel :: NEL a -> ??? --- lastNel :: NEL a -> ??? --- zipNel :: NEL a -> NEL b -> ??? --- listToNel :: [a] -> ??? --- nelToList :: NEL a -> ??? +tailNel :: NEL a -> [a] +tailNel (NEL x xs) = xs + +lastNel :: NEL a -> a +lastNel (NEL x []) = x +lastNel (NEL x xs) = lastNel (NEL xs' xss) + where + xs' = head xs + xss = tail xs + +zipNel :: NEL a -> NEL b -> [(a, b)] +zipNEL (NEL x []) (NEL y []) = [(x, y)] +zipNel (NEL x xs) (NEL y ys) = [(x, y)] ++ zipNel (NEL xs' xss) (NEL ys' yss) + where + xs' = head xs + xss = tail xs + ys' = head ys + yss = tail ys + +listToNel :: [a] -> NEL a +listToNel (a:as) = NEL a as + +nelToList :: NEL a -> [a] +nelToList (NEL a as) = a:as diff --git a/Lab1/src/Luhn.hs b/Lab1/src/Luhn.hs index 1e336a3..870dc73 100644 --- a/Lab1/src/Luhn.hs +++ b/Lab1/src/Luhn.hs @@ -10,5 +10,46 @@ module Luhn where -- Не пытайтесь собрать всё в одну функцию, используйте вспомогательные. -- Например: разбить число на цифры (возможно, сразу в обратном порядке). -- Не забудьте добавить тесты, в том числе для вспомогательных функций! +-- intToList 0 == [] +-- intToList 1 == [1] +-- intToList 123 == [321] (returns reversed list) +intToList :: Int -> [Int] +intToList 0 = [] +intToList x = [x `mod` 10] ++ intToList (x `div` 10) + + +-- processEven [] == [] +-- processEven [1] == [1] +-- processEven [1, 1] == [1, 2] +-- processEven [1, 1, 1] == [1, 2, 1] +processEven :: [Int] -> [Int] +processEven digs = [(digs !! i) * (2 ^ (i `mod` 2))| i <- [0..(length digs) - 1]] + +-- decreaseLarge 1 == 1 +-- decreaseLarge 9 == 9 +-- decreaseLarge 10 == 1 +-- decreaseLarge 18 == 9 +decreaseLarge :: Int -> Int +decreaseLarge x + | x > 9 = x - 9 + | otherwise = x + +-- decreaseAllLarge [] == [] +-- decreaseAllLarge [1] == [1] +-- decreaseAllLarge [1, 18, 3] == [1, 9, 3] +-- decreaseAllLarge [10, 2, 18] == [1, 2, 9] +decreaseAllLarge :: [Int] -> [Int] +decreaseAllLarge digs = map decreaseLarge digs + +isSumDivisible :: [Int] -> Bool +isSumDivisible [] = False +isSumDivisible digs = ((sum digs) `mod` 10) == 0 + +-- isLuhnValid 0 == False +-- isLuhnValid 1241235125 == False +-- isLuhnValid 4140834708961565 == True (generated with https://randommer.io/Card) isLuhnValid :: Int -> Bool -isLuhnValid = error "todo" +isLuhnValid x = isSumDivisible digs1 + where digs1 = decreaseAllLarge digs2 + where digs2 = processEven digs3 + where digs3 = intToList x diff --git a/Lab1/test/Spec.hs b/Lab1/test/Spec.hs index fb64b7d..4acc84f 100644 --- a/Lab1/test/Spec.hs +++ b/Lab1/test/Spec.hs @@ -15,20 +15,90 @@ main = hspec $ do it "max3" $ do max3 1 3 2 `shouldBe` 3 max3 5 2 5 `shouldBe` 5 - it "median3" pending - it "rbgToCmyk" pending - it "geomProgression" pending - it "coprime" pending + it "median3" $ do + median3 1 2 3 `shouldBe` 2 + median3 1 2 2 `shouldBe` 2 + median3 2 2 2 `shouldBe` 2 + median3 3 2 1 `shouldBe` 2 + median3 5 3 4 `shouldBe` 4 + median3 4 3 5 `shouldBe` 4 + it "rbgToCmyk" $ do + rbgToCmyk RGB {red = 255, green = 255, blue = 255} `shouldBe` CMYK {cyan = 0.0, magenta = 0.0, yellow = 0.0, black = 0.0} + rbgToCmyk RGB {red = 255, green = 0, blue = 0} `shouldBe` CMYK {cyan = 0.0, magenta = 1.0, yellow = 1.0, black = 0.0} + rbgToCmyk RGB {red = 0, green = 255, blue = 0} `shouldBe` CMYK {cyan = 1.0, magenta = 0.0, yellow = 1.0, black = 0.0} + rbgToCmyk RGB {red = 0, green = 0, blue = 255} `shouldBe` CMYK {cyan = 1.0, magenta = 1.0, yellow = 0.0, black = 0.0} + rbgToCmyk RGB {red = 255, green = 255, blue = 0} `shouldBe` CMYK {cyan = 0.0, magenta = 0.0, yellow = 1.0, black = 0.0} + rbgToCmyk RGB {red = 255, green = 0, blue = 255} `shouldBe` CMYK {cyan = 0.0, magenta = 1.0, yellow = 0.0, black = 0.0} + rbgToCmyk RGB {red = 0, green = 255, blue = 255} `shouldBe` CMYK {cyan = 1.0, magenta = 0.0, yellow = 0.0, black = 0.0} + rbgToCmyk RGB {red = 0, green = 0, blue = 0} `shouldBe` CMYK {cyan = 0.0, magenta = 0.0, yellow = 0.0, black = 1.0} + it "geomProgression" $ do + geomProgression 1.0 0.5 2 `shouldBe` 0.25 + geomProgression 1.0 2.0 3 `shouldBe` 8.0 + it "coprime" $ do + coprime 2 7 `shouldBe` True + coprime (-2) 7 `shouldBe` True + coprime 2 4 `shouldBe` False + coprime (-2) 4 `shouldBe` False describe "lists" $ do - it "distance" pending - it "intersect" pending - it "zipN" pending - it "find" pending - it "findLast" pending - it "mapFuncs" pending - it "tailNel" pending - it "lastNel" pending - it "zipNel" pending - it "listToNel" pending - it "nelToList" pending - describe "luhn" $ it "" pending + it "distance" $ do + distance (Point [0.0, 0.0]) (Point [0.0, 1.0]) `shouldBe` 1.0 + distance (Point [0, 0, 0, 0]) (Point [1, 1, 1, 1]) `shouldBe` 2.0 + distance (Point []) (Point []) `shouldBe` 0.0 + it "intersect" $ do + intersect [1, 2, 3] [1, 2, 3] `shouldBe` [1, 2, 3] + intersect [1, 2, 3] [3, 4, 5] `shouldBe` [3] + intersect [1, 2] [3, 4] `shouldBe` [] + intersect [1, 2] [] `shouldBe` [] + intersect [] [1, 2] `shouldBe` [] + intersect [] [] `shouldBe` [] + it "zipN" $ do + zipN [[1, 2, 3], [4, 5, 6], [7, 8, 9]] `shouldBe` [[1, 4, 7], [2, 5, 8], [3, 6, 9]] + zipN [[1, 2, 3], [4, 5], [6]] `shouldBe` [[1, 4, 6], [2, 5], [3]] + zipN [[]] shouldBe [] + it "find" $ do + find (> 0) [0, 1] `shouldBe` Just 1 + find (even) [1, 2, 3, 4] `shouldBe` Just 2 + find (< 0) [-1, 0] `shouldBe` Just (-1) + it "findLast" $ do + findLast (> 0) [-1, 0, 1] `shouldBe` Just 1 + findLast (< 0) [-1, -2, -3, -4] `shouldBe` Just (-4) + findLast (even) [-1, 1, -3, 3] `shouldBe` Nothing + it "mapFuncs" $ do + mapFuncs [\x -> x*x, \x -> x - 1] 2 `shouldBe` [4, 1] + mapFuncs [abs] (-9) `shouldBe` [9.0] + it "satisfiesAll" $ do + satisfiesAll [] 1 `shouldBe` True + satisfiesAll [even, \x -> x rem 5 == 0] 10 `shouldBe` True + it "lastNel" $ do + lastNel (NEL 1 [2,3]) `shouldBe` 3 + lastNel (NEL 1 [2]) `shouldBe` 2 + it "zipNel" $ do + zipNel (NEL 1 [2,3]) (NEL 1 [2,3]) `shouldBe` NEL (1,1) [(2,2),(3,3)] + zipNel (NEL 1 [2]) (NEL 3 [4]) `shouldBe` NEL (1,3) [(2,4)] + zipNel (NEL 1 []) (NEL 2 []) `shouldBe` NEL (1,2) [] + it "listToNel" $ do + listToNel [1,2,3] `shouldBe` (NEL 1 [2,3]) + listToNel [1] `shouldBe` (NEL 1 []) + it "nelToList" $ do + nelToList (NEL 1 [2,3]) `shouldBe` [1, 2, 3] + nelToList (NEL 1 []) `shouldBe` [1] + describe "luhn" $ do + it "intToList" $ do + intToList 1 `shouldBe` [1] + intToList 123 `shouldBe` [321] + it "processEven" $ do + processEven [] `shouldBe` [] + processEven [1] `shouldBe` [1] + processEven [1, 1] `shouldBe` [1, 2] + processEven [1, 1, 1] `shouldBe` [1, 2, 1] + it "decreaseLarge" $ do + decreaseLarge 1 `shouldBe` 1 + decreaseLarge 18 `shouldBe` 9 + it "decreaseAllLarge" $ do + decreaseAllLarge [] `shouldBe` [] + decreaseAllLarge [1] `shouldBe` [1] + decreaseAllLarge [1, 18, 3] `shouldBe` [1, 9, 3] + it "isLuhnValid" $ do + isLuhnValid 0 `shouldBe` False + isLuhnValid 1241235125 `shouldBe` False + isLuhnValid 4140834708961565 `shouldBe` True diff --git a/Lab2/src/Poly.hs b/Lab2/src/Poly.hs index 1e261e3..81cfe16 100644 --- a/Lab2/src/Poly.hs +++ b/Lab2/src/Poly.hs @@ -1,68 +1,102 @@ -- Не забудьте добавить тесты. module Poly where +import Data.List -- Многочлены -- a -- тип коэффициентов, список начинается со свободного члена. -- Бонус: при решении следующих заданий подумайте, какие стали бы проще или -- сложнее при обратном порядке коэффициентов (и добавьте комментарий). -newtype Poly a = P [a] +newtype Poly a = Poly [a] -- Задание 1 ----------------------------------------- -- Определите многочлен $x$. -x :: Num a => Poly a -x = undefined - +xPows x = map (\n -> x**n) [0..] +getCoefs (Poly p) = p +discardZeroes [] = [] +discardZeroes p = if (last p /= 0) then p + else discardZeroes $ init p +makePoly p x = sum $ zipWith (*) (getCoefs p) (xPows x) +--x :: Num a => Poly a +x p = Poly [0, 1] -- Задание 2 ----------------------------------------- -- Функция, считающая значение многочлена в точке -applyPoly :: Num a => Poly a -> a -> a -applyPoly = undefined +--applyPoly :: Num a => Poly a -> a -> a +applyPoly p x = makePoly p x -- Задание 3 ---------------------------------------- -- Определите равенство многочленов -- Заметьте, что многочлены с разными списками коэффициентов -- могут быть равны! Подумайте, почему. +-- Ответ: при вычислении суммы или разности многочленов, коэффициенты, стоящие при некоторых степенях, могут оказаться равными нулю. Наличие таких нулей не меняет сам многочлен, но допускает разные списки коэффициентов. В данном случае нулевые элементы в конце списка могут быть отброшены instance (Num a, Eq a) => Eq (Poly a) where - (==) = undefined + (Poly a) == (Poly b) = discardZeroes a == discardZeroes b -- Задание 4 ----------------------------------------- +showPoly [] = show 0 +showPoly p = let cOs = zip p [0..] + nonZeroCOs = filter (\(c,_) -> c /= 0) cOs + cShow c = if c == 1 then "" else show c ++ " *" + nShow n = case n of + 0 -> "" + 1 -> "x" + m -> "x^" ++ show m + cnShow c n = if c == 1 && n == 0 then show 1 + else intercalate " " $ filter (/="") [cShow c, nShow n] + terms = map (\(c,n) -> cnShow c n) nonZeroCOs + in intercalate " + " (reverse terms) -- Определите перевод многочлена в строку. -- Это должна быть стандартная математическая запись, -- например: show (3 * x * x + 1) == "3 * x^2 + 1"). -- (* и + для многочленов можно будет использовать после задания 6.) +--instance (Num a, Eq a, Show a) => Show (Poly a) where instance (Num a, Eq a, Show a) => Show (Poly a) where - show = undefined - + show (Poly []) = show 0 + show (Poly p) = showPoly p -- Задание 5 ----------------------------------------- -- Определите сложение многочленов plus :: Num a => Poly a -> Poly a -> Poly a -plus = undefined +plus p1 p2 = if (length (getCoefs p1) >= length (getCoefs p2)) then Poly $ zipWith (+) (getCoefs p1) ((getCoefs p2) ++ repeat 0) + else plus p2 p1 -- Задание 6 ----------------------------------------- -- Определите умножение многочленов +multiplyBy a p1 = Poly (map (a*)(getCoefs p1)) +multiplyByX p = Poly (0:coefs) + where coefs = getCoefs p times :: Num a => Poly a -> Poly a -> Poly a -times = undefined +times (Poly []) p2 = Poly [] +times p1 p2 = let pTimesP2 = multiplyBy (head $ getCoefs p1) p2 + xTimesP1Timesp2 = multiplyByX $ times (Poly $ tail $ getCoefs p1) p2 + in plus pTimesP2 xTimesP1Timesp2 -- Задание 7 ----------------------------------------- -- Сделайте многочлены числовым типом +negatePoly p = Poly $ map Prelude.negate (getCoefs p) instance Num a => Num (Poly a) where (+) = plus (*) = times - negate = undefined - fromInteger = undefined + negate = negatePoly + fromInteger a = Poly [fromIntegral a] -- Эти функции оставить как undefined, поскольку для -- многочленов они не имеют математического смысла abs = undefined signum = undefined -- Задание 8 ----------------------------------------- +--deriv (Poly []) = (Poly []) +--deriv (Poly (_:ps)) = Poly $ zipWith (*) ps [1..] +--nderiv n p | n == 0 = p +-- | n == 1 = deriv p +-- | otherwise = nderiv (n-1) (deriv p) + -- Реализуйте nderiv через deriv class Num a => Differentiable a where @@ -70,10 +104,12 @@ class Num a => Differentiable a where deriv :: a -> a -- взятие n-ной производной nderiv :: Int -> a -> a - nderiv = undefined + nderiv n p | n == 0 = p + | otherwise = nderiv (n - 1) (deriv p) -- Задание 9 ----------------------------------------- - -- Определите экземпляр класса типов -instance Num a => Differentiable (Poly a) where - deriv = undefined +deriv' (Poly []) = (Poly []) +deriv' (Poly (_:ps)) = Poly $ zipWith (*) ps [1..] +instance (Num a, Enum a) => Differentiable (Poly a) where + deriv (Poly p)= deriv' (Poly p) diff --git a/Lab2/src/SimpleLang.hs b/Lab2/src/SimpleLang.hs index 7e20754..8df9586 100644 --- a/Lab2/src/SimpleLang.hs +++ b/Lab2/src/SimpleLang.hs @@ -45,18 +45,33 @@ type State = String -> Int -- в начальном состоянии все переменные имеют значение 0 empty :: State -empty = undefined +empty = const 0 -- возвращает состояние, в котором переменная var имеет значение newVal, -- все остальные -- то же, что в state extend :: State -> String -> Int -> State -extend state var newVal = undefined +extend state var newVal v | v == var = newVal + | otherwise = state v -- Задание 2 ----------------------------------------- -- возвращает значение выражения expr при значениях переменных из state. eval :: State -> Expression -> Int -eval state expr = undefined +eval state (Var var) = state var +eval _ (Val val) = val +eval state (Op val1 op val2) = case op of + Plus -> var1 + var2 + Minus -> var1 - var2 + Times -> var1 * var2 + Divide -> var1 `div` var2 + Gt -> if var1 > var2 then 1 else 0 + Ge -> if var1 >= var2 then 1 else 0 + Lt -> if var1 < var2 then 1 else 0 + Le -> if var1 <= var2 then 1 else 0 + Eql -> if var1 == var2 then 1 else 0 + where + var1 = eval state val1 + var2 = eval state val2 -- Задание 3 ----------------------------------------- @@ -72,23 +87,61 @@ data DietStatement = DAssign String Expression -- упрощает программу Simple desugar :: Statement -> DietStatement -desugar = undefined + +desugar (If expr outTrue outFalse) = DIf expr (desugar outTrue) (desugar outFalse) + +desugar (While expr state) = DWhile expr (desugar state) + +desugar (Assign var expr) = DAssign var expr + +desugar (Incr var) = DAssign var (Op (Var var) Plus (Val 1)) + +desugar Skip = DSkip + +desugar (Block []) = DSkip + +desugar (Block (st : sts)) = DSequence (desugar st) (desugar (Block sts)) + +desugar (For state expr state1 state2) = DSequence (desugar state) (DWhile expr (DSequence (desugar state2) (desugar state1))) -- Задание 4 ----------------------------------------- -- принимает начальное состояние и программу Simpler -- и возвращает состояние после работы программы runSimpler :: State -> DietStatement -> State -runSimpler = undefined +runSimpler state (DAssign var expr) = extend state var (eval state expr) + +runSimpler state (DIf expr out1 out2) = if eval state expr == 1 then runSimpler state out1 + else runSimpler state out2 + +runSimpler state (DWhile exp st) = if eval state exp == 1 then runSimpler (runSimpler state st) (DWhile exp st) + else state + +runSimpler state (DSequence st1 st2) = runSimpler (runSimpler state st1) st2 + +runSimpler state DSkip = state -- -- in s "A" ~?= 10 -- принимает начальное состояние и программу Simple -- и возвращает состояние после работы программы run :: State -> Statement -> State -run = undefined - +run state (Assign var expr) = extend state var (eval state expr) +run state (Incr var) = extend state var (state var + 1) +run state (If cond st1 st2) = + if eval state cond /= 0 then run state st1 else run state st2 +run state (While cond stmt) = + let loop state = if eval state cond /= 0 then loop (run state stmt) else state + in loop state +run state (For initStmt cond incrStmt bodyStmt) = + let initState = run state initStmt + loop st = if eval st cond /= 0 + then loop (run st (Block [bodyStmt, incrStmt])) + else st + in loop initState +run state (Block sts) = foldl run state sts +run state Skip = state -- Программы ------------------------------------------- {- Вычисление факториала @@ -113,8 +166,10 @@ factorial = For (Assign "Out" (Val 1)) B := B - 1 -} squareRoot :: Statement -squareRoot = undefined - +squareRoot = Block [ Assign "B" (Val 0), + While (Op (Var "A") Ge (Op (Var "B") Times (Var "B"))) (Incr "B"), + Assign "B" (Op (Var "B") Minus (Val 1)) + ] {- Вычисление числа Фибоначчи F0 := 1; @@ -135,4 +190,19 @@ squareRoot = undefined } -} fibonacci :: Statement -fibonacci = undefined +fibonacci = + Block [ + Assign "F0" (Val 1), Assign "F1" (Val 1), + If (Op (Var "In") Eql (Val 0)) + (Assign "Out" (Val 1)) + (If (Op (Var "In") Eql (Val 1)) + (Assign "Out" (Var "F0")) + (For (Assign "C" (Val 2)) + (Op (Var "C") Le (Var "In")) + (Incr "C") + (Block [ Assign "T" (Op (Var "F0") Plus (Var "F1")), Assign "F0" (Var "F1"), Assign "F1" (Var "T") , Assign "Out" (Var "T") + ] + ) + ) + ) + ] diff --git a/Lab2/test/Spec.hs b/Lab2/test/Spec.hs index b2592c5..d2c2e06 100644 --- a/Lab2/test/Spec.hs +++ b/Lab2/test/Spec.hs @@ -5,7 +5,45 @@ import Test.Hspec main :: IO () main = hspec $ do describe "poly" $ do - it "applyPoly" $ pending + it "applyPoly" $ do + applyPoly (Poly []) 1 `shouldBe` 0 + applyPoly (Poly [2]) 1 `shouldBe` 2 + applyPoly (Poly [1, 2, 3]) 2 `shouldBe` 17.0 + it "+" $ do + (Poly [1, 2]) + (Poly []) `shouldBe` (Poly [1, 2]) + (P [1, 2, 3]) + (P [1, 2]) `shouldBe` (P [2, 4, 3]) + it "*" $ do + (P [1, 2, 3]) * (P []) `shouldBe` (P [0, 0, 0]) + (P [1, 2, 3]) * (P [1, 2]) `shouldBe` (P [1, 4, 7, 6]) + it "negate" $ do + negate (P []) `shouldBe` P [] + negate (P [1, -2, 3]) `shouldBe` P [-1, 2, -3] + it "(==)" $ do + ((P [0, 1, 0]) == (P [0, 1])) `shouldBe` True + ((P [1, 2, 3]) == (P [1, 2])) `shouldBe` False + ((P [1, 2, 3]) == (P [1, 2, 4])) `shouldBe` False + it "show" $ do + show (P [1, 2, 3]) `shouldBe` "3 * x^2 + 2 * x + 1" + it "nderiv" $ do + nderiv 0 (P [1, 2, 3]) `shouldBe` (P [1, 2, 3]) + nderiv 1 (P [1, 2, 3]) `shouldBe` (P [2, 6]) + nderiv 2 (P [1, 2, 3]) `shouldBe` (P [6]) + nderiv 3 (P [1, 2, 3]) `shouldBe` (P []) describe "simpleLang" $ do -- включите тесты на работу - it "desugar" $ pending + it "extend" $ do + extend empty "a" 1 "a" `shouldBe` 1 + it "eval" $ do + eval (extend empty "a" 1) (Op (Var "a") Plus (Val 1)) `shouldBe` 2 + eval (extend empty "a" 2) (Op (Var "a") Minus (Val 1)) `shouldBe` 1 + eval (extend empty "a" 6) (Op (Var "a") Divide (Val 3)) `shouldBe` 2 + eval (extend empty "a" 2) (Op (Var "a") Gt (Val 1)) `shouldBe` 1 + eval (extend empty "a" 2) (Op (Var "a") Ge (Val 2)) `shouldBe` 1 + eval (extend empty "a" 2) (Op (Var "a") Lt (Val 1)) `shouldBe` 0 + eval (extend empty "a" 2) (Op (Var "a") Le (Val 2)) `shouldBe` 1 + eval (extend empty "a" 1) (Op (Var "a") Eql (Val 1)) `shouldBe` 1 + it "desugar" $ do + desugar (Incr "a") `shouldBe` DAssign "a" (Op (Var "a") Plus (Val 1)) + it "programms" $ do + ((SimpleLang.run (extend empty "In" 7) fibonacci) "Out") `shouldBe` 21 + ((SimpleLang.run (extend empty "A" 49) squareRoot) "B") `shouldBe` 7 diff --git a/Lab3/src/FunctorsMonads.hs b/Lab3/src/FunctorsMonads.hs index 7d15ff8..187c63a 100644 --- a/Lab3/src/FunctorsMonads.hs +++ b/Lab3/src/FunctorsMonads.hs @@ -25,10 +25,10 @@ infixl 4 <**> -- реализуйте join' через >>== и наоборот class Applicative' m => Monad' m where - (>>==) :: m a -> (a -> m b) -> m b - (>>==) = error "implement using join' (and Applicative')" - join' :: m (m a) -> m a - join' = error "implement using >>== (and Applicative')" + (>>==) :: Applicative m => m a -> (a -> m b) -> m b + x >>== m = join' (fmap m x) + join' :: Applicative m => m (m a) -> m a + join' x = x >>== id -- пример instance Functor' Maybe where @@ -70,32 +70,33 @@ instance Monad' [] where -- liftA2' (+) (Just 1) (Just 2) == Just 3 -- liftA2' (+) Nothing (Just 2) == Nothing liftA2' :: Applicative' f => (a -> b -> c) -> f a -> f b -> f c -liftA2' = undefined +liftA2' f fa fb = f <$$> fa <**> fb -- Выполняет все действия в списке и собирает их результаты в один список -- seqA [Just 1, Just 2] == Just [1, 2] -- seqA [Just 1, Just 2, Nothing] == Nothing seqA :: Applicative' f => [f a] -> f [a] -seqA = undefined +seqA [] = pure' [] +seqA (x:xs) = (:) <$$> x <**> seqA xs -- Применяет функцию, возвращающую действия, ко всем элементам списка, выполняет эти действия -- и собирает результаты в список -- traverseA Just [1, 2] == Just [1, 2] -- traverseA (\a -> if a > 2 then Just a else Nothing) [1, 3] == Nothing traverseA :: Applicative' f => (a -> f b) -> [a] -> f [b] -traverseA = undefined +traverseA f = seqA . fmap f -- Фильтрует список, используя "предикат с эффектом". -- filterA (\a -> if a > 10 then Nothing else Just (a > 0)) [-1, -2, 1, 2] == Just [1, 2] -- filterA (\a -> if a < 0 then Nothing else Just (a > 1)) [-1, -2, 1, 2] == Nothing filterA :: Applicative' f => (a -> f Bool) -> [a] -> f [a] -filterA = undefined +filterA predicate = foldr (\x -> liftA2' (\cond -> if cond then (x:) else id) (predicate x)) (pure' []) -- Композиция монадических функций -- composeM Just Just == Just (т.е. для всех x: composeM Just Just x == Just x) -- composeM Just (const Nothing) == const Nothing -composeM :: Monad' m => (b -> m c) -> (a -> m b) -> (a -> m c) -composeM = undefined +-- composeM :: Applicative' m => (b -> m c) -> (a -> m b) -> (a -> m c) +-- composeM f g = \x -> g x >>== f -- Задание 3 ----------------------------------------- @@ -104,15 +105,24 @@ composeM = undefined -- Добавьте тесты на поведение функций из задания 2 с этими экземплярами instance Functor' (Either t) where - (<$$>) = undefined + _ <$$> Left l = Left l + f <$$> Right r = Right (f r) + instance Applicative' (Either t) where - pure' = undefined - (<**>) = undefined + pure' = Right + Left l <**> _ = Left l + Right f <**> r = fmap f r + instance Monad' (Either t) where + (Left l) >>== _ = Left l + (Right r) >>== f = f r instance Functor' ((->) t) where -- (->) a b -- то же самое, что a -> b - (<$$>) = undefined + f <$$> g = \x -> f (g x) + instance Applicative' ((->) t) where - pure' = undefined - (<**>) = undefined + pure' = const + (<**>) f g x = f x (g x) + instance Monad' ((->) t) where + f >>== k = \r -> k (f r) r diff --git a/Lab3/src/Streams.hs b/Lab3/src/Streams.hs index 4fcefca..78435cd 100644 --- a/Lab3/src/Streams.hs +++ b/Lab3/src/Streams.hs @@ -17,12 +17,13 @@ instance Show a => Show (Stream a) where -- Реализуйте функцию, превращающую поток в (бесконечный) список streamToList :: Stream a -> [a] -streamToList = undefined +streamToList (x :> xs) = x : streamToList xs -- функция, возвращающая n первых элементов потока -- удобна для написания тестов следующих функций sTake :: Int -> Stream a -> [a] -sTake = undefined +sTake n (x :> xs) | n == 0 = [] + | n > 0 = x : (sTake (n - 1) xs) -- Задание 2 ----------------------------------------- @@ -31,7 +32,7 @@ sTake = undefined -- поток, состоящий из одинаковых элементов sRepeat :: a -> Stream a -sRepeat = undefined +sRepeat x = x :> sRepeat x -- sRepeat 1 == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, ... @@ -40,19 +41,19 @@ sRepeat = undefined -- будет циклическим (ссылаться сам на себя), а не бесконечно растущим) -- sCycle [1, 2, 3] == [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, ... sCycle :: [a] -> Stream a -sCycle = undefined +sCycle (x:xs) = foldr (:>) (sCycle xs) xs -- поток, заданный начальным значением и функцией, строящей следующее значение -- по текущему -- sIterate (/ 2) 1.0 == [1.0, 0.5, 0.25, 0.125, 0.0625, ... sIterate :: (a -> a) -> a -> Stream a -sIterate = undefined +sIterate f x = x :> (sIterate f (f x)) -- функция, возвращающая поток из чередующихся элементов двух потоков -- (для следующего задания нужно сделать эту функцию ленивой по -- второму аргументу, то есть не сопоставлять его с образцом) sInterleave :: Stream a -> Stream a -> Stream a -sInterleave (_ :> _) _ = undefined +sInterleave (x :> xs) ys = x :> sInterleave ys xs -- sInterleave (sRepeat 1) (sRepeat 2) == [1, 2, 1, 2, 1, 2, ... @@ -62,7 +63,7 @@ sInterleave (_ :> _) _ = undefined -- поток натуральных чисел (начиная с 0) nats :: Stream Integer -nats = undefined +nats = sIterate ((+) 1) 0 -- nats == [0, 1, 2, 3, 4, 5, 6, 7, ... @@ -70,8 +71,11 @@ nats = undefined -- делящая n нацело. Подсказка: с помощью sInterleave это можно сделать без -- проверок на делимость, если её реализация ленива по второму аргументу -- (подумайте, почему это важно). +-- pows 0 = 0 +-- pows 1 = 0 +-- pows x = if x `mod` 2 == 0 then 1 + pows (x-1) else 0 ruler :: Stream Integer -ruler = undefined +ruler = helper 0 where helper n = sInterleave (sRepeat n) (helper (n + 1)) -- ruler == [0, 1, 0, 2, 0, 1, 0, 3, ... @@ -90,7 +94,8 @@ minMaxSlow xs = Just (minimum xs, maximum xs) {- -O0: Total time: ??? Total Memory in use: ??? -} {- -O2: Total time: ??? Total Memory in use: ??? -} -minMax = undefined +minMax [] = Nothing +minMax (x:xs) = Just (foldl update (x, x) xs) where update (minimum, maximum) curr = (min minimum curr, max maximum curr) -- Дополнительное задание: реализуйте ту же самую функцию (под названием minMaxBang) с -- использованием явной строгости (seq и/или !) @@ -128,26 +133,28 @@ main = print $ minMaxSlow $ sTake 1000000 $ ruler -- или http://hackage.haskell.org/package/hedgehog-classes, если в предыдущем задании использовали Hedgehog. instance Functor Stream where - fmap = undefined + fmap f (x :> xs) = f x :> fmap f xs instance Applicative Stream where - pure = undefined - (<*>) = undefined + pure x = sRepeat x + (<*>) (f:>fs) (x:>xs) = (f x) :> (fs <*> xs) instance Monad Stream where return = pure -- в этом случае может быть проще использовать реализацию через join -- xs >>= f = join ... where join = ... - (>>=) = undefined + (>>=) xs f = join' (fmap f xs) where join' ((x :> zs) :> ys) = x :> sInterleave zs (join' ys) -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Foldable.html instance Foldable Stream where -- достаточно определить одну из них - -- foldr = undefined + foldr :: (a -> b -> b) -> b -> Stream a -> b + foldr f acc (x :> xs) = f x (foldr f acc xs) -- foldMap = undefined -- https://hackage.haskell.org/package/base-4.12.0.0/docs/Data-Traversable.html instance Traversable Stream where -- достаточно определить одну из них -- traverse = undefined - -- sequenceA = undefined + sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a) + sequenceA = traverse id diff --git a/Lab3/test/Spec.hs b/Lab3/test/Spec.hs index 6803959..69ab0b7 100644 --- a/Lab3/test/Spec.hs +++ b/Lab3/test/Spec.hs @@ -1,6 +1,7 @@ import FunctorsMonads import Streams hiding (main) import Test.Hspec +import Data.List -- Раскомментируйте QuickCheck или Hegdehog, в зависимости от того, что будете использовать -- Документация https://hspec.github.io/quickcheck.html -- import Test.Hspec.QuickCheck @@ -8,10 +9,72 @@ import Test.Hspec -- import Test.Hspec.Hedgehog -- Добавьте минимум 5 тестов свойств для функций из первых 2 лабораторных (скопируйте определения тестируемых функций сюда). +coprime :: Integer -> Integer -> Bool +coprime a b + | c == 0 || d == 0 = False + | c == 1 || d == 1 = True + | c > d = coprime (c - d) d + | otherwise = coprime (d - c) c + where c = abs a + d = abs b + +max3, median3 :: Integer -> Integer -> Integer -> Integer +max3 x y z = if max x y >= z then max x y else z +median3 x y z = if max x y >= z then (if x >= y then max y z else max x z) else max x y + +newtype Poly a = Poly [a] +xPows x = map (\n -> x**n) [0..] +getCoefs (Poly p) = p +discardZeroes [] = [] +discardZeroes p = if (last p /= 0) then p + else discardZeroes $ init p +makePoly p x = sum $ zipWith (*) (getCoefs p) (xPows x) +applyPoly p x = makePoly p x main :: IO () main = hspec $ do - describe "functors and monads" $ do - it "" $ pending - describe "streams" $ do - it "" $ pending + describe "FunctorsMonads" $ do + it "<$$>" $ do + (+1) <$$> Nothing `shouldBe` Nothing + (*2) <$$> Just 1 `shouldBe` Just 2 + it "join'" $ do + join' Nothing = Nothing + join' (Just [1, 2, 3]) = [1, 2, 3] + it "liftA2'" $ do + liftA2' (+) Nothing Nothing `shouldBe` Nothing + liftA2' (+) Nothing (Just 1) `shouldBe` Nothing + liftA2' (+) (Just 2) (Just 1) `shouldBe` Just 3 + it "seqA" $ do + seqA [Nothing, Nothing] `shouldBe` Nothing + seqA [Just 1, Nothing] `shouldBe` Nothing + seqA [Just 1, Just 2] `shouldBe` Just [1, 2] + seqA [[1, 2], [3, 4]] `shouldBe` [[1,3],[1,4],[2,3],[2,4]] + it "traverseA" $ do + traverseA Just [1, 2] `shouldBe` Just [1, 2] + traverseA (*2) [1, 2] `shouldBe` [2, 4] + it "filterA" $ do + filterA (\x -> if x > 10 then Nothing else Just (x > 0)) [-2, -1, 0, 1, 2] `shouldBe` Just [1, 2] + filterA (\x -> if x > 0 then Nothing else Just (x > 0)) [-2, -1, 1, 2] `shouldBe` Nothing + it "composeM" $ do + composeM Just Just 1 `shouldBe` Just 1 + composeM (*2) (+1) 3 `shouldBe` Just 8 + describe "Streams" $ do + it "sTake" $ do + sTake 0 (sIterate (+1) 1) `shouldBe` [] + sTake 3 (sIterate (+1) 1) `shouldBe` [1, 2, 3] + it "sRepeat" $ do + sTake 5 (sRepeat 1) `shouldBe` [1, 1, 1, 1, 1] + it "sCycle" $ do + sTake 4 (sCycle [1, 2]) `shouldBe` [1, 2, 1, 2] + it "sIterate" $ do + sTake 5 (sIterate (*2) 1) `shouldBe` [2, 4, 6, 8, 10] + sTake 5 (sIterate not True) `shouldBe` [True, False, True, False, True] + it "nats" $ do + sTake 5 nats `shouldBe` [0, 1, 2, 3, 4] + it "ruler" $ do + sTake 5 (ruler) `shouldBe` [0, 1, 0, 2, 0] + it "minMax" $ do + minMax [] `shouldBe` Nothing + minMax [1] `shouldBe` Just (42, 42) + minMax [-10, 15, -25, 70, 1] `shouldBe` Just (-25, 70) +