Изучай Haskell во имя добра! (Липовача) - страница 65

>sum' xs = foldl (+) 0 xs

Образец >xs представлен дважды с правой стороны. Из–за каррирования мы можем пропустить образец >xs с обеих сторон, так как >foldl (+) 0 создаёт функцию, которая принимает на вход список. Если мы запишем эту функцию как >sum' = foldl (+) 0, такая запись будет называться бесточечной. А как записать следующее выражение в бесточечном стиле?

>fn x = ceiling (negate (tan (cos (max 50 x))))

Мы не можем просто избавиться от образца >x с обеих правых сторон выражения. Образец >x в теле функции заключён в скобки. Выражение >cos (max 50) не будет иметь никакого смысла. Вы не можете взять косинус от функции! Всё, что мы можем сделать, – это выразить функцию >fn в виде композиции функций.

>fn = ceiling . negate . tan . cos . max 50

Отлично! Во многих случаях бесточечная запись легче читается и более лаконична; она заставляет думать о функциях, о том, как их соединение порождает результат, а не о данных и способе их передачи. Можно взять простые функции и использовать композицию как «клей» для создания более сложных. Однако во многих случаях написание функций в бесточечном стиле может делать код менее «читабельным», особенно если функция слишком сложна. Вот почему я не рекомендую создавать длинные цепочки функций, хотя меня частенько обвиняли в пристрастии к композиции. Предпочитаемый стиль – использование выражения >let для присвоения меток промежуточным результатам или разбиение проблемы на подпроблемы и их совмещение таким образом, чтобы функции имели смысл для того, кто будет их читать, а не представляли собой огромную цепочку композиций.

Ранее в этой главе мы решали задачу, в которой требовалось найти сумму всех нечётных квадратов меньших 10 000. Вот как будет выглядеть решение, если мы поместим его в функцию:

>oddSquareSum :: Integer

>oddSquareSum = sum (takeWhile (<10000) (filter odd (map ( 2) [1..])))

Со знанием композиции функций этот код можно переписать так:

>oddSquareSum :: Integer

>oddSquareSum = sum . takeWhile (<10000) . filter odd $ map ( 2) [1..]

Всё это на первый взгляд может показаться странным, но вы быстро привыкнете. В подобных записях меньше визуального «шума», поскольку мы убрали все скобки. При чтении такого кода можно сразу сказать, что >filter odd применяется к результату >map ( 2) [1..], что затем применяется >takeWhile (<10000), а функция >sum суммирует всё, что получилось в результате.

6

Модули


В языке Haskell модуль – это набор взаимосвязанных функций, типов и классов типов. Программа на Haskell – это набор модулей; главный модуль подгружает все остальные и использует функции, определённые в них, чтобы что-либо сделать. Разбиение кода на несколько модулей удобно по многим причинам. Если модуль достаточно общий, экспортируемые им функции могут быть использованы во множестве программ. Если ваш код разделён на несколько самостоятельных модулей, не очень зависящих один от другого (мы говорим, что они слабо связаны), модули могут многократно использоваться в разных проектах. Это отчасти облегчает непростую задачу написания кода, разбивая его на несколько частей, каждая из которых имеет некоторое назначение.