![]() Заглавная страница Избранные статьи Случайная статья Познавательные статьи Новые добавления Обратная связь КАТЕГОРИИ: ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ![]() ТОП 10 на сайте Приготовление дезинфицирующих растворов различной концентрацииТехника нижней прямой подачи мяча. Франко-прусская война (причины и последствия) Организация работы процедурного кабинета Смысловое и механическое запоминание, их место и роль в усвоении знаний Коммуникативные барьеры и пути их преодоления Обработка изделий медицинского назначения многократного применения Образцы текста публицистического стиля Четыре типа изменения баланса Задачи с ответами для Всероссийской олимпиады по праву ![]() Мы поможем в написании ваших работ! ЗНАЕТЕ ЛИ ВЫ?
Влияние общества на человека
Приготовление дезинфицирующих растворов различной концентрации Практические работы по географии для 6 класса Организация работы процедурного кабинета Изменения в неживой природе осенью Уборка процедурного кабинета Сольфеджио. Все правила по сольфеджио Балочные системы. Определение реакций опор и моментов защемления |
Написание собственных модулей
Практически все языки программирования позволяют разделять код на несколько файлов, и Haskell – не исключение. При написа- нии программ очень удобно помещать функции и типы, служащие схожим целям, в отдельный модуль. Таким образом, можно будет повторно использовать эти функции в других программах, просто импортировав нужный модуль.
Модуль Geometry Давайте разберём процесс создания модулей на простом приме- ре. Создадим модуль, который содержит функции для вычисления объёма и площади поверхности нескольких геометрических фигур. И начнём с создания файла Geometry.hs.
В начале модуля указывается его имя. Если мы назвали файл Geometry.hs, то имя нашего модуля должно быть Geometry. Затем сле- дует перечислить экспортируемые функции, после чего мы можем писать сами функции: module Geometry (sphereVolume , sphereArea , cubeVolume , cubeArea , cuboidArea , cuboidVolume
) where Как видите, мы будем вычислять площади и объёмы для сфер (sphere), кубов (cube) и прямоугольных параллелепипедов (cuboid). Сфера – это круглая штука наподобие грейпфрута, куб – квадратная штука, похожая на кубик Рубика, а прямоугольный параллелепи- пед – точь-в-точь пачка сигарет. (Дети, курить вредно!)
Продолжим и определим наши функции: module Geometry (sphereVolume , sphereArea , cubeVolume
, cubeArea , cuboidArea , cuboidVolume ) where
sphereVolume:: Float –> Float sphereVolume radius = (4.0 / 3.0) * pi * (radius
ˆ 3)
sphereArea:: Float –> Float sphereArea radius = 4 * pi * (radius
ˆ 2)
cubeVolume:: Float –> Float cubeVolume side = cuboidVolume side side side
cubeArea:: Float –> Float cubeArea side = cuboidArea side side side
cuboidVolume:: Float –> Float –> Float –> Float cuboidVolume a b c = rectArea a b * c
cuboidArea:: Float –> Float –> Float –> Float cuboidArea a b c = rectArea a b * 2 + rectArea a c * 2 + rectArea c b * 2
rectArea:: Float –> Float –> Float rectArea a b = a * b Довольно стандартная геометрия, но есть несколько вещей, на которые стоит обратить внимание. Так как куб – это разновидность параллелепипеда, мы определили его площадь и объём, трактуя куб как параллелепипед с равными сторонами. Также мы определили вспомогательную функцию rectArea, которая вычисляет площадь прямоугольника по его сторонам. Функция очень проста – она просто перемножает стороны. Заметьте, мы используем функ- цию rectArea в функциях модуля (а именно в функциях cuboidArea и cuboidVolume), но не экспортируем её, так как хотим создать мо- дуль для работы только с трёхмерными объектами.
При создании модуля мы обычно экспортируем только те функ- ции, которые служат интерфейсом нашего модуля, и скрываем ре- ализацию. Использующий наш модуль человек ничего не должен знать о тех функциях, которые мы не экспортируем. Мы можем полностью их поменять или удалить в следующей версии (скажем,
удалить определение функции rectArea и просто использовать ум- ножение), и никто не будет против – в первую очередь потому, что эти функции не экспортируются.
Чтобы использовать наш модуль, запишем: import Geometry Файл Geometry.hs должен находиться в той же папке, что и им- портирующая его программа.
Иерархия модулей Модулям можно придать иерархическую структуру. Каждый модуль может иметь несколько подмодулей, которые в свою очередь также могут содержать подмодули. Давайте разделим наш модуль Geometry таким образом, чтобы в него входили три подмодуля, по одному на каждый тип объекта. Сначала создадим папку с именем Geometry. В этой папке мы разместим три файла: Sphere.hs, Cuboid.hs и Cube.hs. Посмотрим, что должно находиться в каждом файле.
Вот содержимое файла Sphere.hs: module Geometry.Sphere (volume , area ) where
volume:: Float –> Float volume radius = (4.0 / 3.0) * pi * (radius
ˆ 3)
area:: Float –> Float area radius = 4 * pi * (radius
ˆ 2)
Файл Cuboid.hs выглядит так: module Geometry.Cuboid (volume , area ) where
volume:: Float –> Float –> Float –> Float volume a b c = rectArea a b * c
area:: Float –> Float –> Float –> Float area a b c = rectArea a b * 2 + rectArea a c * 2 + rectArea c b * 2
rectArea:: Float –> Float –> Float rectArea a b = a * b А вот и содержимое файла Cube.hs: module Geometry.Cube (volume , area ) where import qualified Geometry.Cuboid as Cuboid volume:: Float –> Float volume side = Cuboid.volume side side side
area:: Float –> Float
area side = Cuboid.area side side side Обратите внимание, что мы поместили файл Sphere.hs в папку с именем Geometry и определили имя модуля как Geometry.Sphere. То же самое мы сделали для куба и параллелепипеда. Также отметьте, что во всех трёх модулях определены функции с одинаковыми име- нами. Мы вправе так поступать, потому
Итак, если мы редактируем файл, ко- торый находится на одном уровне с пап- кой Geometry, то запишем: import Geometry.Sphere после чего сможем вызывать функции area и volume, которые вычислят пло- щадь и объём сферы. Если нам потре- буется использовать несколько наших модулей, мы должны выполнить квали- фицированный импорт, потому что они экспортируют функции с одинаковыми
именами. Делаем так: import qualified Geometry.Sphere as Sphere import qualified Geometry.Cuboid as Cuboid import qualified Geometry.Cube as Cube Затем мы сможем вызывать функции Sphere.area, Sphere.volume, Cuboid.area и т. д., и каждая функция вычислит площадь или объём соответствующего объекта. В следующий раз, когда вы поймаете себя за написанием огром- ного файла с кучей функций, попытайтесь выяснить, какие функ- ции служат некоей общей цели, и можно ли включить их в отде- льный модуль. Позднее при написании программы сосхожей функциональнос- тью вы сможете просто импортировать свой модуль.
СОЗДАНИЕ НОВЫХ ТИПОВ И КЛАССОВ ТИПОВ В предыдущих главах мы изучили некоторые типы и классы типов в языке Haskell. Из этой главы вы узнаете, как создать и заставить работать свои собственные!
До сих пор мы сталкивались со мно- гими типами данных – Bool, Int, Char, Maybe и др. Но как создать свой собс- твенный тип? Один из способов – ис- пользовать ключевое слово data. Да- вайте посмотрим, как в стандартной библиотеке определён тип Bool: data Bool = False | True Ключевое слово data объявляет новый тип данных. Часть до знака равенства вводит идентифика- тор типа, в данном случае Bool. Часть после знака равенства – это конструкторы данных, которые также называют конструкторами значений. Они определяют, какие значения может принимать тип. Символ | означает «или». Объявление можно прочесть так: тип
Bool может принимать значения True или False. И имя типа, и конс- трукторы данных должны начинаться с прописной буквы. Рассуждая подобным образом, мы можем думать, что тип Int
объявлен так: data Int = –2147483648 | –2147483647 |... | –1 | 0 | 1 | 2 |... | 2147483647 Первое и последнее значения – минимальное и максимальное для Int. На самом деле тип Int объявлен иначе – видите, я пропус- тил уйму чисел – такая запись полезна лишь в иллюстративных це- лях.
Отличная фигура за 15 минут
Теперь подумаем, как бы мы представили некую геометрическую фигуру в языке Haskell. Один из способов – использовать кортежи. Круг может быть представлен как (43.1, 55.0, 10.4), где первое и второе поле – координаты центра, а третье – радиус. Вроде бы подходит, но такой же кортеж может представлять вектор в трёх- мерном пространстве или что-нибудь ещё. Лучше было бы опреде- лить свой собственный тип для фигуры. Скажем, наша фигура мо- жет быть кругом или прямоугольником. data Shape = Circle Float Float Float | Rectangle Float Float Float Float Ну и что это? Размышляйте следующим образом. Конструк- тор для значения Circle содержит три поля типа Float. Когда мы записываем конструктор значения типа, опционально мы можем добавлять типы после имени конструктора; эти типы определя- ют, какие значения будет содержать тип с данным конструктором. В нашем случае первые два числа – это координаты центра, третье число – радиус. Конструктор для значения Rectangle имеет четыре поля, которые также являются числами с плавающей точкой. Пер- вые два числа – это координаты верхнего левого угла, вторые два числа – координаты нижнего правого угла.
Когда я говорю «поля», то подразумеваю «параметры». Конст- рукторы данных на самом деле являются функциями, только эти функции возвращают значения типа данных. Давайте посмотрим на сигнатуры для наших двух конструкторов: ghci>:t Circle Circle:: Float –> Float –> Float –> Shape
ghci>:t Rectangle
Rectangle:: Float –> Float –> Float –> Float –> Shape Классно, конструкторы значений – такие же функции, как лю- бые другие! Кто бы мог подумать!..
Давайте напишем функцию, которая принимает фигуру и воз- вращает площадь её поверхности: area:: Shape –> Float area (Circle _ _ r) = pi * r ^ 2
area (Rectangle x1 y1 x2 y2) = (abs $ x2 – x1) * (abs $ y2 – y1) Первая примечательная вещь в объявлении – это декларация типа. Она говорит, что функция принимает фигуру и возвраща- ет значение типа Float. Мы не смогли бы записать функцию типа Circle –> Float, потому что идентификатор Circle не является ти- пом; типом является идентификатор Shape. По той же самой при- чине мы не смогли бы написать функцию с типом True –> Int. Вто- рая примечательная вещь – мы можем выполнять сопоставление с образцом по конструкторам. Мы уже записывали подобные со- поставления раньше (притом очень часто), когда сопоставляли со значениями [], False, 5, только эти значения не имели полей. Только что мы записали конструктор и связали его поля с имена- ми. Так как для вычисления площади нам нужен только радиус, мы не заботимся о двух первых полях, которые говорят нам, где располагается круг. ghci> area $ Circle 10 20 10 314.15927 ghci> area $ Rectangle 0 0 100 100
10000.0 Ура, работает! Но если попытаться напечатать Circle 10 20 5 в командной строке интерпретатора, то мы получим ошибку. Пока Haskell не знает, как отобразить наш тип данных в виде строки. Вспомним, что когда мы пытаемся напечатать значение в команд- ной строке, интерпретатор языка Haskell вызывает функцию show, для того чтобы получить строковое представление значения, и за- тем печатает результат в терминале. Чтобы определить для нашего типа Shape экземпляр класса Show, модифицируем его таким образом:
data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show) Не будем пока концентрировать внимание на конструкции deriving (Show). Просто скажем, что если мы добавим её в конец объявления типа данных, Haskell автоматически определит экзем- пляр класса Show для этого типа. Теперь можно делать так: ghci> Circle 10 20 5 Circle 10.0 20.0 5.0 ghci> Rectangle 50 230 60 90
Rectangle 50.0 230.0 60.0 90.0 Конструкторы значений – это функции, а значит, мы можем их отображать, частично применять и т. д. Если нам нужен список концентрических кругов с различными радиусами, напишем следу- ющий код: ghci> map (Circle 10 20) [4,5,6,6] [Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0
20.0 6.0]
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Последнее изменение этой страницы: 2017-02-17; просмотров: 173; Нарушение авторского права страницы; Мы поможем в написании вашей работы! infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.152.250 (0.049 с.) |