- •Оглавление
- •От автора
- •Структура
- •Пояснения и обозначения
- •Демонстрация кунг-фу
- •Теория Основные понятия и типы данных
- •Кортежи
- •Функции, операторы
- •Полиморфные типы данных
- •Чтение сигнатур типов
- •Простейшие функции и операторы
- •Арифметические функции
- •Логические функции
- •Списочные функции
- •Кортежные функции
- •Создание своих функций
- •Способ 1. Определение функции как выражения от параметров:
- •Способ 2. Несколько определений одной функции:
- •Способ 3. Определение функции через синоним:
- •Способ 4. Лямбда функция (анонимная функция):
- •Способ 5. Частичное применение функции:
- •Образцы и сопоставление с образцом
- •Синтаксический хлеб и синтаксический сахар
- •Условия и ограничения
- •Локальные определения
- •Двумерный синтаксис
- •Арифметические последовательности
- •Замыкания списков
- •Функциональное мышление
- •Рекурсия как основное средство
- •Ручная редукция выражений
- •Думаем функционально, шаг раз
- •Думаем функционально, шаг два: аккумуляторы
- •Реализация простейших списочных и прочих функций
- •Думаем функционально, шаг три: хвостовая рекурсия
- •Еще раз о рекурсии
- •Полезные хитрости языка
- •Ленивые вычисления и строгие функции
- •Бесконечные списки
- •Функция show
- •Совсем немного о классах
- •Функция read
- •Функция error
- •Побочные эффекты и функция trace
- •Функции высших порядков
- •Мотивация
- •Функция map
- •Функция filter
- •Композиция функций
- •Функция foldr
- •Функция foldl
- •Свертки: разбор полетов
- •Выявление общей функциональности
- •Стандартные функции высших порядков
- •Еще немного про строгие функции
- •Создание своих типов данных
- •Простые перечислимые типы данных
- •Контейнеры
- •О сравнении, отображении и прочих стандартных операциях
- •Параметрические типы данных
- •Сложные типы данных
- •Тип данных Maybe
- •Рекурсивные типы данных: списки
- •Рекурсивные типы данных: деревья
- •Ввод-вывод
- •Простейший ввод-вывод
- •Объяснение кухни
- •Пример программы, производящей нетривиальное преобразование текстового файла
- •Пример решения задачи: Поиск в пространстве состояний
- •Через массивы и последовательность промежуточных состояний
- •Решение для тех, кто не хочет разбираться сам
- •Через списки, лог истории и уникальную очередь
- •Решение для тех, кто не хочет разбираться сам
- •Задачник
- •Пояснения и обозначения
- •Лабораторная работа 1 Простейшие функции
- •Простейшие логические функции
- •Простейшие списочные функции
- •Лабораторная работа 2 Символьные функции
- •Простейшие кортежные функции
- •Теоретико-множественные операции
- •Сортировка
- •Арифметические последовательности
- •Генераторы списков
- •Лабораторная работа 4 Бесконечные списки
- •Ввод-вывод
- •Нетривиальные функции
- •Лабораторная работа 5 Простые числа и факторизация
- •Деревья
- •Деревья вычислений
- •Дополнительные задания для самостоятельной работы Задания с Project Euler
- •Простейший инструментарий Установка WinHugs и начало работы
- •Работа с интерпретатором WinHugs в интерактивном режиме
- •Команды интерпретатору
- •Работа с модулями
- •Список рекомендуемой литературы и электронных ресурсов
Кортежные функции
Простейшие кортежные функции включают, по сути, только две функции: fst и snd:
fst :: (a,b) -> a
snd :: (a,b) -> b
Обе функции берут кортеж, только первая возвращает первый элемент кортежа, а вторая, кхм... второй, что ли? Проверим:
fst (23,"hello") → 23
snd (23,"hello") → "hello"
Отлично, а где же функция для создания кортежа? А нет такой, собственно. Если нужно создать кортеж из двух значений x и y – надо просто записать их в скобках и разделить запятыми: (x,y).
Из кортежных функций нужно еще обязательно упомянуть функции zip и unzip, мы обязательно их рассмотрим позднее.
Создание своих функций
Как несложно догадаться, в функциональном языке функции играют главную роль, и поэтому способов создания функций в языке предусмотрено аж целых пять.
Способ 1. Определение функции как выражения от параметров:
fact x = x * fact (x-1)
Определение функции в данном случае состоит из имени функции и ее параметров слева – и одного выражения справа, которое показывает, какое значение должна возвращать функция, если ей передали все параметры.
Способ 2. Несколько определений одной функции:
fact 0 = 1
fact x = x * fact (x-1)
Оказывается, функцию можно задать не одним определением, а несколькими. Как компилятор или интерпретатор языка сможет разобраться, какое определение ему использовать, если вы, например, захотели вычислить значение fact 4? Интуитивно понятно, что для вычисления значения fact 4 первое определение нам "не подходит". Чуть позже, когда мы будем говорить об образцах, мы уточним, что это такое "не подходит", а в самом конце курса найдем то единственное абсолютно логичное объяснение, которое и может быть в таком строгом и логичном языке, как Haskell. А пока просто запомним, что если определений несколько – берется первое из них, которое "подходит", то есть позволяет вычислить требуемое значение.
Способ 3. Определение функции через синоним:
productMy = product
Тут все просто – если уже есть какая-то функция product, то мы можем дать ей новое имя productMy. Ну, или по-другому, – можем объявить, что под именем productMy скрывается значение product, имеющее тип функции Num a => [a] -> a.
Ту же самую функцию можно было написать и по-другому:
productMy xs = product xs
Однако между двумя этими способами, идентичными с точки зрения выполнения, эффективности и всего прочего, есть существенная разница. Она показывает, на каком уровне думает программист, написавший их.
Вспомните пример с функцией как ящиком-преобразователем на колесиках! Во втором случае мы описываем, что наш требуемый ящик на колесиках должен делать с тем списком чисел xs, который ему передали – он должен в свою очередь передать этот список функции product, подождать результата, и, в свою очередь, вернуть его. Мы работаем так, как привыкли в любом императивном языке – на уровне данных, передаваемых туда-сюда между функциями и переменными.
А вот в первом случае мы работаем на уровне функций! Мы описываем, как скомпоновать наш требуемый ящик на колесиках из уже имеющихся ящиков – в то время, когда никаких данных еще нет! Чувствуете разницу?
Функциональное программирование – во многом про то, как описывать процесс обработки данных без упоминания данных вообще; как описывать нужный процесс обработки данных в терминах существующих маленьких ящичков-преобразователей.