Функциональное программирование
Ссылки
- haskell.org для установки
- wiki.haskell.org много материалов для изучения
- Haskell Wikibook.
Читайте разделы в Haskell Basics:
Variables and functions
,Truth values
,Type basics
,Lists and tuples
,Next steps
. В разделе Elementary Haskell: всё безTypes declarations
и без разделов о непонятном. - Кубенский А.А. Функциональное программирование. Ищите на library.spbu.ru или старая версия Кубенский А.А. Функциональное программирование 2010
Лекции
Что мы успели изучить на лекции
carrying.hs Частичное применение, функции $
и .
Вводные
Операции со списками
length1
длина спискаlast1
возвращает последний элемент спискаconc1
конкатенация списковpush1
дописать элемент в конец спискаrepeat1 :: Int -> Char -> String
повторить элемент указанное число разget1
получить элемент списка по его индексуrev1
перевернуть списокtake1
вернуть список, состоящий из указанного количества начальных элементов спискаfilter1
фильтрация списка. Оставить только те элементы, которые подходят под условие:filter1 :: (a -> Bool) -> [a] -> [a]
Теория чисел
Пользуйтесь функциями обработки списков: map
, filter
и т.п.
- Дано число, вывести список всех его положительных делителей.
- Дано число, проверить его на простоту.
Воспользуйтесь предыдущей задачей, но не перебирайте список делителей полностью, проверьте только, какой делитель идёт после единицы. - Дано число, проверить, совершенное ли это число. Другими словами, верно ли что число равно сумме своих делителей, не считая себя. Например, 6 = 1 + 2 + 3.
- Найдите все совершенные числа от 1 до миллиона
- Решето эратосфена. Дано n, найдите все простые числа от 1 до n. Прочитайте алгоритм решета Эратосфена, и реализуйте его следующим образом: создайте список чисел от 2 до n, потом отделяйте от него первый элемент, фильтруйте остальные, которые на него делятся. Продолжайте это, пока список не кончится. Обсуждение решета Эратосфена и его реализации на Haskell см. учебник Кубенского.
- Простые близнецы. Дано n, найдите все пары простых чисел от 1 до n, отличающихся на 2.
Операции на списках
concatMap
Следуюшие задачи нужно решить с помощью встроенной функции concatMap
- Дан список, повторите каждый его элемент два раза.
- Опять реализуйте
filter
- Реализуйте
map
zip
, zipWith
и unzip
Используйте функции zip, zipWith, unzip.
- Реализуйте функцию zipWithIndex: к каждому элементу списка “припишите” его индекс. Т.е. каждый элемент списка должен превратиться в тьюпл, где вначале идет индекс элемента, а потом сам элемент. Начинайте считать элементы с нуля.
- Сложите каждый элемент списка
[Int]
с его индексом. - Дан список
[Int]
, верните список, в котором все четные по счету элементы заменены на ноль - Дан список, удалите из него все элементы с четным индексом.
- Дан список двузначных чисел. Верните тьюпл из двух списков: первые цифры чисел и вторые цифры чисел. Например,
[10, 44, 31]
превращается в([1, 4, 3], [0, 4, 1])
.
foldl
и foldr
Решите каждую задачу с помощью одной из функций: foldl
или foldr
.
Обычно с помощью одной из этих функций решение проще и естественней.
Иногда хорошее решение можно получить обеими функциями,
в этом случае попробуйте решить одну задачу двумя функциями.
Обратите внимание, что разные задачи в этом блоке имеют очень похожие решения, они отличаются буквально в одной операции. Найдите такие задачи с похожими решениями.
- Посчитайте количество элементов в списке
- Переверните список
- Припишите один список к другому (операция
++
) - Реализуйте функцию
filter
- Реализуйте функцию
map
- Реализуйте функцию
concatMap
. Она имеет тип:(a -> [b]) -> [a] -> [b]
и делает следующее:concatMap f l
применяет функциюf
к каждому элементу спискаl
и получает списки. Все полученные списки объединяются в один. - Реализуйте
foldl
черезfoldr
. И потом наоборот. (Подсказка: переворачивание списка мы делали через foldl, поэтому его можно использовать во втором случае) - Напишите функцию
minmax
, которая возвращает минимальный и максимальный элемент списка в виде Tuple.
Несколько задач на алгоритмы
Эти задачи не на конкретные функции или приемы, а на то, как запрограммировать что-то содержательное. Вы можете использовать всё, что мы проходили о Haskell.
- Алгоритм быстрого возведения в степень. Положим, нам нужно вычислить \(x^n\). Запишем следующее равенство:
$Answer = a^b \cdot c$. Оно верное, если \(a = x\), \(b = n\), \(с = 1\).
Теперь выполняем следующие шаги:
- Если \(b\) четно, то заменяем \(a \rightarrow a^2\), а \(b \rightarrow b/2\). Теперь \(Answer = (a^2)^{b/2}\cdot c\)
- Если \(b\) нечетно, то заменяем \(b \rightarrow b-1\), \(с \rightarrow c\cdot a\). Теперь \(Answer = a^{b-1} \cdot (a \cdot c)\)
Когда \(b\) станет равно нулю, мы получим \(Answer = a^0 \cdot c\), т.е. ответ это \(c\). Реализуйте этот алгоритм функционально, с хвостовой рекурсией
- Быстрая сортировка
- Напишите функцию, которая по списку и заданному числу возвращает три списка в виде Tuple: все элементы, меньшие
v
; все элементы, равныеv
; все элементы, большиеv
. - Напишите функцию, которая сортирует массив рекурсивно: Вызывает функцию из предыдущего пункта для первого элемента массива. Рекурсивно сортирует первый и третий списки, соединяет их опять в один список.
- Напишите функцию, которая по списку и заданному числу возвращает три списка в виде Tuple: все элементы, меньшие
- Сортировка слиянием.
- Напишите функцию, которая делит один список на два одинаковой длины. Или почти одинаковой, если длина нечетна. Просмотртие список функций по работе со списком из стандартной библиотеки, чтобы найти какую-нибудь функцию, которая в этом поможет.
- Напишите функцию, которая сливает два отсортированных списка в один отсортированный. Сделайте это рекурсивно, откусывая по одному элементу с начала каждого из списков.
- Теперь сама сортировка. Разделите заданный список на два равной длины. Рекурсивно сортируйте оба эти списка, потом слейте их вместе