Нумерованный тип - Enumerated type

В компьютерном программировании , перечисленных типа (также называемый перечисления , перечисления , или фактор в языке R программирования и категориальной переменной в статистике) представляет собой тип данных , состоящий из множества названных значений , называемых элементами , члены , enumeral или нумераторы из тип. Имена перечислителей обычно являются идентификаторами, которые в языке ведут себя как константы . Перечислимый тип можно рассматривать как вырожденный меченый союз от единичного типа . Переменная , которая была объявлена как имеющая перечислимый тип может быть назначен любой из нумераторов в качестве значения. Другими словами, перечислимый тип имеет значения, которые отличаются друг от друга, которые можно сравнивать и присваивать, но программист не указывает, что они имеют какое-либо конкретное конкретное представление в памяти компьютера; компиляторы и интерпретаторы могут представлять их произвольно.

Например, четыре масти в колоде игральных карт могут быть четырьмя счетчиками с именами Клуб , Бриллиант , Сердце и Пика , принадлежащими к пронумерованному типу с именем масть . Если переменная V объявляется с костюмом в качестве типа данных, ей можно присвоить любое из этих четырех значений.

Хотя перечислители обычно различны, в некоторых языках один и тот же перечислитель может быть указан дважды в объявлении типа. Имена счетчиков не обязательно должны быть семантически полными или совместимыми в каком-либо смысле. Например, нумерованный тип, называемый цветом, может быть определен как состоящий из перечислителей Red , Green , Zebra , Missing и Bacon . В некоторых языках объявление перечислимого типа также намеренно определяет порядок его членов; в других счетчики неупорядочены; в других случаях неявное упорядочивание возникает из-за того, что компилятор конкретно представляет счетчики как целые числа.

Некоторые типы перечислителей могут быть встроены в язык. Булева типа , например , часто является предопределенным перечисление значений ложных и истинных . Многие языки позволяют пользователям определять новые перечисляемые типы.

Значения и переменные перечислимого типа обычно реализуются как битовые строки фиксированной длины , часто в формате и размере, совместимых с некоторым целочисленным типом. Некоторые языки, особенно языки системного программирования , позволяют пользователю указывать битовую комбинацию, которая будет использоваться для каждого перечислителя. В теории типа , перечисляемые типы часто рассматриваются как меченые союзы из типов единиц . Поскольку такие типы имеют форму , их также можно записать как натуральные числа.

Обоснование

Некоторые ранние языки программирования изначально не имели перечислимых типов. Если бы программист хотел, чтобы переменная, например myColor , имела значение red, переменная red должна быть объявлена ​​и ей присвоено какое-то произвольное значение, обычно целочисленная константа. Затем переменная red будет присвоена myColor . Другие методы присваивают произвольные значения строкам, содержащим имена счетчиков.

Эти произвольные значения иногда назывались магическими числами, поскольку часто не было объяснения того, как числа были получены или были ли их фактические значения значимыми. Эти магические числа могут усложнить понимание и поддержку исходного кода.

С другой стороны, нумерованные типы делают код более самодокументируемым. В зависимости от языка компилятор может автоматически назначать счетчикам значения по умолчанию, тем самым скрывая ненужные детали от программиста. Эти значения могут быть даже не видны программисту (см. Скрытие информации ). Перечислимые типы также могут помешать программисту писать нелогичный код, такой как выполнение математических операций над значениями перечислителей. Если значение переменной, которой был назначен перечислитель, должно было быть напечатано, некоторые языки программирования могли также печатать имя перечислителя, а не его базовое числовое значение. Еще одно преимущество состоит в том, что перечисляемые типы позволяют компиляторам обеспечивать семантическую корректность. Например: myColor = TRIANGLE может быть запрещено, в то время myColor = RED как допускается, даже если ТРЕУГОЛЬНИК и КРАСНЫЙ оба внутренне представлены как 1 .

