- •Оглавление
- •От автора
- •Структура
- •Пояснения и обозначения
- •Демонстрация кунг-фу
- •Теория Основные понятия и типы данных
- •Кортежи
- •Функции, операторы
- •Полиморфные типы данных
- •Чтение сигнатур типов
- •Простейшие функции и операторы
- •Арифметические функции
- •Логические функции
- •Списочные функции
- •Кортежные функции
- •Создание своих функций
- •Способ 1. Определение функции как выражения от параметров:
- •Способ 2. Несколько определений одной функции:
- •Способ 3. Определение функции через синоним:
- •Способ 4. Лямбда функция (анонимная функция):
- •Способ 5. Частичное применение функции:
- •Образцы и сопоставление с образцом
- •Синтаксический хлеб и синтаксический сахар
- •Условия и ограничения
- •Локальные определения
- •Двумерный синтаксис
- •Арифметические последовательности
- •Замыкания списков
- •Функциональное мышление
- •Рекурсия как основное средство
- •Ручная редукция выражений
- •Думаем функционально, шаг раз
- •Думаем функционально, шаг два: аккумуляторы
- •Реализация простейших списочных и прочих функций
- •Думаем функционально, шаг три: хвостовая рекурсия
- •Еще раз о рекурсии
- •Полезные хитрости языка
- •Ленивые вычисления и строгие функции
- •Бесконечные списки
- •Функция show
- •Совсем немного о классах
- •Функция read
- •Функция error
- •Побочные эффекты и функция trace
- •Функции высших порядков
- •Мотивация
- •Функция map
- •Функция filter
- •Композиция функций
- •Функция foldr
- •Функция foldl
- •Свертки: разбор полетов
- •Выявление общей функциональности
- •Стандартные функции высших порядков
- •Еще немного про строгие функции
- •Создание своих типов данных
- •Простые перечислимые типы данных
- •Контейнеры
- •О сравнении, отображении и прочих стандартных операциях
- •Параметрические типы данных
- •Сложные типы данных
- •Тип данных Maybe
- •Рекурсивные типы данных: списки
- •Рекурсивные типы данных: деревья
- •Ввод-вывод
- •Простейший ввод-вывод
- •Объяснение кухни
- •Пример программы, производящей нетривиальное преобразование текстового файла
- •Пример решения задачи: Поиск в пространстве состояний
- •Через массивы и последовательность промежуточных состояний
- •Решение для тех, кто не хочет разбираться сам
- •Через списки, лог истории и уникальную очередь
- •Решение для тех, кто не хочет разбираться сам
- •Задачник
- •Пояснения и обозначения
- •Лабораторная работа 1 Простейшие функции
- •Простейшие логические функции
- •Простейшие списочные функции
- •Лабораторная работа 2 Символьные функции
- •Простейшие кортежные функции
- •Теоретико-множественные операции
- •Сортировка
- •Арифметические последовательности
- •Генераторы списков
- •Лабораторная работа 4 Бесконечные списки
- •Ввод-вывод
- •Нетривиальные функции
- •Лабораторная работа 5 Простые числа и факторизация
- •Деревья
- •Деревья вычислений
- •Дополнительные задания для самостоятельной работы Задания с Project Euler
- •Простейший инструментарий Установка WinHugs и начало работы
- •Работа с интерпретатором WinHugs в интерактивном режиме
- •Команды интерпретатору
- •Работа с модулями
- •Список рекомендуемой литературы и электронных ресурсов
Функция filter
Рассмотрим еще несколько функций. Опять-таки, сначала разберитесь, что конкретно делает каждая из этих функций:
foo [] = []
foo (x:xs) | x > 0 = x : foo xs
| otherwise = foo xs
moo [] = []
moo (x:xs) | x `mod` 2 == 0 = x : moo xs
| otherwise = moo xs
boo [] = []
boo (x:xs) | x `elem` ['0'..'9'] = boo xs
| otherwise = x : boo xs
goo [] = []
goo (s:ss) | length s == 0 = goo ss
| otherwise = s : goo ss
Что делают все эти функции? Первая функция берет числовой список и возвращает список только положительных элементов из него. Вторая функция оставляет в числовом списке только четные числа. Третья функция выкидывает из строки (списка символов) цифры. Четвертая функция берет список строк, и выбрасывает из него пустые строки.
Все функции работают по одной и той же схеме: они проходят по всем элементам списка, для каждого элемента проверяют определенное условие, и в зависимости от результатов, либо оставляют в списке элемент, либо безвозвратно выкидывают его.
Думаю, вы уже поняли, что и эти функции следуют одному и тому же шаблону, который легко выписывается в виде еще одной очень часто используемой функции:
filter f [] = []
filter f (x:xs) | f x = x : filter f xs
| otherwise = filter f xs
И здесь опять параметризуется (то есть становится параметром более общей функции) то, что отличает эти четыре функции: условие, по которому принимается решение – выбросить элемент или поместить в результирующий список. Давайте теперь перепишем все функции с помощью функции filter:
foo = filter (>0)
moo = filter (\x -> x `mod` 2 == 0)
boo = filter (\x -> not (x `elem` ['0'..'9']))
goo = filter (\s -> not (null s))
А как насчет функции, которая бы выкидывала отрицательные числа из списка, а положительные увеличивала бы на единицу? Думаю, вы уже догадались – это не filter, потому что filter не может изменить элементы списка, а только выкинуть или не выкинуть.
А как насчет функции, которая бы оставляла в списке только те элементы, которые больше чем предыдущий элемент списка? Опять-таки, это не filter, потому что условие, применяемое функцией filter к каждому элементу списка, является функцией, которая зависит только от значения этого элемента и ни от чего другого.
Если вспомнить функцию map, то функцию filter тоже можно представить в виде робота, который идет вдоль конвейера с предметами, применяет к каждому предмету свое условие, и если условие не выполняется – выкидывает предмет с контейнера. Как и робот для функции map, новый робот тоже видит в каждый момент времени только один элемент, и ничего не может запоминать, переходя от одного элемента к другому.
И функция filter также отлично фильтрует бесконечные списки! Правда, вычисление выражения filter (==2) [1..] не завершится никогда: функции filter неоткуда взять информацию о том, что в списке натуральных чисел число 2 встречается только один раз!