Выбрать главу

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

Функции высшего порядка

Функцией высшего порядка называют функцию, которая может принимать на вход функции или возвращать