Концептуально перечислимый тип аналогичен списку номиналов (числовых кодов), поскольку каждому возможному значению типа присваивается отличительное натуральное число. Таким образом, данный перечислимый тип является конкретной реализацией этого понятия. Когда порядок имеет значение и / или используется для сравнения, то перечислимый тип становится порядковым .

Условные обозначения

Языки программирования, как правило, имеют свои собственные, часто множественные, стили программирования и соглашения об именах . Переменная, присвоенная перечислению, обычно является существительным в единственном числе и часто следует соглашению либо в PascalCase, либо в верхнем регистре , в то время как нижний регистр и другие встречаются реже.

Синтаксис на нескольких языках программирования

Паскаль и синтаксически похожие языки

Паскаль

В Паскале перечислимый тип может быть неявно объявлен путем перечисления значений в списке в скобках:

  var
    suit: (clubs, diamonds, hearts, spades);

Объявление часто появляется в объявлении синонима типа, поэтому его можно использовать для нескольких переменных:

  type
    cardsuit = (clubs, diamonds, hearts, spades);
    card = record
             suit: cardsuit;
             value: 1 .. 13;
           end;
  var
    hand: array [ 1 .. 13 ] of card;
    trump: cardsuit;

Порядок, в котором указаны значения перечисления, имеет значение. Перечислены тип является порядковым типом, а predи succфункции будут давать предварительное или следующее значение перечисления, и ordможет преобразовывать значения перечисления их целочисленного представления. Однако стандартный Паскаль не предлагает преобразования арифметических типов в перечисления. Extended Pascal предлагает эту функциональность через расширенную succфункцию. Некоторые другие диалекты Паскаля позволяют это через приведение типов. Некоторые современные потомки Паскаля, такие как Modula-3 , предоставляют специальный синтаксис преобразования с использованием метода, называемого VAL; Modula-3 также рассматривает BOOLEANи CHARкак специальные предопределенные перечисляемые типы и виды использования, ORDа также VALдля стандартного декодирования и кодирования ASCII .

Языки стиля Pascal также позволяют использовать перечисление в качестве индекса массива:

  var
    suitcount: array [cardsuit] of integer;

Ада

В Ada использование "=" было заменено на "is", оставив определение очень похожим:

type Cardsuit is (clubs, diamonds, hearts, spades);

В дополнение к Pred, Succ, Valи PosАда также поддерживает простые преобразования строки через Imageи Value.

Подобно языкам C-стиля, Ada позволяет указывать внутреннее представление перечисления:

for Cardsuit use
  (clubs => 1, diamonds => 2, hearts => 4, spades => 8);

В отличие от языков C-стиля, Ada также позволяет указывать количество бит перечисления:

for Cardsuit'Size use 4;  -- 4 bits

Кроме того, можно использовать перечисления в качестве индексов для массивов, как в Паскале, но есть атрибуты, определенные для перечислений.

   Shuffle : constant array(Cardsuit) of Cardsuit :=
     (Clubs => Cardsuit'Succ(Clubs), -- see attributes of enumerations 'First, 'Last, 'Succ, 'Pred
      Diamonds => Hearts, --an explicit value
      Hearts => Cardsuit'Last, --first enumeration value of type Cardsuit e.g., clubs
      Spades => Cardsuit'First --last enumeration value of type Cardsuit e.g., spades
      );

Как и Modula-3, Ада рассматривает Booleanи Characterкак специальные предопределенные (в пакете " Standard") перечисляемые типы. В отличие от Modula-3 можно также определять собственные типы символов:

type Cards is ('7', '8', '9', 'J', 'Q', 'K', 'A');

C и синтаксически похожие языки

C

Исходный диалект K&R языка программирования C не имел перечисляемых типов. В C перечисления создаются явными определениями ( enumключевое слово само по себе не вызывает выделения памяти), которые используют enumключевое слово и напоминают определения структур и объединений :

enum cardsuit {
    Clubs,
    Diamonds,
    Hearts,
    Spades
};

