. После вызова
>scanl1
посмотрим, сколько элементов не превышают 1000. Первый элемент в результате работы функции
>scanl1
должен быть равен единице. Второй будет равен 1 плюс квадратный корень двух. Третий элемент – это корень трёх плюс второй элемент. Если у нас
x сумм меньших 1000, то нам потребовалось (
x+1) элементов, чтобы превзойти 1000.
>sqrtSums :: Int
>sqrtSums = length (takeWhile (< 1000) (scanl1 (+) (map sqrt [1..]))) + 1
>ghci> sqrtSums
>131
>ghci> sum (map sqrt [1..131])
>1005.0942035344083
>ghci> sum (map sqrt [1..130])
>993.6486803921487
Мы задействовали функцию >takeWhile
вместо >filter
, потому что последняя не работает на бесконечных списках. В отличие от нас, функция >filter
не знает, что список возрастает, поэтому мы используем >takeWhile
, чтобы отсечь список, как только сумма превысит 1000.
Применение функций с помощью оператора $
Пойдём дальше. Теперь объектом нашего внимания станет оператор >$
, также называемый аппликатором функций. Прежде всего посмотрим, как он определяется:
>($) :: (a –> b) –> a –> b
>f $ x = f x
Зачем? Что это за бессмысленный оператор? Это просто применение функции! Верно, почти, но не совсем!.. В то время как обычное применение функции (с пробелом) имеет высший приоритет, оператор >$
имеет самый низкий приоритет. Применение функции с пробелом левоассоциативно (то есть >f a b c i
– это то же самое, что >(((f a) b) c))
, в то время как применение функции при помощи оператора >$
правоассоциативно.
Всё это прекрасно, но нам-то с того какая польза? Прежде всего оператор >$
удобен тем, что с ним не приходится записывать много вложенных скобок. Рассмотрим выражение >sum (map sqrt [1..130])
. Поскольку оператор >$
имеет самый низкий приоритет, мы можем переписать это выражение как >sum $ map sqrt [1..130]
, сэкономив драгоценные нажатия на клавиши. Когда в функции встречается знак >$
, выражение справа от него используется как параметр для функции слева от него. Как насчёт >sqrt 3 + 4 + 9
? Здесь складываются >9
, >4
и корень из >3
. Если мы хотим получить квадратный корень суммы, нам надо написать >sqrt (3 + 4 + 9)
– или же (в случае использования оператора >$
) >sqrt $ 3 + 4 + 9
, потому что у оператора >$
низший приоритет среди всех операторов. Вот почему вы можете представить символ >$
как эквивалент записи открывающей скобки с добавлением закрывающей скобки в крайней правой позиции выражения.
Посмотрим ещё на один пример:
>ghci> sum (filter (> 10) (map (*2) [2..10]))
>80
Очень много скобок, даже как-то уродливо. Поскольку оператор $ правоассоциативен, выражение >f (g (z x))
эквивалентно записи