- •Оглавление
- •От автора
- •Структура
- •Пояснения и обозначения
- •Демонстрация кунг-фу
- •Теория Основные понятия и типы данных
- •Кортежи
- •Функции, операторы
- •Полиморфные типы данных
- •Чтение сигнатур типов
- •Простейшие функции и операторы
- •Арифметические функции
- •Логические функции
- •Списочные функции
- •Кортежные функции
- •Создание своих функций
- •Способ 1. Определение функции как выражения от параметров:
- •Способ 2. Несколько определений одной функции:
- •Способ 3. Определение функции через синоним:
- •Способ 4. Лямбда функция (анонимная функция):
- •Способ 5. Частичное применение функции:
- •Образцы и сопоставление с образцом
- •Синтаксический хлеб и синтаксический сахар
- •Условия и ограничения
- •Локальные определения
- •Двумерный синтаксис
- •Арифметические последовательности
- •Замыкания списков
- •Функциональное мышление
- •Рекурсия как основное средство
- •Ручная редукция выражений
- •Думаем функционально, шаг раз
- •Думаем функционально, шаг два: аккумуляторы
- •Реализация простейших списочных и прочих функций
- •Думаем функционально, шаг три: хвостовая рекурсия
- •Еще раз о рекурсии
- •Полезные хитрости языка
- •Ленивые вычисления и строгие функции
- •Бесконечные списки
- •Функция show
- •Совсем немного о классах
- •Функция read
- •Функция error
- •Побочные эффекты и функция trace
- •Функции высших порядков
- •Мотивация
- •Функция map
- •Функция filter
- •Композиция функций
- •Функция foldr
- •Функция foldl
- •Свертки: разбор полетов
- •Выявление общей функциональности
- •Стандартные функции высших порядков
- •Еще немного про строгие функции
- •Создание своих типов данных
- •Простые перечислимые типы данных
- •Контейнеры
- •О сравнении, отображении и прочих стандартных операциях
- •Параметрические типы данных
- •Сложные типы данных
- •Тип данных Maybe
- •Рекурсивные типы данных: списки
- •Рекурсивные типы данных: деревья
- •Ввод-вывод
- •Простейший ввод-вывод
- •Объяснение кухни
- •Пример программы, производящей нетривиальное преобразование текстового файла
- •Пример решения задачи: Поиск в пространстве состояний
- •Через массивы и последовательность промежуточных состояний
- •Решение для тех, кто не хочет разбираться сам
- •Через списки, лог истории и уникальную очередь
- •Решение для тех, кто не хочет разбираться сам
- •Задачник
- •Пояснения и обозначения
- •Лабораторная работа 1 Простейшие функции
- •Простейшие логические функции
- •Простейшие списочные функции
- •Лабораторная работа 2 Символьные функции
- •Простейшие кортежные функции
- •Теоретико-множественные операции
- •Сортировка
- •Арифметические последовательности
- •Генераторы списков
- •Лабораторная работа 4 Бесконечные списки
- •Ввод-вывод
- •Нетривиальные функции
- •Лабораторная работа 5 Простые числа и факторизация
- •Деревья
- •Деревья вычислений
- •Дополнительные задания для самостоятельной работы Задания с Project Euler
- •Простейший инструментарий Установка WinHugs и начало работы
- •Работа с интерпретатором WinHugs в интерактивном режиме
- •Команды интерпретатору
- •Работа с модулями
- •Список рекомендуемой литературы и электронных ресурсов
Тип данных Maybe
Это мой любимый, веселый, самокритичный тип данных, вот как он определяется:
data Maybe a = Nothing | Just a
deriving (Eq, Ord, Read, Show)
Где взять значения этого типа? А вот их примеры:
Nothing :: Maybe a
Just True :: Maybe Bool
Just 1 :: Num a => Maybe a
Just "ice" :: Maybe [Char]
Зачем может быть нужен такой тип? А давайте представим себе функцию, которая ищет значение в ассоциативном массиве по ключу. Пусть есть список кортежей [(a,b)], причем тип a допускает сравнение на равенство. И пусть у нас есть какое-то значение типа a. Мы хотим написать функцию, которая будет искать в списке кортежей пару (a,b), у которой значение a совпадает с искомым, и возвращает значение b из этой пары. Какой должен быть тип у этой функции? Может быть, нужно возвращать просто значение типа b?
lookup :: Eq a => a -> [(a,b)] -> b
Но ведь поиск в массиве может оказаться и неудачным, что возвращать тогда? Может быть, нужно возвращать список значений типа b? Тогда, если функция ничего не нашла, она сможет вернуть пустой список:
lookup :: Eq a => a -> [(a,b)] -> [b]
Но в этом случае тип этой функции может ввести кого-нибудь в заблуждение: можно будет подумать, что функция вполне может вернуть несколько значений, а не только одно или ноль.
Как раз в таких ситуациях и пригодится тип Maybe. Он как бы дополняет нашу функцию контекстом – дополнительной информацией о том, что функция может и ничего не найти:
lookup:: Eq a => a -> [(a,b)] -> Maybe b
lookup key [] = Nothing
lookup key ((k,v):dict)
| key == k = Just v
| otherwise = lookup key dict
Вот оно: из типа этой функции сразу становится ясно: функция берет значение ключа и словарь, и может быть возвратит значение из словаря, сопоставленное с ключом. Давайте проверим:
lookup 2 [(1,"one"),(2,"two"),(3,"three")] → Just "two"
lookup 4 [(1,"one"),(2,"two"),(3,"three")] → Nothing
А как работать с значениями Maybe String, ведь именно такие значения вернула нам функция? Можно, например, взять и склеить эту строку с другой?
SomeModule> Just "two" ++ " is a lot"
ERROR - Type error in application
*** Expression : Just "two" ++ " is a lot"
*** Term : Just "two"
*** Type : Maybe [Char]
*** Does not match : [Char]
В чем тут проблема? Да в том, что функция (++ " is a lot") ожидает, что ей передадут значение типа String, а ей передают Maybe String. А это, как говорится – две большие разницы. Значение Maybe String нельзя просто так сливать с другой строкой, ведь Maybe String может и пустым, Nothing.
Другими словами, тип возвращаемого функцией lookup значения "заражен" контекстом возможной неудачи lookup, контекстом возможной пустоты. Мы, конечно, можем преобразовать значение этого типа в обычную строку – но мы тогда должны взять на себя ответственность за то, как в обычную строку должно преобразовываться значение Nothing:
maybeToString :: Maybe String -> String
maybeToString Nothing = ""
maybeToString (Just s) = s
Вот теперь можно попробовать и сложить:
maybeToString (Just "two") ++ " is a lot" → "two is a lot"
maybeToString (lookup 3 [(1,"one"),(2,"two")]) ++ " is a lot" → " is a lot"