struct card {
    enum cardsuit suit;
    short int value;
} hand[13];

enum cardsuit trump;

C предоставляет целочисленное представление значений перечисления непосредственно программисту. Целые числа и значения перечисления можно свободно смешивать, и все арифметические операции над значениями перечисления разрешены. Переменная перечисления даже может содержать целое число, которое не представляет ни одно из значений перечисления. В самом деле, в соответствии с определением языка, приведенный выше код будет определять Clubs, Diamonds, Heartsи Spadesкак константы типа int, которые будут преобразованы только (бесшумно) , чтобы , enum cardsuitесли они сохранены в переменной этого типа.

C также позволяет программисту явно выбирать значения констант перечисления, даже без типа. Например,

enum cardsuit {
    Clubs    = 1,
    Diamonds = 2,
    Hearts   = 4,
    Spades   = 8
};

может использоваться для определения типа, позволяющего представлять математические наборы костюмов как enum cardsuitпобитовые логические операции.

C #

Перечислимые типы в языке программирования C # сохраняют большую часть семантики "малых целых чисел" перечислений C. Некоторые арифметические операции не определены для перечислений, но значение перечисления может быть явно преобразовано в целое число и обратно, а переменная перечисления может иметь значения, которые не были объявлены определением перечисления. Например, учитывая

enum Cardsuit
{
    Clubs,
    Diamonds,
    Spades,
    Hearts
}

выражения CardSuit.Diamonds + 1и CardSuit.Hearts - CardSuit.Clubsразрешены напрямую (потому что может иметь смысл пройти через последовательность значений или спросить, сколько шагов между двумя значениями), но CardSuit.Hearts * CardSuit.Spadesсчитается менее понятным и разрешается только в том случае, если значения сначала преобразуются в целые числа .

C # также предоставляет C-подобную функцию, позволяющую определять конкретные целочисленные значения для перечислений. Делая это, можно выполнять двоичные операции с перечислениями, таким образом обрабатывая значения перечисления как наборы флагов. Эти флаги можно проверить с помощью бинарных операций или встроенного метода HasFlag типа Enum.

Определение перечисления определяет имена для выбранных целочисленных значений и является синтаксическим сахаром , поскольку можно присвоить переменной перечисления другие целочисленные значения, которые не входят в область определения перечисления.

C ++

В C ++ есть типы перечисления, которые напрямую наследуются от языков C и работают в основном так же, за исключением того, что перечисление является реальным типом в C ++, что дает дополнительную проверку во время компиляции. Также (как и в случае со структурами) enumключевое слово C ++ автоматически комбинируется с typedef , так что вместо наименования типа enum nameпросто назовите его name. Это можно смоделировать на C с помощью typedef:typedef enum {Value1, Value2} name;

C ++ 11 предоставляет второй тип-безопасный тип перечисления, который неявно преобразуется в целочисленный тип. Это позволяет определить потоковую передачу io для этого типа. Кроме того, перечисления не протекают, поэтому их нужно использовать с Enumeration Type::enumeration. Это определяется фразой «enum class». Например:

enum class Color {Red, Green, Blue};

Основной тип является реализация определенных интегрального типа , который является достаточно большим , чтобы вместить все перечисленные значения (он не должен быть наименьший возможный тип!). В C ++ вы можете напрямую указать базовый тип. Это позволяет "предварительное объявление" перечислений:

enum class Color : long {Red, Green, Blue};  // must fit in size and memory layout the type 'long'
enum class Shapes : char;  // forward declaration. If later there are values defined that don't fit in 'char' it is an error.

Идти

Go использует iotaключевое слово для создания нумерованных констант.

type ByteSize float64

const (
    _           = iota // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota)
    MB
    GB
)

Джава

В J2SE версии 5.0 языка программирования Java добавлены перечисляемые типы, синтаксис объявления которых аналогичен синтаксису C :

enum Cardsuit { CLUBS, DIAMONDS, SPADES, HEARTS };
...
Cardsuit trump;

