Первое домашнее задание проверяет понимание базовых конструкций языка: списки, полиморфизм, паттерн-мэтчинг, алгебраические типы данных, классы типов. А также стандартные функции и интерфейсы. Обратите внимание, что задание соответствует материалу, который будет рассказан в темах со 2 по 4ю отсюда, поэтому на момент выполнения Вы можете не знать некоторых конструкций - это нормально. Они будут объяснены на следующих лекцих, что и позволит Вам узнать все необходимое для выполнения.
Задания разделены на блоки. Были предприняты некоторые усилия со стороны преподавателей, чтобы блоки соответстовали независимым частям лекций. Некоторые задания имеют усложнённые версии, которые необходимо выполнять дополнительно к базовой, но они также будут оценены дополнительными баллами.
Упорядочить по возрастанию переданную тройку элементов.
ghci> order3 (5, 2, 10)
(2, 5, 10)
Функция должна повторять каждый элемент столько раз, чему равен сам элемент.
ghci> smartReplicate [1,2,3]
[1,2,2,3,3,3]
Напишите функцию, которой передаётся список списков и некоторый элемент. Эта функция должна вернуть список только тех списков, которые содержат переданный элемент.
ghci> contains 3 [[1..5], [2,0], [3,4]]
[[1,2,3,4,5],[3,4]]
Требуется написать функцию, которая принимает строку, состоящую только из чисел, разделённых пробельными символами, и находит сумму всех чисел в этой строке. Тип функции должен быть String -> Int
. В данном задании нужно считать, что переданная строка корректная.
ghci> stringSum "1 1"
2
ghci> stringSum "100\n\t-3"
97
Больше тестов ниже:
passTests = [ "1", "1 2 3", " 1", "1 ", "\t1\t", "\t12345\t", "010 020 030"
, " 123 456 789 ", "-1", "-1 -2 -3", "\t-12345\t", " -123 -456 -789 "
, "\n1\t\n3 555 -1\n\n\n-5", "123\t\n\t\n\t\n321 -4 -40"
]
mustFail = ["asd", "1-1", "1.2", "--2", "+1", "1+"]
Реализовать функцию, которая удаляет элемент по заданному индексу и возвращает удалённый элемент. Подумайте над тем, как отразить такое поведение в типе функции наиболее полно.
Требуется реализовать сортировку слиянием:
ghci> mergeSort [2, 1, 0, 3, 10, 5]
[0, 1, 2, 3, 5, 10]
Поместите следующий код в начале вашего модуля
import System.Random (newStdGen, randomRs)
randomIntList :: Int -> Int -> Int -> IO [Int]
randomIntList n from to = take n . randomRs (from, to) <$> newStdGen
чтобы тестировать вашу функцию в ghci следующим образом
ghci> example <- randomIntList 5 (-10) 10
ghci> example
[-8,1,-8,-5,-7]
ghci> mergeSort example
[-8,-8,-7,-5,1]
Для этого необходимо в зависимости добавить библиотеку random
.
В этом блоке разрешено использовать автоматический deriving
только для Show
. Остальные инстансы необходимо реализовывать самостоятельно.
Вы проектируете собственный фантастический мир. Эта задача не из лёгких. Для начала Вы решаете ввести время в ваш собственный мир, используя привычные дни недели.
Определите свой тип данных для Дней недели. Реализуйте следующие функции:
nextDay
: возвращает следующий за переданным день недели.afterDays
: возвращает день недели, который наступит после заданного через переданное число дней.isWeekend
: проверяет, является ли день недели выходным.daysToParty
: выводит число дней, оставшихся до пятницы.Преждем чем населить мир обитателями, Вы принимаете решение создать города в Вашем мире, потому что Вам всегда нравилось играть в Civilization. Необходимо определить типы данных для представления города:
Определите тип данных для города таким образом, чтобы в Вашем мире нельзя было в принципе создать города, не удовлетворяющие этому условию. После этого поддержите следующие функции по развитию городов:
Чтобы экономика в городах могла развиваться, необходимо ввести в мир деньги, назначить цены за товары и услуги.
Но сначала необходимо ввести некоторые операции по работе с натуральными числами, в которых будут выражаться деньги. Напомню, что натуральным числом является либо 0
, либо какое-то натуральное число, увеличенное на 1
. Этот тип данных представляется следующим образом:
data Nat = Z | S Nat
Реализуйте следующие операции (которые должны быть реализованы полностью самостоятельно):
Дополнительно требуется реализовать следующие функции:
Чтобы сделать Ваш мир более красочным и живописным, Вы решаете добавить в него растительность. Так как Вы программист, то единственный вид растительности, который Вам известен – двоичные деревья поиска.
Дерево имеет два конструктора:
При этом двоичное дерево называется двоичным деревом поиска если оно удовлетворяет следующему условию: значения всех элементов в левом поддереве меньше значения в узле, а значения элементов в правом поддереве больше значения в узле.
Реализуйте следующие операции с двоичным деревом поиска:
fromList
, которая создаёт дерево из списка элементов.В этом задании обязательно использовать расширение языка -XInstanceSigs
и указывать типы функций в инстансах. При этом необходимо реализовать обе функции foldMap
и foldr
.
Реализуйте вручную инстансы Foldable
для следующих типов данных:
data Pair a = Pair a a
data NonEmpty a = a :| [a]
Прошло много времени, как Вы создали собственный мир… В исходном коде вашего мира появилось много типов данных и абстракций. Пришло время порефакторить предыдущий код. Для этого необходимо сделать тип данных Tree
инстансом класса типов Foldable
. Инстанс должен быть реализован таким образом, чтобы выполнялось свойство: toList . fromList ≡ sort
.
Используя свёртку, реализуйте функцию splitOn
, которая разбивает список на подсписки по элементу.
ghci> splitOn '/' "path/to/file"
["path", "to", "file"]
Стоит обратить внимание, что функция splitOn
всегда возвращает непустой список элементов. Это должно быть отражено в типе. Пример приведён для обычного списка, хотя это решение не полностью корректное.
Дополнительно реализуйте функцию (опять же, используя свёртку) joinWith
, обратную splitOn
. При этом Ваши реализации должны удовлетворять свойству:
joinWith x . splitOn x ≡ id
.
ghci> joinWith '/' ["path", "to", "file"]
"path/to/file"
Стоит обратить внимание на то, что функция joinWith
принимает непустой список.
Напишите функцию, принимающую список списков внутри Maybe
и возвращающую конкатенацию всех внутренних списков.
ghci> maybeConcat [Just [1,2,3], Nothing, Just [4,5]]
[1,2,3,4,5]
Функция должна принимать произвольный набор Either
, где и Left
и Right
содержат некоторые моноидальные элементы, и необходимо вернуть пару из результатов моноидального объединения отдельно элементов внутри Left
и отдельно элементов внутри Right
.
ghci> eitherConcat [Left (Sum 3), Right [1,2,3], Left (Sum 5), Right [4,5]]
(Sum {getSum = 8}, [1,2,3,4,5])
Реализуйте инстансы алгебраических классов типов для следующих структур данных. Ваши инстансы должны удовлетворять законам для этих структур.
Semigroup
для data NonEmpty a = a :| [a]
.Semigroup
для типа данных data ThisOrThat a b = This a | That b | Both a b
.Дополнительно реализуйте следующие инстансы:
Semigroup
и Monoid
для строк, объединяемых при помощи '.'
.ghci> Name "root" <> Name "server"
Name "root.server"
Semigroup
и Monoid
для newtype Endo a = Endo { getEndo :: a -> a }
.Имеется следующий тип данных Builder
для создания строк:
data Builder = One Char | Many [Builder]
Реализуйте инстансы классов типов Semigroup
и Monoid
для типа Builder
. Реализуйте функцию создания билдера из строки:
fromString :: String -> Builder
Реализуйте функцию построения строки из билдера:
toString :: Builder -> String
Подумайте над тем, как можно реализовать этот тип данных более эффективно и предложите способы оптимизаций.