Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь 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 модуль – это набор взаимосвязанных функ- ций, типов и классов типов. Программа на Haskell – это набор модулей; главный мо- дуль подгружает все осталь- ные и использует функции, определённые в них, чтобы что-либо сделать. Разбиение кода на несколько модулей удобно по многим причинам. Если модуль достаточно общий, экспортируемые им функции мо- гут быть использованы во множестве программ. Если ваш код раз- делён на несколько самостоятельных модулей, не очень зависящих один от другого (мы говорим, что они слабо связаны), модули могут многократно использоваться в разных проектах. Это отчасти об- легчает непростую задачу написания кода, разбивая его на несколь- ко частей, каждая из которых имеет некоторое назначение. Стандартная библиотека языка 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; просмотров: 333; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.223.238.150 (0.007 с.) |