Однако система типов Java рассматривает перечисления как тип, отдельный от целых чисел, и смешивание значений перечисления и целых чисел не допускается. Фактически, тип перечисления в Java на самом деле является специальным классом, сгенерированным компилятором, а не арифметическим типом, а значения перечисления ведут себя как глобальные предварительно сгенерированные экземпляры этого класса. Типы перечисления могут иметь методы экземпляра и конструктор (аргументы которого могут быть указаны отдельно для каждого значения перечисления). Все типы перечислений неявно расширяют Enumабстрактный класс. Тип перечисления не может быть создан напрямую.

Внутри каждое значение перечисления содержит целое число, соответствующее порядку, в котором они объявлены в исходном коде, начиная с 0. Программист не может напрямую установить настраиваемое целое число для значения перечисления, но можно определить перегруженные конструкторы, которые затем могут назначать произвольные значения для самоопределяемых членов класса перечисления. Определение геттеров позволяет затем получить доступ к этим самоопределенным членам. Внутреннее целое число может быть получено из значения перечисления с помощью ordinal()метода, а список значений перечисления типа перечисления может быть получен по порядку с помощью values()метода. Обычно программистам не рекомендуется преобразовывать перечисления в целые числа и наоборот. Перечислимые типы Comparable, использующие внутреннее целое число; в результате их можно отсортировать.

Стандартная библиотека Java предоставляет служебные классы для использования с перечислениями. В EnumSetклассе реализует Setзначения перечислений; он реализован в виде битового массива , что делает его очень компактным и таким же эффективным, как явное манипулирование битами, но более безопасным. В EnumMapреализует класс Mapзначений перечислений объекта. Он реализован как массив с целочисленным значением значения перечисления, служащим индексом.

Perl

Языки с динамической типизацией в синтаксической традиции C (например, Perl или JavaScript ), как правило, не предоставляют перечислений. Но в программировании на Perl тот же результат может быть получен с помощью сокращенного списка строк и хэшей (возможно, срезов ):

my @enum = qw(Clubs Diamonds Hearts Spades);
my( %set1, %set2 );
@set1{@enum} = ();          # all cleared
@set2{@enum} = (1) x @enum; # all set to 1
$set1{Clubs} ...            # false
$set2{Diamonds} ...         # true

Раку

Raku (ранее известный как Perl 6) поддерживает перечисления. Существует несколько способов объявления перечислений в Raku, каждый из которых создает внутреннюю карту.

enum Cat <sphynx siamese bengal shorthair other>; # Using "quote-words"
enum Cat ('sphynx', 'siamese', 'bengal', 'shorthair', 'other'); # Using a list
enum Cat (sphynx => 0, siamese => 1, bengal => 2, shorthair => 3, other => 4); # Using Pair constructors
enum Cat (:sphynx(0), :siamese(1), :bengal(2), shorthair(3), :other(4)); # Another way of using Pairs, you can also use `:0sphynx`

PHP

Перечисления были добавлены в версии PHP 8.1.

enum CardSuit
{
    case Hearts;
    case Diamonds;
    case Clubs;
    case Spades;
}

Ржавчина

Хотя Rust использует такое enumключевое слово, как C, он использует его для описания помеченных объединений , перечисления которых можно рассматривать как вырожденную форму. Таким образом, перечисления Rust гораздо более гибкие и могут содержать варианты структур и кортежей.

enum Message {
    Quit,
    Move { x: i32, y: i32 }, // struct
    Write(String), // single-element tuple
    ChangeColor(i32, i32, i32), // three-element tuple
}

Быстрый

В C перечисления присваивают связанные имена набору целочисленных значений. В Swift перечисления намного более гибкие и не требуют предоставления значения для каждого случая перечисления. Если значение (называемое необработанным значением) предоставляется для каждого случая перечисления, значение может быть строкой, символом или значением любого целочисленного типа или типа с плавающей запятой.

