Интервалы

А что если нам нужен список всех чисел от 1 до 20? Конечно, мы могли бы просто набрать их подряд, но, очевидно, это не решение для джентльмена, требующего совершенства от языка программирования. Вместо этого мы будем использовать интервалы. Интервалы – это способ создания списков, являющихся арифметическими последовательностями элементов, которые можно перечислить по порядку: один, два, три, четыре и т. п. Символы тоже могут быть перечислены: например, алфавит – это перечень символов от A до Z. А вот имена перечислить нельзя. (Какое, например, имя будет идти после «Иван»? Лично я понятия не имею!)

Чтобы создать список, содержащий все натуральные числа от 1 до 20, достаточно написать [1..20]. Это эквивалентно полной записи [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], и единственная разница в том, что записывать каждый элемент списка, как показано во втором варианте, довольно глупо.

ghci> [1..20]

[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]

ghci> ['a'..'z']

"abcdefghijklmnopqrstuvwxyz"

ghci> ['K'..'Z']

"KLMNOPQRSTUVWXYZ"

Интервалы замечательны ещё и тем, что они позволяют указать шаг. Что если мы хотим внести в список все чётные числа от 1 до 20? Или каждое третье число от 1 до 20?

ghci> [2,4..20]

[2,4,6,8,10,12,14,16,18,20]

ghci> [3,6..20]

[3,6,9,12,15,18]

Нужно всего лишь поставить запятую между первыми двумя элементами последовательности и указать верхний предел диапазона. Но, хотя интервалы достаточно «умны», на их сообразительность не всегда следует полагаться. Вы не можете написать [1,2,4,8,16..100] и после этого ожидать, что получите все степени двойки. Во-первых, потому, что при определении интервала можно указать только один шаг. А во-вторых, потому что некоторые последовательности, не являющиеся арифметическими, неоднозначны, если представлены только несколькими первыми элементами.

ПРИМЕЧАНИЕ. Чтобы создать список со всеми числами от 20 до 1 по убыванию, вы не можете просто написать [20..1], а должны написать [20,19..1]. При попытке записать такой интервал без шага (т. е. [20..1]) Haskell начнёт с пустого списка, а затем будет увеличивать начальный элемент на единицу, пока не достигнет или не превзойдёт элемент в конце интервала. Поскольку 20 уже превосходит 1, результат окажется просто пустым списком.

Будьте осторожны при использовании чисел с плавающей точкой в интервалах! Из-за того что они не совсем точны (по определению), их использование в диапазонах может привести к весьма забавным результатам.

ghci> [0.1, 0.3 .. 1]

[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]

Мой совет: не используйте такие числа в интервалах!

Интервалы, кроме прочего, можно использовать для создания бесконечных списков, просто не указывая верхний предел. Позже мы рассмотрим этот вариант в подробностях. А сейчас давайте посмотрим, как можно получить список первых 24 чисел, кратных 13. Конечно, вы могли бы написать [13,26..24*13]. Но есть способ получше: take 24 [13,26..]. Поскольку язык Haskell ленив, он не будет пытаться немедленно вычислить бесконечный список, потому что процесс никогда не завершится. Он подождёт, пока вы не захотите получить что-либо из такого списка. Тут-то обнаружится, что вы хотите получить только первые 24 элемента, что и будет исполнено.

Немного функций, производящих бесконечные списки:

• Функция cycle принимает список и зацикливает его в бесконечный. Если вы попробуете отобразить результат, на это уйдёт целая вечность, поэтому вам придётся где-то его обрезать.

ghci> take 10 (cycle [1,2,3])

[1,2,3,1,2,3,1,2,3,1]

ghci> take 12 (cycle "LOL ")

"LOL LOL LOL "

• Функция repeat принимает элемент и возвращает бесконечный список, состоящий только из этого элемента. Это подобно тому, как если бы вы зациклили список из одного элемента.

ghci> take 10 (repeat 5)

[5,5,5,5,5,5,5,5,5,5]

Однако проще использовать функцию replicate, если вам нужен список из некоторого количества одинаковых элементов. replicate 3 10 вернёт [10,10,10].