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

sayMe :: Int -> String

sayMe 1 = "Один!"

sayMe 2 = "Два!"

sayMe 3 = "Три!"

sayMe 4 = "Четыре!"

sayMe 5 = "Пять!"

sayMe x = "Это число не в пределах от 1 до 5"

Заметьте, что если бы мы переместили последнюю строку определения функции (образец в которой соответствует любому вводу) вверх, то функция всегда выводила бы "Это число не в пределах от 1 до 5", потому что невозможно было бы пройти дальше и провести проверку на совпадение с другими образцами.

Помните реализованную нами функцию факториала? Мы определили факториал числа n как произведение чисел [1..n]. Мы можем определить данную функцию рекурсивно, точно так же, как факториал определяется в математике. Начнём с того, что объявим факториал нуля равным единице.

Затем определим факториал любого положительного числа как данное число, умноженное на факториал предыдущего числа. Вот как это будет выглядеть в терминах языка Haskell.

factorial :: Integer -> Integer

factorial 0 = 1

factorial n = n * factorial (n – 1)

Мы в первый раз задали функцию рекурсивно. Рекурсия очень важна в языке Haskell, и подробнее она будет рассмотрена позже.

Сопоставление с образцом может завершиться неудачей, если мы зададим функцию следующим образом:

charName :: Char –> String

charName 'а' = "Артём"

charName 'б' = "Борис"

charName 'в' = "Виктор"

а затем попытаемся вызвать её с параметром, которого не ожидали. Произойдёт следующее:

ghci> charName 'а'

"Артём"

ghci> charName 'в'

"Виктор"

ghci> charName 'м'

"*** Exception: Non-exhaustive patterns in function charName

Это жалоба на то, что наши образцы не покрывают всех возможных случаев (недоопределены) – и, воистину, так оно и есть! Когда мы определяем функцию, мы должны всегда включать образец, который можно сопоставить с любым входным значением, для того чтобы наша программа не закрывалась с сообщением об ошибке, если функция получит какие-то непредвиденные входные данные.

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

Сопоставление с образцом может быть использовано и для кортежей. Что если мы хотим создать функцию, которая принимает два двумерных вектора (представленных в форме пары) и складывает их? Чтобы сложить два вектора, нужно сложить их соответствующие координаты. Вот как мы написали бы такую функцию, если б не знали о сопоставлении с образцом:

addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)

addVectors a b = (fst a + fst b, snd a + snd b)

Это, конечно, сработает, но есть способ лучше. Давайте исправим функцию, чтобы она использовала сопоставление с образцом:

addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)

addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

Так гораздо лучше. Теперь ясно, что параметры функции являются кортежами; к тому же компонентам кортежа сразу даны имена – это повышает читабельность. Заметьте, что мы сразу написали образец, соответствующий любым значениям. Тип функции addVectors в обоих случаях совпадает, так что мы гарантированно получим на входе две пары:

ghci> :t addVectors

addVectors :: (Double, Double) -> (Double, Double) -> (Double, Double)

Функции fst и snd извлекают компоненты пары. Но как быть с тройками? Увы, стандартных функций для этой цели не существует, однако мы можем создать свои:

first :: (a, b, c) –> a

first (x, _, _) = x

second :: (a, b, c) –> b

second (_, y, _) = y

third :: (a, b, c) –> c

third (_, _, z) = z

Символ _ имеет то же значение, что и в генераторах списков. Он означает, что нам не интересно значение на этом месте, так что мы просто пишем _.

Сопоставление со списками и генераторы списков

В генераторах списков тоже можно использовать сопоставление с образцом, например:

ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]

ghci> [a+b | (a,b) <– xs]

[4,7,6,8,11,4]

Если сопоставление с образцом закончится неудачей для одного элемента списка, просто произойдёт переход к следующему элементу.

Списки сами по себе (то есть заданные прямо в тексте образца списковые литералы) могут быть использованы при сопоставлении с образцом. Вы можете проводить сравнение с пустым списком или с любым образцом, который включает оператор : и пустой список. Так как выражение [1,2,3] – это просто упрощённая запись выражения 1:2:3:[], можно использовать [1,2,3] как образец.