В качестве альтернативы, варианты перечисления могут указывать связанные значения любого типа, которые должны храниться вместе с каждым другим значением case, так же, как объединения или варианты на других языках. Можно определить общий набор связанных наблюдений как часть одного перечисления, каждое из которых имеет свой набор значений соответствующих типов, связанных с ним.

В Swift перечисления - это первоклассный тип. Они используют многие функции, традиционно поддерживаемые только классами, такие как вычисляемые свойства для предоставления дополнительной информации о текущем значении перечисления и методы экземпляра для обеспечения функциональности, связанной со значениями, которые представляет перечисление. Перечисления также могут определять инициализаторы для предоставления начального значения case и могут быть расширены для расширения их функциональности за пределы их исходной реализации; и может соответствовать протоколам для обеспечения стандартной функциональности.

enum CardSuit {
    case clubs
    case diamonds
    case hearts
    case spades
}

В отличие от C и Objective-C , случаям перечисления Swift не присваивается целочисленное значение по умолчанию при их создании. В приведенном выше примере CardSuit трефы, бубны, червы и пики неявно равны 0, 1, 2 и 3. Вместо этого различные варианты перечисления являются полноценными значениями сами по себе с явно определенным типом CardSuit. .

В одной строке может быть несколько вариантов, разделенных запятыми:

enum CardSuit {
    case clubs, diamonds, hearts, spades
}

При работе с перечислениями, в которых хранятся целые или строковые необработанные значения, нет необходимости явно назначать необработанное значение для каждого случая, потому что Swift автоматически присваивает значения.

Например, когда целые числа используются для необработанных значений, неявное значение для каждого случая на единицу больше, чем в предыдущем случае. Если для первого случая значение не задано, его значение равно 0.

Перечисление ниже является уточнением предыдущего перечисления Planet с целыми необработанными значениями для представления порядка каждой планеты от солнца:

enum Planet: Int {
    case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune
}

В приведенном выше примере Planet.mercury имеет явное исходное значение 1, Planet.venus имеет неявное исходное значение 2 и так далее.

«Подробности можно найти в онлайн-документации Swift здесь».

Машинопись

TypeScript добавляет в JavaScript тип данных enum.

enum Cardsuit {Clubs, Diamonds, Hearts, Spades};
var c: Cardsuit = Cardsuit.Diamonds;

По умолчанию перечисляет элементы, начиная с 0; это можно изменить, установив значение первого:

enum Cardsuit {Clubs = 1, Diamonds, Hearts, Spades};
var c: Cardsuit = Cardsuit.Diamonds;

Все значения могут быть установлены:

enum Cardsuit {Clubs = 1, Diamonds = 2, Hearts = 4, Spades = 8};
var c: Cardsuit = Cardsuit.Diamonds;

TypeScript поддерживает сопоставление числового значения с его именем. Например, это находит имя значения 2:

enum Cardsuit {Clubs = 1, Diamonds, Hearts, Spades};
var suitName: string = Cardsuit[2];

alert(suitName);

Python

enumМодуль был добавлен в стандартной библиотеке Python в версии 3.4.

from enum import Enum
class Cards(Enum):
    CLUBS = 1
    DIAMONDS = 2
    HEARTS = 3
    SPADES = 4

Также есть функциональный API для создания перечислений с автоматически сгенерированными индексами (начиная с единицы):

Cards = Enum('Cards', 'CLUBS DIAMONDS HEARTS SPADES'])

Перечисления Python не обеспечивают семантическую корректность (бессмысленное сравнение с несовместимым перечислением всегда возвращает False, а не вызывает TypeError ):

>>> Color = Enum("Color", "RED GREEN BLUE")
>>> Shape = Enum("Shape", ["CIRCLE", "TRIANGLE", "SQUARE", "HEXAGON"])
>>> def has_vertices(shape):
... 	return shape != Shape.CIRCLE
...
>>> has_vertices(Color.GREEN)
True

Фортран

