Это, конечно, сработает, но есть способ лучше. Давайте исправим функцию, чтобы она использовала сопоставление с образцом:
>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]
как образец.
Образец вида >(x:xs)
связывает «голову» списка с >x
, а оставшуюся часть – с >xs
, даже если в списке всего один элемент; в этом случае >xs
– пустой список.
ПРИМЕЧАНИЕ. Образец >(x:xs)
используется очень часто, особенно с рекурсивными функциями. Образцы, в определении которых присутствует >:
, могут быть использованы только для списков длиной не менее единицы.
Если вы, скажем, хотите связать первые три элемента с переменными, а оставшиеся элементы списка – с другой переменной, то можете использовать что-то наподобие >(x:y:z:zs)
. Образец сработает только для списков, содержащих не менее трёх элементов.
Теперь, когда мы знаем, как использовать сопоставление с образцом для списков, давайте создадим собственную реализацию функции >head
:
>head' :: [a] –> a
>head' [] = error "Нельзя вызывать head на пустом списке, тупица!"
>head' (x:_) = x
Проверим, работает ли это…
>ghci> head' [4,5,6]
>4
>ghci> head' "Привет"
>H'
Отлично! Заметьте, что если вы хотите выполнить привязку к нескольким переменным (даже если одна из них обозначена всего лишь символом >_
и на самом деле ни с чем не связывается), вам необходимо заключить их в круглые скобки. Также обратите внимание на использование функции >error
. Она принимает строковый параметр и генерирует ошибку времени исполнения, используя этот параметр для сообщения о причине ошибки.