Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь FAQ Написать работу КАТЕГОРИИ: ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву
Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Композиция функций с несколькими параметрамиСодержание книги
Поиск на нашем сайте
Ну а как насчёт функций, которые принимают несколько парамет- ров? Если мы хотим использовать их в композиции, обычно мы час- тично применяем их до тех пор, пока не получим функцию, прини- мающую только один параметр. Запись
sum (replicate 5 (max 6.7 8.9)) может быть преобразована так: (sum. replicate 5) (max 6.7 8.9)
sum. replicate 5 $ max 6.7 8.9 Функция replicate 5 применяется к результату вычисления max 6.7 8.9, после чего элементы полученного списка суммируются. Об- ратите внимание, что функция replicate частично применена так, чтобы у неё остался только один параметр, так что теперь результат max 6.7 8.9 передаётся на вход replicate 5; новым результатом оказы- вается список чисел, который потом передаётся функции sum. Если вы хотите переписать выражение с кучей скобок, исполь- зуя функциональную композицию, можно сначала записать самую внутреннюю функцию с её параметрами, затем поставить перед ней знак $, а после этого пристраивать вызовы всех других функ- ций, записывая их без последнего параметра и разделяя точками.
Например, выражение replicate 2 (product (map (*3) (zipWith max [1,2] [4,5])))
можно переписать так: replicate 2. product. map (*3) $ zipWith max [1,2] [4,5]
Как из одного выражения получилось другое? Ну, во-первых, мы посмотрели на самую правую функцию и её параметры как раз перед группой закрывающихся скобок. Это функция zipWith max [1,2] [4,5]. Так её и запишем: zipWith max [1,2] [4,5]
Затем смотрим на функцию, которая применяется к zipWith max [1,2] [4,5], это map (*3). Поэтому мы ставим между ней и тем, что было раньше, знак $: map (*3) $ zipWith max [1,2] [4,5]
Теперь начинаются композиции. Проверяем, какая функция применяется ко всему этому, и присоединяем её к map (*3): product. map (*3) $ zipWith max [1,2] [4,5]
Наконец, дописываем функцию replicate 2 и получаем оконча- тельное выражение: replicate 2. product. map (*3) $ zipWith max [1,2] [4,5]
Если выражение заканчивалось на три закрывающие скобки, велики шансы, что у вас получится два оператора композиции.
Бесточечная нотация
Композиция функций часто используется и для так называемого бесточечного стиля записи функций. Возьмём, для примера, функ- цию, которую мы написали ранее: sum':: (Num a) => [a] –> a 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 суммирует всё, что получилось в результате.
МОДУЛИ
Если модуль достаточно общий, экспортируемые им функции мо- гут быть использованы во множестве программ. Если ваш код раз- делён на несколько самостоятельных модулей, не очень зависящих один от другого (мы говорим, что они слабо связаны), модули могут многократно использоваться в разных проектах. Это отчасти об- легчает непростую задачу написания кода, разбивая его на несколь- ко частей, каждая из которых имеет некоторое назначение. Стандартная библиотека языка Haskell разбита на модули, каж- дый из которых содержит взаимосвязанные функции и типы, слу- жащие некоторой общей цели. Есть модуль для работы со списками, модуль для параллельного программирования, модуль для работы с комплексными числами и т. д. Все функции, типы и классы типов, с которыми мы имели дело до сих пор, были частью стандартного ИМПОРТ МОДУЛЕЙ 123
модуля Prelude – он импортируется по умолчанию. В этой главе мы познакомимся с несколькими полезными модулями и их функция- ми. Но для начала посмотрим, как импортировать модули.
Импорт модулей Синтаксис для импорта модулей в программах на языке Haskell – import ModuleName. Импортировать модули надо прежде, чем вы при- ступите к определению функций, поэтому обычно импорт делается в начале файла. Конечно же, одна программа может импортиро- вать несколько модулей. Для этого вынесите каждый оператор import в отдельную строку.
Давайте импортируем модуль Data.List, который содержит мас- су функций для работы со списками, и используем экспортируемую им функцию для того, чтобы написать свою – вычисляющую, сколь- ко уникальных элементов содержит список. import Data.List
numUniques:: (Eq a) => [a] –> Int numUniques = length. nub Когда выполняется инструкция import Data.List, все функ- ции, экспортируемые модулем Data.List, становятся доступными в глобальном пространстве имён. Это означает, что вы можете вы- зывать их из любого места программы. Функция nub определена в модуле Data.List; она принимает список и возвращает список, из которого удалены дубликаты элементов исходного списка. Компо- зиция функций length и nub создаёт функцию, которая эквивалент- на \xs –> length (nub xs). ПРИМЕЧАНИЕ. Чтобы найти нужные функции и уточнить, где они определены, воспользуйтесь сервисом Hoogle, который досту- пен по адресу http://www.haskell.org/hoogle/. Это поистине уди- вительный поисковый механизм для языка Haskell, который поз- воляет вести поиск по имени функции, по имени модуля и даже по сигнатуре. В интерпретаторе GHCi вы также можете подключить функ- ции из модулей к глобальному пространству имён. Если вы работа- ете в GHCi и хотите вызывать функции, экспортируемые модулем Data.List, напишите следующее:
ghci>:m + Data.List Если требуется подгрузить программные сущности из несколь- ких модулей, не надо вызывать команду:m + несколько раз, так как можно загрузить ряд модулей одновременно:
ghci>:m + Data.List Data.Map Data.Set Кроме того, если вы загрузили скрипт, который импортирует модули, то не нужно использовать команду:m +, чтобы получить к ним доступ.
Если вам необходимо всего несколько функций из модуля, вы можете выборочно импортировать только эти функции. Если бы вам были нужны только функции nub и sort из модуля Data.List, им- порт выглядел бы так: import Data.List (nub, sort)
Также вы можете осуществить импорт всех функций из модуля за исключением некоторых. Это бывает полезно, когда несколько модулей экспортируют функции с одинаковыми именами, и вы хо- тите избавиться от ненужных повторов. Предположим, у вас уже есть функция с именем nub и вы хотите импортировать все функ- ции из модуля Data.List, кроме nub, определённой в нём: import Data.List hiding (nub)
Другой способ разрешения конфликтов имён – квалифици- рованный импорт. Модуль Data.Map, который содержит структуру данных для поиска значения по ключу, экспортирует несколько функций с теми же именами, что и модуль Prelude, например filter и null. Если мы импортируем модуль Data.Map и вызовем функцию filter, язык Haskell не будет знать, какую функцию использовать. Вот как можно обойти такую ситуацию: import qualified Data.Map
Если после такого импорта нам понадобится функция filter из модуля Data.Map; мы должны вызывать её как Data.Map.filter – просто идентификатор filter ссылается на обычную функцию из модуля Prelude, которую мы все знаем и любим. Но печатать строку Data.Map перед именем каждой функции может и поднадоесть! Вот
почему желательно переименовать модуль при импорте во что-ни- будь более короткое: import qualified Data.Map as M
Теперь, чтобы сослаться на функцию из Data.Map, мы вызываем её как M.filter. Как вы видите, символ. используется для обращения к функ- циям, импортированным из модулей с указанием квалификатора, например: M.filter. Мы также помним, что он используется для обозначения композиции функций. Как Haskell узнаёт, что мы имеем в виду? Если мы помещаем символ. между квалифициро- ванным именем модуля и функцией без пробелов – это обраще- ние к функции из модуля; во всех остальных случаях – композиция функций. ПРИМЕЧАНИЕ. Отличный способ узнать Haskell изнутри – про- смотреть документацию к стандартной библиотеке и исследо- вать все стандартные модули и их функции. Также можно изучить исходные тексты всех модулей. Чтение исходных текстов некото- рых модулей – отличный способ освоить язык и прочувствовать его особенности1.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Последнее изменение этой страницы: 2017-02-17; просмотров: 403; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 216.73.216.196 (0.009 с.) |