Fortran имеет только перечисленные типы для взаимодействия с C; следовательно, семантика аналогична C, и, как и в C, значения перечисления являются просто целыми числами, и никакая дополнительная проверка типа не выполняется. Пример C, приведенный выше, можно записать на Фортране как

enum, bind( C )
  enumerator :: CLUBS = 1, DIAMONDS = 2, HEARTS = 4, SPADES = 8
end enum

Visual Basic / VBA

Пронумерованным типам данных в Visual Basic (до версии 6) и VBA автоматически присваивается " Long" тип данных, а также они сами становятся типом данных:

'Zero-based
Enum CardSuit
    Clubs
    Diamonds
    Hearts
    Spades
End Enum

Sub EnumExample()
    Dim suit As CardSuit
    suit = Diamonds
    MsgBox suit
End Sub

Пример кода в VB.NET

Enum CardSuit
    Clubs
    Diamonds
    Hearts
    Spades
End Enum

Sub EnumExample()
    Dim suit As CardSuit
    suit = CardSuit.Diamonds
    MessageBox.show(suit)
End Sub

Лисп

Common Lisp использует спецификатор типа члена, например,

(deftype cardsuit ()
  '(member club diamond heart spade))

в котором говорится, что объект относится к типу костюма, если это клюшка #'eql, бриллиант, сердце или лопата. Однако спецификатор типа члена недопустим в качестве специалиста параметров Common Lisp Object System (CLOS). Вместо этого может использоваться, что (eql atom)эквивалентно (member atom)может использоваться (то есть только один член набора может быть указан с помощью спецификатора типа eql, однако он может использоваться как специализатор параметра CLOS.) Другими словами, для определения методов. чтобы охватить перечислимый тип, метод должен быть определен для каждого конкретного элемента этого типа.

Кроме того,

(deftype finite-element-set-type (&rest elements)
   `(member ,@elements))

может использоваться для определения произвольных перечислимых типов во время выполнения. Например

(finite-element-set-type club diamond heart spade)

будет относиться к типу, эквивалентному предыдущему определению carduit, поскольку, конечно, просто использовалось бы

(member club diamond heart spade)

но может быть менее запутанной функцией #'memberпо стилистическим причинам.

Алгебраический тип данных в функциональном программировании

В языках функционального программирования из линии ML (например, Standard ML (SML), OCaml и Haskell ) для реализации перечислимого типа можно использовать алгебраический тип данных с конструкторами только с нулевым значением . Например (в синтаксисе сигнатур SML):

datatype cardsuit = Clubs | Diamonds | Hearts | Spades
type card = { suit: cardsuit; value: int }
val hand : card list
val trump : cardsuit

В этих языках представление с малым целым числом полностью скрыто от программиста, если такое представление действительно используется реализацией. Однако в Haskell есть Enum класс типа, который тип может наследовать или реализовать, чтобы получить отображение между типом и Int.

Базы данных

Некоторые базы данных напрямую поддерживают перечисляемые типы. MySQL предоставляет перечислимый тип ENUMс допустимыми значениями, указанными в виде строк при создании таблицы. Значения сохраняются как числовые индексы, при этом пустая строка хранится как 0, первое строковое значение хранится как 1, второе строковое значение сохраняется как 2 и т. Д. Значения могут быть сохранены и извлечены как числовые индексы или строковые значения.

Пример:

CREATE TABLE shirts (
    name VARCHAR(40),
    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')
);

Схема XML

Схема XML поддерживает нумерованные типы через фасет перечисления, используемый для ограничения большинства примитивных типов данных, таких как строки.

<xs:element name="cardsuit">
  <xs:simpleType>
    <xs:restriction base="xs:string">
      <xs:enumeration value="Clubs"/>
      <xs:enumeration value="Diamonds"/>
      <xs:enumeration value="Hearts"/>
      <xs:enumeration value="Spades"/>
    </xs:restriction>
  </xs:simpleType>
</xs:element>

Смотрите также

использованная литература

внешние ссылки