Changeset 1d5e5601 for doc

Ignore:
Timestamp:
Mar 1, 2024, 11:14:23 AM (5 months ago)
Branches:
master
Children:
647e2ea
Parents:
924534e
Message:

more proofreading on the enumeration related-work section

File:
1 edited

Legend:

Unmodified
 r924534e \label{s:RelatedWork} An enumeration type exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}, and the algebraic data-type in functional programming. Enumeration types exist in many popular programming languages, both past and present, \eg Pascal~\cite{Pascal}, Ada~\cite{Ada}, \Csharp~\cite{Csharp}, \CC, Go~\cite{Go}, Java~\cite{Java}, Modula-3~\cite{Modula-3}, Rust~\cite{Rust}, Swift~\cite{Swift}, Python~\cite{Python}, and algebraic data-types in functional programming. Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions. PI = 3.14159;   Plus = '+';   Fred = 'Fred'; \end{pascal} The enumerator type is inferred from the constant-expression type. There is no notion of an ordered set, modulo the \lstinline[language=pascal]{set of} type. Here, there is no enumeration because there is no specific type (pseudo enumeration). Hence, there is no notion of a (possibly ordered) set, modulo the \lstinline[language=pascal]{set of} type. The type of each constant name (enumerator) is inferred from the constant-expression type. Free Pascal~\cite[\S~3.1.1]{FreePascal} is a modern, object-oriented version of classic Pascal, with a C-style enumeration type. An Ada enumeration type is an ordered list of constants, called \Newterm{literals} (enumerators). \begin{ada} type RGB is ( @Red@, @Green@, Blue ); -- 3 literals (enumerators) \end{ada} No other enumerators are assignable to objects of this type. type RGB is ( Red, Green, Blue ); -- 3 literals (enumerators) \end{ada} Object initialization and assignment are restricted to the enumerators of this type. Enumerators without an explicitly designated constant value are auto-initialized: from left to right, starting at zero or the next explicitly initialized constant, incrementing by 1. To explicitly set enumerator values, \emph{all} enumerators must be set in \emph{ascending} order, \ie there is no auto-initialization. (0, 10, RED)  (1, 20, GREEN)  (2, 30, BLUE) \end{ada} Note, Ada is case-\emph{insensitive} so names may appear in multiple forms and still be the same, \eg @Red@ and @RED@ (a questionable design decision). Like C, Ada enumerators are unscoped, \ie enumerators declared inside of an enum are visible (projected) into the enclosing scope. Note, Ada is case-\emph{insensitive} so names may appear in multiple forms and still be the same name (a questionable design decision). The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relation is given implicitly by the sequence of enumerators, which is always ascending. The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of enumerators, which is always ascending. Ada enumerators are overloadable. \begin{ada} type Operator is ( '+', '-', '*', '/' ); Op : Operator; \end{ada} which is syntactic sugar for the label and not character literals from the predefined type @Character@. The purpose is readability using character literals rather than names. \begin{ada} Op := '+'; case Op is                    -- all enumerators must appear when '+' => ... ; when '-' => ... ; when '*' => ... ; when '/' => ... ; end case; \end{ada} Arrays of character enumerators can be treated as strings. The purpose is strictly readability using character literals rather than names. \begin{ada} Op : Operator := '+'; if Op = '+' or else Op = '-' then ... ; elsif Op = '*' or else Op = '/' then ... ; end if; \end{ada} Interestingly, arrays of character enumerators can be treated as strings. \begin{ada} Ops : array( 0..3 ) of Operator; Ops := @"+-*/"@;            -- string assignment to array elements Ops := @"+-" & "*/"@;   -- string concatenation and assignment for Op of Ops loop Put_Line( Operator'Image( Op ) ); end loop; \end{ada} Ada's @Character@ type is defined as a character enumeration across all Latin-1 characters. Ada's boolean type is defined as a special enumeration, which can be used in conditions. Ada's boolean type is also a special enumeration, which can be used in conditions. \begin{ada} type Boolean is (False, True); -- False / True not keywords Hence, the ordering of the enumerators is crucial to provide the necessary ranges. An enumeration type can be used in the Ada \lstinline[language=ada]{case} (switch) and iterating constructs. An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a default) or iterating constructs. \begin{cquote} \lstDeleteShortInline@ enum class E { A, B, C }; E e = @E::@A;                                                   $\C{// qualified enumerator}$ e = B;                                                                  $\C{// B not in scope}$ e = B;                                                                  $\C{// error: B not in scope}$ \end{c++} \CC{20} supports explicit unscoping with a \lstinline[language=c++]{using enum} declaration. enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 }; \end{c++} There is no implicit conversion from the \lstinline[language=c++]{enum class} type and to its declared type. There is no implicit conversion from the \lstinline[language=c++]{enum class} type to its declared type. \begin{c++} rgb crgb = rgb::Red; char ch = rgb::Red;   ch = crgb;                $\C{// disallowed}$ char ch = rgb::Red;   ch = crgb;                $\C{// error}$ \end{c++} Finally, there is no mechanism to iterate through an enumeration nor use the enumeration type to declare an array dimension. Finally, enumerations can be used in the @switch@ statement but there is no mechanism to iterate through an enumeration. An enumeration type cannot declare an array dimension but can be used as a subscript. There is no mechanism to subtype or inherit from enumerations. % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to C/\CC enumeration. \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to the C/\CC enumeration. \begin{csharp} enum Weekday : byte { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ }; \end{csharp} The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@) The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@). A method cannot be defined in an enumeration type. As well, there is an explicit bidirectional conversion between an enumeration and its integral type, and an implicit conversion to the enumerator label in display contexts. \end{csharp} The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable. The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation). \begin{csharp} foreach ( Weekday constant in @Enum.GetValues@( typeof(Weekday) ) ) { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; static bool isWeekday( Weekday wd ) { return Weekday.Mon <= wd && wd <= Weekday.Fri; return wd <= Weekday.Fri; } static bool isWeekend( Weekday wd ) { return Weekday.Sat <= wd && wd <= Weekday.Sun; return Weekday.Sat <= wd; } \begin{csharp} public class Program { public @class@ WeekDay { public enum Weekday { Mon=-4, Tue=-3, Wed=-2, Thu=-1, Fri=0, Sat=1, Sun=2 }; Weekday day; public @class@ WeekDay : Enumeration { public enum Day { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; public enum Day2 : Day { XXX, YYY }; Day day; public bool isWeekday() { return day <= 0; return day <= Day.Fri; } public bool isWeekend() { return day > 0 return day > Day.Fri; } public WeekDay( Weekday d ) { day = d; } public WeekDay( Day d ) { day = d; } } public static void Main() { WeekDay cday = new WeekDay( WeekDay.Weekday.Sat ); WeekDay( WeekDay.Day.Sat ); Console.WriteLine( cday.isWeekday() ); Console.WriteLine( cday.isWeekend() ); \lstnewenvironment{Go}[1][]{\lstset{language=Go,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} Golang provides a mechanism similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression (pseudo enumeration). Golang provides pseudo-enumeration similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression. \begin{Go} const ( R = 0; G; B )$\C{// implicit: 0 0 0}$for i := Mon; i <= Sun; i += 1 { fmt.Println( i ) fmt.Println( i ) } \lstMakeShortInline@ \end{cquote} The loop prints the values from 0 to 13 because there is no actual enumeration. However, the loop prints the values from 0 to 13 because there is no actual enumeration. \section{Java} \lstnewenvironment{Java}[1][]{\lstset{language=Java,escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{} \lstnewenvironment{Java}[1][]{\lstset{language=Java,morekeywords={enum,assert,strictfp}, escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{} Every enumeration in Java is an enumeration class. the scoped enumerators are an ordered list of @final@ methods of type integer, ordered left to right starting at 0, increasing by 1. The value of an enumeration instance is restricted to the enumeration's enumerators. There is an implicit integer variable in the enumeration used to store the value of an enumeration instance. The position (ordinal) and label are accessible, and the value is the same as the position. There is an implicit @int@ variable in the enumeration used to store the value of an enumeration instance. The position (ordinal) and label are accessible, where the value is the same as the position. \begin{Java} System.out.println( !day.ordinal()! + " " + !day! ); // 5 Sat System.out.println( day.!ordinal()! + " " + day.!name()! ); // 5 Sat \end{Java} There is an inverse function @valueOf@ from string to enumerator. \begin{Java} day = Weekday.valueOf( "Wed" ); \end{Java} There are no implicit conversions to/from an enumerator and its underlying type. To explicitly assign enumerator values, the enumeration must specify an explicit type in the enumeration class. \begin{Java} enum Weekday { Mon( -4 ), Tue( -3 ), Wed( -2 ), Thu( -1 ), Fri( 0 ), Sat( 1 ), Sun( 2 ); // must appear first private int day;$\C{// underlying enumeration type}$private Weekday( int d ) { day = d; }$\C{// used to initialize enumerators and instances}$}; Weekday day = Weekday.Sat;$\C{// implicit constructor call}$\end{Java} The position, value, and label are accessible. \begin{Java} System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day! ); // 5 1 Sat \end{Java} The constructor is private so only initialization or assignment can be used to set an enumeration, which ensures only corresponding enumerator values are allowed. Like \Csharp, \VRef[Figure]{f:JavaFreeVersusClass} shows the same example for an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class. \hline \begin{Java} public class test { enum Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; static boolean isWeekday( Weekday wd ) { return Weekday.Mon.ordinal() <= wd.ordinal() && wd.ordinal() <= Weekday.Fri.ordinal(); } static boolean isWeekend( Weekday wd ) { return Weekday.Sat.ordinal() <= wd.ordinal() && wd.ordinal() <= Weekday.Sun.ordinal(); } public static void main( String[] args ) { Weekday day = Weekday.Sat; System.out.println( isWeekday( day ) ); System.out.println( isWeekend( day ) ); } enum Weekday !{! Mon, Tue, Wed, Thu, Fri, Sat, Sun !}!; static boolean isWeekday( Weekday wd ) { return wd.ordinal() <= Weekday.Fri.ordinal(); } static boolean isWeekend( Weekday wd ) { return Weekday.Fri.ordinal() < wd.ordinal(); } public static void main( String[] args ) { Weekday day = Weekday.Sat; System.out.println( isWeekday( day ) ); System.out.println( isWeekend( day ) ); } \end{Java} & \begin{Java} public class test { public !enum! WeekDay { Mon(-4), Tue(-3), Wed(-2), Thu(-1), Fri(0), Sat(1), Sun(2); private int day; public boolean isWeekday() { return day <= 0; } public boolean isWeekend() { return day > 0; } private WeekDay( int d ) { day = d; } enum Weekday !{! Mon, Tue, Wed, Thu, Fri, Sat, Sun; public boolean isWeekday() { return ordinal() <= Weekday.Fri.ordinal(); } public static void main( String[] args ) { WeekDay day = WeekDay.Sat; System.out.println( day.isWeekday() ); System.out.println( day.isWeekend() ); public boolean isWeekend() { return Weekday.Fri.ordinal() < ordinal(); } !}! public static void main( String[] args ) { WeekDay day = WeekDay.Sat; System.out.println( day.isWeekday() ); System.out.println( day.isWeekend() ); } \end{Java} \label{f:JavaFreeVersusClass} \end{figure} To explicitly assign enumerator values and/or use a non-@int@ enumeration type (any Java type may be used), the enumeration must specify an explicit type in the enumeration class and a constructor. \begin{Java} enum Weekday { Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first private !long! day;$\C{// underlying enumeration type}$private Weekday( !long! d ) { day = d; }$\C{// used to initialize enumerators}$}; Weekday day = Weekday.Sat; \end{Java} If an enumerator initialization is a runtime expression, the expression is executed once at the point the enumeration is declaraed. The position, value, and label are accessible. \begin{Java} System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! ); // 5 6 Sat \end{Java} The constructor is private so only initialization or assignment can be used to set an enumeration, which ensures only corresponding enumerator values are allowed. An enumeration can appear in a @switch@ statement, but no ranges. } \end{Java} Looping over an enumeration is done using method @values@, which returns the array of enumerator values. Like \Csharp, looping over an enumeration is done using method @values@, which returns the array of enumerator values (expensive operation). \begin{Java} for ( Weekday iday : Weekday.values() ) { System.out.println( iday.ordinal() + " " + iday + " " ); } System.out.print( iday.ordinal() + iday.day + " " + iday.name() + ", " ); } 0 1 Mon, 1 2 Tue, 2 3 Wed, 3 4 Thu, 4 5 Fri, 5 6 Sat, 6 7 Sun, \end{Java} \section{Modula-3} \section{Rust} \lstnewenvironment{rust}[1][]{\lstset{language=Rust,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} Enumerations \begin{rust} Syntax Enumeration : enum IDENTIFIER  GenericParams? WhereClause? { EnumItems? } EnumItems : EnumItem ( , EnumItem )* ,? EnumItem : OuterAttribute* Visibility? IDENTIFIER ( EnumItemTuple | EnumItemStruct )? EnumItemDiscriminant? EnumItemTuple : ( TupleFields? ) EnumItemStruct : { StructFields? } EnumItemDiscriminant : = Expression \end{rust} An enumeration, also referred to as an enum, is a simultaneous definition of a nominal enumerated type as well as a set of constructors, that can be used to create or pattern-match values of the corresponding enumerated type. Enumerations are declared with the keyword enum. An example of an enum item and its use: \begin{rust} enum Animal { Dog, Cat, } let mut a: Animal = Animal::Dog; a = Animal::Cat; \end{rust} Enum constructors can have either named or unnamed fields: \begin{rust} enum Animal { Dog(String, f64), Cat { name: String, weight: f64 }, } let mut a: Animal = Animal::Dog("Cocoa".to_string(), 37.2); a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; \end{rust} In this example, Cat is a struct-like enum variant, whereas Dog is simply called an enum variant. An enum where no constructors contain fields are called a field-less enum. For example, this is a fieldless enum: \begin{rust} enum Fieldless { Tuple(), Struct{}, Unit, } \end{rust} If a field-less enum only contains unit variants, the enum is called an unit-only enum. For example: \begin{rust} enum Enum { Foo = 3, Bar = 2, Baz = 1, } \end{rust} \subsection{Discriminants} Each enum instance has a discriminant: an integer logically associated to it that is used to determine which variant it holds. Under the default representation, the discriminant is interpreted as an isize value. However, the compiler is allowed to use a smaller type (or another means of distinguishing variants) in its actual memory layout. \subsection{Assigning discriminant values} \subsection{Explicit discriminants} In two circumstances, the discriminant of a variant may be explicitly set by following the variant name with = and a constant expression: if the enumeration is "unit-only". if a primitive representation is used. For example: \begin{rust} #[repr(u8)] enum Enum { Unit = 3, Tuple(u16), Struct { a: u8, b: u16, } = 1, } \end{rust} \subsection{Implicit discriminants} If a discriminant for a variant is not specified, then it is set to one higher than the discriminant of the previous variant in the declaration. If the discriminant of the first variant in the declaration is unspecified, then it is set to zero. \begin{rust} enum Foo { Bar,                    // 0 Baz = 123,        // 123 Quux,              // 124 } let baz_discriminant = Foo::Baz as u32; assert_eq!(baz_discriminant, 123); \end{rust} \subsection{Restrictions} It is an error when two variants share the same discriminant. \begin{rust} enum SharedDiscriminantError { SharedA = 1, SharedB = 1 } enum SharedDiscriminantError2 { Zero,      // 0 One,            // 1 OneToo = 1  // 1 (collision with previous!) } \end{rust} It is also an error to have an unspecified discriminant where the previous discriminant is the maximum value for the size of the discriminant. \begin{rust} #[repr(u8)] enum OverflowingDiscriminantError { Max = 255, MaxPlusOne // Would be 256, but that overflows the enum. } #[repr(u8)] enum OverflowingDiscriminantError2 { MaxMinusOne = 254, // 254 Max,                       // 255 MaxPlusOne               // Would be 256, but that overflows the enum. } \end{rust} \subsection{Accessing discriminant} \begin{rust} Via mem::discriminant \end{rust} mem::discriminant returns an opaque reference to the discriminant of an enum value which can be compared. This cannot be used to get the value of the discriminant. Casting If an enumeration is unit-only (with no tuple and struct variants), then its discriminant can be directly accessed with a numeric cast; e.g.: \begin{rust} enum Enum { Foo, Bar, Baz, } assert_eq!(0, Enum::Foo as isize); assert_eq!(1, Enum::Bar as isize); assert_eq!(2, Enum::Baz as isize); \end{rust} Field-less enums can be casted if they do not have explicit discriminants, or where only unit variants are explicit. \begin{rust} enum Fieldless { Tuple(), Struct{}, Unit, } assert_eq!(0, Fieldless::Tuple() as isize); assert_eq!(1, Fieldless::Struct{} as isize); assert_eq!(2, Fieldless::Unit as isize); \end{rust} \begin{rust} #[repr(u8)] enum FieldlessWithDiscrimants { First = 10, Tuple(), Second = 20, Struct{}, Unit, } assert_eq!(10, FieldlessWithDiscrimants::First as u8); assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8); assert_eq!(20, FieldlessWithDiscrimants::Second as u8); assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8); assert_eq!(22, FieldlessWithDiscrimants::Unit as u8); \end{rust} \subsection{Pointer casting} If the enumeration specifies a primitive representation, then the discriminant may be reliably accessed via unsafe pointer casting: \begin{rust} #[repr(u8)] enum Enum { Unit, Tuple(bool), Struct{a: bool}, } impl Enum { fn discriminant(&self) -> u8 { unsafe { *(self as *const Self as *const u8) } } } let unit_like = Enum::Unit; let tuple_like = Enum::Tuple(true); let struct_like = Enum::Struct{a: false}; assert_eq!(0, unit_like.discriminant()); assert_eq!(1, tuple_like.discriminant()); assert_eq!(2, struct_like.discriminant()); \end{rust} \subsection{Zero-variant enums} Enums with zero variants are known as zero-variant enums. As they have no valid values, they cannot be instantiated. \begin{rust} enum ZeroVariants {} \end{rust} Zero-variant enums are equivalent to the never type, but they cannot be coerced into other types. \begin{rust} let x: ZeroVariants = panic!(); let y: u32 = x; // mismatched type error \end{rust} \subsection{Variant visibility} Enum variants syntactically allow a Visibility annotation, but this is rejected when the enum is validated. This allows items to be parsed with a unified syntax across different contexts where they are used. \begin{rust} macro_rules! mac_variant { ($vis:vis$name:ident) => { enum $name {$vis Unit, $vis Tuple(u8, u16),$vis Struct { f: u8 }, } } } // Empty vis is allowed. mac_variant! { E } // This is allowed, since it is removed before being validated. #[cfg(FALSE)] enum E { pub U, pub(crate) T(u8), pub(super) T { f: String } } \end{rust} \lstnewenvironment{swift}[1][]{\lstset{language=Swift,escapechar=\\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} Model custom types that define a list of possible values. % https://www.programiz.com/swift/online-compiler A Swift enumeration provides a heterogenous set of enumerators, like a tagged @union@, where the field name is the enumerator and its list of type parameters form its type. \begin{swift} enum Many { case Mon, Tue, Wed, Thu, Fri, Sat, Sun // basic enumerator case code( String ) // string enumerator case tuple( Int, Int, Int ) // tuple enumerator }; var day = Many.Sat; // qualification to resolve type print( day ); day = .Wed // no qualification after type resolved print( day ); day = .code( "ABC" ); print( day ); day = .tuple( 1, 2, 3 ); print( day ); Sat Wed code("ABC") tuple(1, 2, 3) \end{swift} An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code. \paragraph{Enumeration Syntax} You introduce enumerations with the @enum@ keyword and place their entire definition within a pair of braces: \begin{swift} enum SomeEnumeration { // enumeration definition goes here } \end{swift} Here's an example for the four main points of a compass: \begin{swift} enum CompassPoint { case north case south case east case west } \end{swift} The values defined in an enumeration (such as @north@, @south@, @east@, and @west@) are its enumeration cases. You use the @case@ keyword to introduce new enumeration cases. Note: