
- •Оглавление
- •От автора
- •Структура
- •Пояснения и обозначения
- •Демонстрация кунг-фу
- •Теория Основные понятия и типы данных
- •Кортежи
- •Функции, операторы
- •Полиморфные типы данных
- •Чтение сигнатур типов
- •Простейшие функции и операторы
- •Арифметические функции
- •Логические функции
- •Списочные функции
- •Кортежные функции
- •Создание своих функций
- •Способ 1. Определение функции как выражения от параметров:
- •Способ 2. Несколько определений одной функции:
- •Способ 3. Определение функции через синоним:
- •Способ 4. Лямбда функция (анонимная функция):
- •Способ 5. Частичное применение функции:
- •Образцы и сопоставление с образцом
- •Синтаксический хлеб и синтаксический сахар
- •Условия и ограничения
- •Локальные определения
- •Двумерный синтаксис
- •Арифметические последовательности
- •Замыкания списков
- •Функциональное мышление
- •Рекурсия как основное средство
- •Ручная редукция выражений
- •Думаем функционально, шаг раз
- •Думаем функционально, шаг два: аккумуляторы
- •Реализация простейших списочных и прочих функций
- •Думаем функционально, шаг три: хвостовая рекурсия
- •Еще раз о рекурсии
- •Полезные хитрости языка
- •Ленивые вычисления и строгие функции
- •Бесконечные списки
- •Функция show
- •Совсем немного о классах
- •Функция read
- •Функция error
- •Побочные эффекты и функция trace
- •Функции высших порядков
- •Мотивация
- •Функция map
- •Функция filter
- •Композиция функций
- •Функция foldr
- •Функция foldl
- •Свертки: разбор полетов
- •Выявление общей функциональности
- •Стандартные функции высших порядков
- •Еще немного про строгие функции
- •Создание своих типов данных
- •Простые перечислимые типы данных
- •Контейнеры
- •О сравнении, отображении и прочих стандартных операциях
- •Параметрические типы данных
- •Сложные типы данных
- •Тип данных Maybe
- •Рекурсивные типы данных: списки
- •Рекурсивные типы данных: деревья
- •Ввод-вывод
- •Простейший ввод-вывод
- •Объяснение кухни
- •Пример программы, производящей нетривиальное преобразование текстового файла
- •Пример решения задачи: Поиск в пространстве состояний
- •Через массивы и последовательность промежуточных состояний
- •Решение для тех, кто не хочет разбираться сам
- •Через списки, лог истории и уникальную очередь
- •Решение для тех, кто не хочет разбираться сам
- •Задачник
- •Пояснения и обозначения
- •Лабораторная работа 1 Простейшие функции
- •Простейшие логические функции
- •Простейшие списочные функции
- •Лабораторная работа 2 Символьные функции
- •Простейшие кортежные функции
- •Теоретико-множественные операции
- •Сортировка
- •Арифметические последовательности
- •Генераторы списков
- •Лабораторная работа 4 Бесконечные списки
- •Ввод-вывод
- •Нетривиальные функции
- •Лабораторная работа 5 Простые числа и факторизация
- •Деревья
- •Деревья вычислений
- •Дополнительные задания для самостоятельной работы Задания с Project Euler
- •Простейший инструментарий Установка WinHugs и начало работы
- •Работа с интерпретатором WinHugs в интерактивном режиме
- •Команды интерпретатору
- •Работа с модулями
- •Список рекомендуемой литературы и электронных ресурсов
Функция show
Функция show используется тогда, когда нужно значение определенного типа превратить в строку, вот ее тип:
show :: Show a => a -> String
Предлагаю вам вместе со мной порадоваться этой логичности языка: функция show умеет показывать (преобразовывать в строку) значения только тех типов, которые относятся к классу Show, то есть умеют преобразовываться в строку. А что – логично:
show 1 → "1"
show True → "True"
show "hello" → "\"hello\""
Классу Show принадлежит большинство простейших типов, и, что еще важнее, производные от них сложные структуры. Например, если система умеет отображать число (а она умеет), то она умеет отображать и список чисел, как и список любых других отображаемых значений – для этого система напечатает открывающую квадратную скобку "[", потом через запятую покажет преобразованные в строку элементы списка, а потом закрывающую скобку "]". То же касается и кортежей:
show [1..3] → "[1,2,3]"
show (3, "hello", [1,2,3]) → "(3,\"hello\",[1,2,3])"
На самом деле, когда вы печатаете выражение в командной строке интерпретатора, он вычисляет его значение, а потом преобразовывает его в строку, чтобы вывести на экран. Именно поэтому, если типом выражения будет нечто странное (например, функция), вы увидите ошибку, связанную с тем, что функции show не удается правильно работать с элементами этого типа:
Prelude> sin
ERROR - Cannot find "show" function for:
*** Expression : sin
*** Of type : Double -> Double
Совсем немного о классах
Давайте вспомним, как у нас появилось понятие класса типов. Мы разбирались, применительно к каким типам должна работать, например, операция (>), и решили, что ограничивать ее каким-то одним типом нельзя, но и работать со всеми возможными типами эта операция, очевидно, не может. Тогда и выяснилось, что работать операция (>) может только над типами, входящими в класс Ord, то есть над типами, допускающими сравнение на больше-меньше. Довольно тавтологично, не так ли?
(>) :: Ord a => a -> a -> Bool
В прошлой главе мы встретили функцию show, которая умеет показывать (преобразовывать в строку) значения тех типов, которые относятся к классу Show, то есть умеют преобразовываться в строку. Давайте посмотрим на сам класс Show:
class Show a where
show :: a -> String
…
Когда определяется класс, задаются все функции, которые должен реализовывать тип, для того, чтобы иметь право принадлежать этому классу. Классы в Haskell – это что-то вроде интерфейсов, или абстрактных классов во многих развитых объектно-ориентированных языках: они перечисляют операции, но не задают никакой реализации. Последнее, правда, для Haskell не совсем верно, вот посмотрите на полное объявление класса Eq:
class Eq a where
(==), (/=) :: a -> a -> Bool
-- Minimal complete definition: (==) or (/=)
x == y = not (x/=y)
x /= y = not (x==y)
Класс Eq заявляет: все, кто хочет сертифицироваться на принадлежность мне, должен будет предоставить мне одно из двух – или свою операцию равенства, или операцию неравенства. То бишь, если ты какой-нибудь Integer, то расскажи классу Eq о том, как сравнивать два значения своего типа, и получи отметку о полном служебном соответствии.
Записка на полях: кстати, а иккс-иггрек-зеет одинаков со строкой "Зачет внимательным читателям", запомните это – вдруг пригодится?
И что тогда? А тогда операция (==) сможет сравнивать и значения твоего типа Integer. Вот он, полиморфизм в стиле Haskell в действии. Видите аналогию? В терминах ООП можно сказать, что операция (==) работает с типами, у которых есть перекрытая реализация полиморфной функции сравнения.
А что же с классом Show? Если у вас есть какой-нибудь новосозданный тип данных, то функция show с ним работать, конечно, не будет. А вот если вы расскажете, как ваш тип преобразовывать в строку, то получите сертификат о том, что ваш тип теперь принадлежит классу Show, и тогда функция show сможет его показывать. Разобрались в тавтологиях?
Позже, когда будем создавать свои собственные типы, мы увидим конкретные примеры.