Главная » Haskell » Именованные поля

0

При определении структур можно всем их элементам давать определённые наименования, и, более того, это автоматически сгенерирует методы доступа на чтение и на запись к соответствующим элементам. Для этого в языке Haskell введено такое понятие, как именованное поле.

Пусть, к примеру, имеется тип данных, который описывает положение грузового контейнера на контейнерной площадке (для единообразия в этом разделе рассматриваются  только геометрические задачи). Этот тип можно описать следующим образом:

data  ContainerPlace

= CP  Int Int Int Int

Грамотный разработчик немного модифицировал бы взаимное расположение элементов данного определения, чтобы работать с ним было бы удобнее. В результате он мог бы получить что-нибудь вроде:

data  ContainerPlace

= CP  Int -Ряд

Int  – Секция Int  – Место Int  – Ярус

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

getCPRow  (CP row _ _ _)  = row

getCPSection  (CP _ section _ _)  = section

и т. д.

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

data  ContainerPlace

= CP

{

row          ::  Int, section  ::  Int, place      ::  Int, level      ::  Int

}

В этом случае транслятор языка автоматически построит функции для доступа к одиночным значениям данной структуры, которые имеют такие же наименования, как и приведённые в определении типа.

Таким же самым образом можно обновлять одиночные значения:

store2D  container r s  p = container{row = r, section = s, place  = p}

Достаточно в фигурных скобках после параметра соответствующего типа перечислить после запятой «присваивания» новых значений элементам через функции доступа, и сам объект обновит  своё состояние.  При этом необходимо отметить, что использование традиционного  подхода вместе с этим вполне возможно, то есть разработчик программ самостоятельно может написать функции для доступа. Но необходимости в этом нет  никакой. Более того, разработчик может использовать и механизм  сопоставления с образцами и создания копии объекта с изменёнными параметрами вручную — использование  именованных  полей лишь даёт новые возможности по упрощению процесса программирования.

Обновление состояния объекта производится через присваивание  именованному полю нового значения. Это положение не должно смущать — никаких деструктивных действий не производится, ибо они запрещены в чистом функциональном программировании. Слово «присваивание»  употреблено здесь образно, только для наименования операции (=). На самом деле происходит создание нового объекта, в котором содержится  новое значение заданного поля, но при этом надо понимать, что вполне возможно, в некоторых трансляторах в качестве оптимизирующего  метода может быть реализовано и деструктивное присваивание, когда значение поля в старом объекте меняется на новое. Естественно, это может производиться только в случае, когда старый объект уже не нужен. О старом же объекте в любом случае, как это принято, позаботится сборщик мусора.

Источник: Душкин Р. В., Справочник по языку Haskell. М.: ДМК Пресс, 2008. 544 с., ил.

По теме:

  • Комментарии