Тип 30 (винтовка)
- 1 year ago
- 0
- 0
Перечисляемый тип (сокращённо перечисле́ние , англ. enumeration, enumerated type ) — в программировании тип данных , чьё множество значений представляет собой ограниченный список идентификаторов.
Перечисляемый тип определяется как набор идентификаторов, с точки зрения языка играющих ту же роль, что и обычные именованные константы, но связанные с этим типом. Классическое описание типа-перечисления в языке Паскаль выглядит следующим образом:
type Cardsuit = (clubs, diamonds, hearts, spades);
Здесь производится объявление типа данных Cardsuit (карточная масть), значениями которого может быть любая из четырёх перечисленных констант. Переменная типа
Cardsuit
может принимать одно из значений
clubs, diamonds, hearts, spades
, допускается сравнение значений типа перечисление на равенство или неравенство, а также использование их в операторах выбора (в Паскале —
case
) в качестве значений, идентифицирующих варианты.
Использование перечислений позволяет сделать исходные коды программ более читаемыми, так как позволяют заменить « магические числа », кодирующие определённые значения, на читаемые имена.
На базе перечислений в некоторых языках могут создаваться типы-множества . В таких случаях множество понимается (и описывается) как неупорядоченный набор уникальных значений типа-перечисления.
Перечисляемый тип может использоваться в объявлениях переменных и формальных параметров функций (процедур, методов). Значения перечислимого типа могут присваиваться соответствующим переменным и передаваться через параметры соответствующих типов в функции. Кроме того, всегда поддерживается сравнение значений перечислимого типа на равенство и неравенство. Некоторые языки поддерживают также другие операции сравнения для значений перечислимых типов. Результат сравнения двух перечислимых значений в таких случаях определяется, как правило, порядком следования этих значений в объявлении типов — значение, которое в объявлении типа встречается раньше, считается «меньше» значения, встречающегося позже. Иногда перечислимый тип или некоторый диапазон значений перечислимого типа также может быть использован в качестве типа индекса для массива. В этом случае для каждого значения выбранного диапазона в массиве имеется один элемент, а реальный порядок следования элементов соответствует порядку следования значений в объявлении типа.
Обычно в процессе компиляции значения перечислений представляются при помощи целых чисел. В зависимости от конкретного языка программирования такое представление может быть либо полностью скрыто от программиста, либо доступно ему с помощью тех или иных «обходных манёвров» (например, принудительного преобразования значения типа перечисление к значению типа «целое число»), либо даже управляемо программистом (в таких случаях программист имеет возможность явно указать, какими числами будут кодироваться все или некоторые значения типа-перечисления). У всех вариантов есть свои положительные и отрицательные стороны. С одной стороны, возможность использования числовых значений констант, составляющих тип-перечисление, особенно при злоупотреблении ею, лишает смысла использование этих типов и создаёт опасность появления ошибок (когда используются числовые значения, для которых в типе нет соответствующих констант). С другой стороны, явное управление значениями даёт некоторые дополнительные возможности. Например, позволяет использовать типы-перечисления при организации интерфейса с модулями, написанными на других языках, если они используют или возвращают кодированные целыми числами значения из некоторого предопределённого набора.
Ещё одна возможность, которую дают перечислимые типы на уровне реализации языка — экономия памяти. При небольшом объёме типа-перечисления для хранения значения этого типа достаточно нескольких битов (вышеприведённый тип
Cardsuit
требует всего два бита на значение, в то время как стандартное целое число на большинстве используемых архитектур занимает 32 бита — в 16 раз больше), и компилятор может использовать этот факт для уплотнения хранения данных в памяти. Это может быть особенно важно, если несколько значений типов-перечислений хранятся в одной записи — уплотнение
записей
при обработке больших их количеств может освободить много памяти. Компиляторы обычно не реализуют эту возможность, по крайней мере, в последнее время, когда компьютерная память существенно подешевела.
Тип перечисление является традиционным для развитых языков программирования, используется достаточно широко и часто воспринимается как нечто само собой разумеющееся. Тем не менее, этот тип также не обходится без критики со стороны теоретиков и практиков программирования. Так, при разработке языка программирования Оберон перечисляемые типы попали в список возможностей, которые были удалены из языка. Никлаус Вирт , разработчик языка, назвал следующие причины:
С другой стороны, например, в Java , первоначально не содержащей перечислимого типа, этот тип был впоследствии введён из соображений не только удобства, но и надёжности: проблема использования вместо перечислений групп именованных констант в том, что отсутствует контроль со стороны компилятора как за уникальностью значений констант, так и за возможностью случайного присваивания переменным значений, не соответствующих ни одной из этих констант.
В языке Ada перечисления задаются с помощью ключевого слова
is
и последующего списка значений через запятую:
type Cardsuit is (clubs, diamonds, hearts, spades);
Первоначальный K&R -диалект C не имел перечисляемых типов, однако они были добавлены в стандарте ANSI C .
enum cardsuit
{
CLUBS,
DIAMONDS,
HEARTS,
SPADES
};
Динамические языки слабой типизации с C-подобным синтаксисом (например perl или JavaScript ), как правило, не имеют перечислений.
Перечисления в языке C++ прямо наследуют поведение перечислений языка C, за исключением того, что перечисляемый тип в C++ — настоящий тип, и ключевое слово
enum
используется только при объявлении такого типа. Если при обработке параметра являющегося перечислением, какое-либо значение из перечисления не обрабатывается (например один из элементов перечисления забыли обработать в конструкции
switch
), то компилятор может выдать предупреждение о забытом значении.
C++ 11 предоставляет второй, типобезопасный тип перечисления, который не преобразуется неявно в целочисленный тип. Это определяется фразой «enum class». Например:
enum class Color {Red, Green, Blue};
Базовый тип является реализация определённых интегрального типа, который достаточно велик, чтобы вместить все перечисленные значения (он не должен быть наименьший возможный тип!). В C++ вы можете указать базовый тип напрямую. Это позволяет «предварительные объявления» перечислений:
enum class Color : long {Red, Green, Blue}; // должен соответствовать по размеру и разметке памяти типа «long»
enum class Shapes : char; // предварительная декларация. Если позже будут определены значения, которые не помещаются в 'char', это ошибка.
enum Cardsuit { Clubs, Diamonds, Spades, Hearts }
В первоначальной Java перечислений не было, вместо них предлагалось использовать классы со статическими константами. Начиная с версии 5 (1.5) перечисления были введены в язык, они представляют собой полноценный класс, в который можно добавлять произвольное количество полей и методов. Перечисления были введены для улучшенного контроля за типобезопасностью.
enum Cardsuit { Clubs, Diamonds, Spades, Hearts }
enum class Direction {
NORTH, SOUTH, WEST, EAST
}
В некоторых языках программирования (например, в языке Haskell) при помощи Алгебраических типов можно эмулировать перечисления. Например, так кодируется булевский тип, содержащий два идентификатора для представления значений истинности:
data Bool = False | True
type
enumType = enum
one, two, three
var a: enumType = three
var b = two
echo a > b
//Обычный enum
enum Colors {
GREEN = 1,
BLUE,
RED
}
//Flags enum
[Flags]
enum Borders {
LEFT,
RIGHT,
TOP,
BOTTOM
}
void draw_borders (Borders selected_borders) {
// equivalent to: if ((Borders.LEFT & selected_borders) > 0)
if (Borders.LEFT in selected_borders) {
}
}
pragma solidity ^0.4.4;
contract SimpleEnum {
enum SomeData {DEFAULT,ONE,TWO}
SomeData someData;
function SimpleEnum(){
someData = SomeData.DEFAULT;
}
function setValues(uint _value){
require(uint(SomeData.TWO) >= _value);
someData = SomeData(_value);
}
function getValue() constant returns (uint){
return uint(someData);
}
}