Читаем Учебник по Haskell полностью

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

Перейти на страницу:

Похожие книги

C++: базовый курс
C++: базовый курс

В этой книге описаны все основные средства языка С++ - от элементарных понятий до супервозможностей. После рассмотрения основ программирования на C++ (переменных, операторов, инструкций управления, функций, классов и объектов) читатель освоит такие более сложные средства языка, как механизм обработки исключительных ситуаций (исключений), шаблоны, пространства имен, динамическая идентификация типов, стандартная библиотека шаблонов (STL), а также познакомится с расширенным набором ключевых слов, используемым в .NET-программировании. Автор справочника - общепризнанный авторитет в области программирования на языках C и C++, Java и C# - включил в текст своей книги и советы программистам, которые позволят повысить эффективность их работы. Книга рассчитана на широкий круг читателей, желающих изучить язык программирования С++.

Герберт Шилдт

Программирование, программы, базы данных