haskell-notes

where (Succ (Succ res)) = x

Конечно в этом примере where не нужны, но здесь они приведены для иллюстрации привязки where

выражения к данному уравнению. Мы определили три локальных переменных с одним и тем же именем.

where-выражения могут быть и у значений, которые определяются внутри where-выражений. Но лучше

избегать сильно вложенных выражений.

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

let-выражения

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

square a b c = let p = (a + b + c) / 2

in

sqrt (p * (p a) * (p b) * (p c))

Слова let и in – ключевые. Выгодным отличием let-выражений является то, что они являются обычными

выражениями и не привязаны к определённому месту как where-выражения. Они могут участвовать в любой

части обычного выражения:

square a b c = let p = (a + b + c) / 2

in

sqrt ((let pa = p a in p * pa) *

(let pb = p b

pc = p c

in

pb * pc))

В этом проявляется их принадлежность композиционному стилю. let-выражения могут участвовать в

любом подвыражении, они также группируются скобками. А where-выражения привязаны к уравнениям в

определении функции.

Также как и в where-выражениях, в let-выражениях слева от знака равно можно проводить декомпозицию

значений.

pred :: Nat -> Nat

pred x = let (Succ y) = x

in

y

Определим функцию фильтрации списков через let:

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

filter

p

[]

= []

filter

p

(x:xs) =

let rest = filter p xs

in

if p x then x : rest else rest

4.2 Декомпозиция

Декомпозиция или сопоставление с образцом позволяет выделять из составных значений, простейшие

значения с помощью которых они были построены

pred (Succ x) = x

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

not True

= False

not False = True

Сопоставление с образцом

Декомпозицию в декларативном стиле мы уже изучили, это обычный случай разбора значений в аргу-

ментах функции. Рассмотрим одну полезную возможность при декомпозиции. Иногда нам хочется провести

декомпозицию и дать псевдоним всему значению. Это можно сделать с помощью специального символа @.

Например определим функцию, которая возвращает соседние числа для данного числа Пеано:

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

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

Страницы: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162