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 aconstStream 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
Pimport Prelude
Компилятор разберётся, какую функцию мы имеем в виду.
Для удобства тестирования можно определить такую функцию печати потоков:
instance Show
a => Show (Stream a) whereshow xs =
showInfinity (show (take 5 xs))
where
showInfinity x = P. init xP.++
”...”Функция 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 ZeroconstStream a
=
iterate (\x -> x) aВозможно вас удивляет тот факт, что в этом упражнении мы оперируем бесконечными значениями, но
пока мы не будем вдаваться в детали того как это работает, просто попробуйте определить этот модуль и
посмотрите в интерпретаторе, что получится.
Упражнения | 71
Глава 5