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

beside :: Nat -> (Nat, Nat)

beside

Zero

= error ”undefined”

beside

x@(Succ y) = (y, Succ x)

В выражении x“(Succ y)@ мы одновременно проводим разбор и даём имя всему значению.

Декомпозиция | 61

case-выражения

Оказывается декомпозицию можно проводить в любом выражении, для этого существуют case-

выражения:

data AnotherNat = None | One | Two | Many

deriving (Show, Eq)

toAnother :: Nat -> AnotherNat

toAnother x =

case x of

Zero

-> None

Succ Zero

-> One

Succ (Succ Zero)

-> Two

_

-> Many

fromAnother :: AnotherNat -> Nat

fromAnother None

= Zero

fromAnother One

= Succ Zero

fromAnother Two

= Succ (Succ Zero)

fromAnother Many

= error ”undefined”

Слова case и of – ключевые. Выгодным отличием case-выражений является то, что нам не приходит-

ся каждый раз выписывать имя функции. Обратите внимание на то, что в case-выражениях также можно

пользоваться обычными переменными и безымянными переменными.

Для проведения декомпозиции по нескольким переменным можно воспользоваться кортежами. Например

определим знакомую функцию равенства для Nat:

instance Eq Nat where

(==) a b =

case (a, b) of

(Zero,

Zero)

-> True

(Succ a’, Succ b’)

-> a’ == b’

_

-> False

Мы проводим сопоставление с образцом по кортежу (a, b), соответственно слева от знака -> мы прове-

ряем значения в кортежах, для этого мы также заключаем значения в скобки и пишем их через запятую.

Давайте определим функцию filter в ещё более композиционном стиле. Для этого мы заменим в исход-

ном определении where на let и декомпозицию в аргументах на case-выражение:

filter :: (a -> Bool) -> [a] -> [a]

filter

p

a =

case a of

[]

-> []

x:xs

->

let rest = filter p xs

in

if (p x)

then (x:rest)

else rest

4.3 Условные выражения

С условными выражениями мы уже сталкивались в сопоставлении с образцом. Например в определении

функции not:

not True

= False

not False = True

В зависимости от поступающего значения мы выбираем одну из двух альтернатив. Условные выражении

в сопоставлении с образцом позволяют реагировать лишь на частичное (с учётом переменных) совпадение

дерева значения в аргументах функции.

Часто нам хочется определить более сложные условия для альтернатив. Например, если значение на

входе функции больше 2, но меньше 10, верни A, а если больше 10, верни B, а во всех остальных случаях

верни C. Или если на вход поступила строка состоящая только из букв латинского алфавита, верни A, а

в противном случае верни B. Нам бы хотелось реагировать лишь в том случае, если значение некоторого

типа a удовлетворяет некоторому предикату. Предикатами обычно называют функции типа a -> Bool. Мы

говорим, что значение удовлетворяет предикату, если предикат для этого значения возвращает True.

62 | Глава 4: Декларативный и композиционный стиль

Охранные выражения

В декларативном стиле условные выражения представлены охранными выражениями (guards). Предполо-

жим у нас есть тип:

data HowMany = Little | Enough | Many

И мы хотим написать функцию, которая принимает число людей, которые хотят посетить выставку, а

возвращает значение типа HowMany. Эта функция оценивает вместительность выставочного зала. С помощью

охранных выражений мы можем написать её так:

hallCapacity :: Int -> HowMany

hallCapacity n

| n < 10

= Little

| n < 30

= Enough

| True

= Many

Специальный символ | уже встречался нам в определении типов. Там он играл роль разделителя аль-

тернатив в сумме типов. Здесь же он разделяет альтернативы в условных выражениях. Сначала мы пишем

| затем выражение-предикат, которое возвращает значение типа Bool, затем равно и после равно – возвра-

щаемое значение. Альтернативы так же как и в случае декомпозиции аргументов функции обходятся сверху

вниз, до тех пор пока в одной из альтернатив предикат не вернёт значение True. Обратите внимание на то,

что нам не нужно писать во второй альтернативе:

| 10 <= n && n < 30

= Enough

Если вычислитель дошёл до этой альтернативы, значит значение точно больше либо равно 10. Поскольку

в предыдущей альтернативе предикат вернул False.

Предикат в последней альтернативе является константой True, он пройдёт сопоставление с любым зна-

чением n. В данном случае, если учесть предыдущие альтернативы мы знаем, что если вычислитель дошёл

до последней альтернативы , значение n больше либо равно 30. Для повышения наглядности кода в Prelude

определена специальная константа-синоним значению True под именем otherwise.

Определим функцию filter для списков в более декларативном стиле, для этого заменим if-выражение

в исходной версии на охранные выражения:

filter :: (a -> Bool) -> [a] -> [a]

filter

p

[]

= []

filter

p

(x:xs)

| p x

= x : rest

| otherwise

= rest

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

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

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

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

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

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