Как и списки, кортежи можно сравнить друг с другом, если можно сравнивать их компоненты. Однако вам не удастся сравнить кортежи разных размеров (хотя списки разных размеров сравниваются, если можно сравнивать их элементы).
Несмотря на то что есть списки с одним элементом, не бывает кортежей с одним компонентом. Если вдуматься, это неудивительно. Кортеж с единственным элементом был бы просто значением, которое он содержит, и, таким образом, не давал бы нам никаких дополнительных возможностей[6].
Использование пар
Вот две полезные функции для работы с парами:
• fst
– принимает пару и возвращает её первый компонент.
ghci> fst (8,11)
8
ghci> fst ("Вау", False)
"Вау"
• snd
– принимает пару и возвращает её второй компонент. Неожиданно!
ghci> snd (8,11)
11
ghci> snd ("Вау", False)
False
ПРИМЕЧАНИЕ. Эти функции работают только с парами. Они не будут работать с тройками, четвёрками, пятёрками и т. д. Выделение данных из кортежей мы рассмотрим чуть позже.
Замечательная функция, производящая список пар, – zip
. Она принимает два списка и сводит их в один, группируя соответствующие элементы в пары. Это очень простая, но крайне полезная функция. Особенно она полезна, когда вы хотите объединить два списка или обойти два списка одновременно. Продемонстрируем работу zip
:
ghci> zip [1,2,3,4,5] [5,5,5,5,5]
[(1,5),(2,5),(3,5),(4,5),(5,5)]
ghci> zip [1 .. 5] ["один", "два", "три", "четыре", "пять"]
[(1,"один"),(2,"два"),(3,"три"),(4,"четыре"),(5,"пять")]
Функция «спаривает» элементы и производит новый список. Первый элемент идёт с первым, второй – со вторым и т. д. Обратите на это внимание: поскольку пары могут содержать разные типы, функция zip
может принять два списка, содержащих разные типы, и объединить их. А что произойдёт, если длина списков не совпадает?
ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["я","не","черепаха"]
[(5,"я"),(3,"не"),(2,"черепаха")]
Более длинный список просто обрезается до длины более короткого! Поскольку язык Haskell ленив, мы можем объединить бесконечный список с конечным:
ghci> zip [1..] ["яблоко", "апельсин", "вишня", "манго"]
[(1,"яблоко"),(2,"апельсин"),(3,"вишня"),(4,"манго")]
В поисках прямоугольного треугольника
Давайте закончим главу задачей, в решении которой пригодятся и генераторы списков, и кортежи. Предположим, что требуется найти прямоугольный треугольник, удовлетворяющий всем следующим условиям:
• длины сторон являются целыми числами;
• длина каждой стороны меньше либо равна 10;
• периметр треугольника (то есть сумма длин сторон) равен 24.
Треугольник называется a
и b
; сторона, противоположная прямому углу, помечена буквой c
. Эта сторона называется
Первым делом построим все тройки, элементы которых меньше либо равны 10:
ghci> let triples = [(a,b,c) | c <– [1..10], b <– [1..10], a <– [1..10]]
Мы просто собираем вместе три списка, и наша производящая функция объединяет их в тройки. Если вы вызовете функцию triples
в GHCi, то получите список из тысячи троек. Теперь добавим условие, позволяющее отфильтровать только те тройки, которые соответствуют длинам сторон b
не больше гипотенузы, и сторона a
не больше стороны b
.
ghci> let rightTriangles = [ (a,b,c) | c <– [1..10], b <– [1..c], a <– [1..b], a 2 + b 2 == c 2]