where (w, s’’) = break Char. isSpace s’
Если строка пуста, то делать больше нечего. Если – нет, мы также как и в предыдущей функции приме-
няем функцию break для того, чтобы выделить все элементы кроме пробела, а затем рекурсивно вызываем
функцию words на оставшейся части списка.
4.6 Краткое содержание
В этой главе мы узнали очень много новых синтаксических конструкций для определения функций. Они
появлялись парами. Сведём их в таблицу:
Элемент
Декларативный стиль
Композиционный
Локальные переменные
where-выражения
let-выражения
Декомпозиция
Сопоставление с образцом
case-выражения
Условные выражения
Охранные выражения
if-выражения
Определение функций
Уравнения
лямбда-функции
Краткое содержание | 69
Особенности синтаксиса
Нам встретилась новая конструкция в сопоставлении с образцом:
beside :: Nat -> (Nat, Nat)
beside
Zero
= error ”undefined”
beside
x@(Succ y) = (y, Succ x)
Она позволяет проводить декомпозицию и давать имя всему значению одновременно. Такие выражения
x(...)@ в англоязычной литературе принято называть as-patterns.
4.7 Упражнения
• В этой главе нам встретилось много полезных стандартных функций, потренируйтесь с ними в интер-
претаторе. Вызывайте их с различными значениями, экспериментируйте.
• Попробуйте определить функции из предыдущих глав в чисто композиционном стиле.
• Посмотрите на те функции, которые мы прошли и попробуйте переписать их определения шиворот
на выворот. Если вы видите, что элемент написан композиционном стиле перепишите его в деклара-
тивном и наоборот. Получившиеся функции могут показаться монстрами, но это упражнение может
помочь вам в закреплении новых конструкций и почувствовать сильные и слабые стороны того или
иного стиля.
• Определите модуль, который будет вычислять площади простых фигур, треугольника, окружности,
прямоугольника, трапеции. Помните, что фигуры могут задаваться различными способами.
• Поток это бесконечный список, или список, у которого нет конструктора пустого списка:
data Stream a = a :& Stream a
Так например мы можем составить поток из всех чисел Пеано:
nats :: Nat -> Stream Nat
nats a = a :& nats (Succ a)
Или поток, который содержит один и тот же элемент:
constStream :: a -> Stream a
constStream a = a :& constStream a
Напишите модуль для потоков. В первую очередь нам понадобятся функции выделения частей потока,
поскольку мы не сможем распечатать поток целиком (ведь он бесконечный):
-- Первый элемент потока
head :: Stream a -> a
-- Хвост потока, всё кроме первого элемента
tail :: Stream a -> Stream a
-- n-тый элемент потока
(!! ) :: Stream a -> Int -> a
-- Берёт из потока несколько первых элементов:
take :: Int -> Stream a -> [a]
Имена этих функций будут совпадать с именами функций для списков чтобы избежать коллизий имён
мы воспользуемся квалифицированным импортом функций. Делается это так:
import qualified Prelude as P( определения )
Слова qualified и as – ключевые. Теперь для использования функций из модуля Prelude мы будем писать
P.имяФункции. Такие имена называются квалифицированными. Для того чтобы пользоваться квалифициро-
ванными именами только для тех функций, для которых возможна коллизия имён можно поступить так:
70 | Глава 4: Декларативный и композиционный стиль
import qualified Prelude as P
import Prelude
Компилятор разберётся, какую функцию мы имеем в виду.
Для удобства тестирования можно определить такую функцию печати потоков:
instance Show a => Show (Stream a) where
show xs =
showInfinity (show (take 5 xs))
where showInfinity x = P. init x
P.++ ”...”
Функция P. init выделяет все элементы списка кроме последнего. В данном случае она откусит от строки
закрывающуюся скобку. После этого мы добавляем троеточие, как символ бесконечности списка.
Функции преобразования потоков:
-- Преобразование потока
map :: (a -> b) -> Stream a -> Stream b
-- Фильтрация потока
filter :: (a -> Bool) -> Stream a -> Stream a
-- zip-ы для потоков:
zip :: Stream a -> Stream b -> Stream (a, b)
zipWith :: (a -> b -> c) -> Stream a -> Stream b -> Stream c
Функция генерации потока:
iterate :: (a -> a) -> a -> Stream a
Эта функция принимает два аргумента: функцию следующего элемента потока и значение первого эле-
мента потока и возвращает поток:
iterate f a = a :& f a :& f (f a) :& f (f (f a)) :& ...
Так с помощью этой функции можно создать поток всех чисел Пеано от нуля или постоянный поток:
nats
= iterate Succ Zero
constStream a
= iterate (\x -> x) a
Возможно вас удивляет тот факт, что в этом упражнении мы оперируем бесконечными значениями, но
пока мы не будем вдаваться в детали того как это работает, просто попробуйте определить этот модуль и
посмотрите в интерпретаторе, что получится.
Упражнения | 71
Глава 5