1 | \chapter{Related Work} |
---|
2 | \label{s:RelatedWork} |
---|
3 | |
---|
4 | 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. |
---|
5 | Among theses languages, there are a large set of overlapping features, but each language has its own unique extensions and restrictions. |
---|
6 | |
---|
7 | |
---|
8 | \section{Pascal} |
---|
9 | \lstnewenvironment{pascal}[1][]{\lstset{language=pascal,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
10 | |
---|
11 | Classic Pascal has the \lstinline[language=pascal]{const} declaration binding a name to a constant literal/expression. |
---|
12 | \begin{pascal} |
---|
13 | const one = 0 + 1; Vowels = set of (A,E,I,O,U); NULL = NIL; |
---|
14 | PI = 3.14159; Plus = '+'; Fred = 'Fred'; |
---|
15 | \end{pascal} |
---|
16 | Here, there is no enumeration because there is no specific type (pseudo enumeration). |
---|
17 | Hence, there is no notion of a (possibly ordered) set, modulo the \lstinline[language=pascal]{set of} type. |
---|
18 | The type of each constant name (enumerator) is inferred from the constant-expression type. |
---|
19 | |
---|
20 | Free Pascal~\cite[\S~3.1.1]{FreePascal} is a modern, object-oriented version of classic Pascal, with a C-style enumeration type. |
---|
21 | Enumerators must be assigned in ascending numerical order with a constant expression and the range can be non-consecutive. |
---|
22 | \begin{pascal} |
---|
23 | Type EnumType = ( one, two, three, forty @= 40@, fortyone ); |
---|
24 | \end{pascal} |
---|
25 | Pseudo-functions @Pred@ and @Succ@ can only be used if the range is consecutive. |
---|
26 | The underlying type is an implementation-defined integral-type large enough to hold all enumerated values; it does not have to be the smallest possible type. |
---|
27 | The integral size can be explicitly specified using compiler directive @$PACKENUM@~$N$, where $N$ is the number of bytes, \eg: |
---|
28 | \begin{pascal} |
---|
29 | Type @{$\color{red}\$$PACKENUM 1}@ SmallEnum = ( one, two, three ); |
---|
30 | @{$\color{red}\$$PACKENUM 4}@ LargeEnum = ( BigOne, BigTwo, BigThree ); |
---|
31 | Var S : SmallEnum; { 1 byte } |
---|
32 | L : LargeEnum; { 4 bytes} |
---|
33 | \end{pascal} |
---|
34 | |
---|
35 | |
---|
36 | \section{Ada} |
---|
37 | \lstnewenvironment{ada}[1][]{\lstset{language=[2005]Ada,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},literate={'}{\ttfamily'\!}1}\lstset{#1}}{} |
---|
38 | |
---|
39 | An Ada enumeration type is an ordered list of constants, called \Newterm{literals} (enumerators). |
---|
40 | \begin{ada} |
---|
41 | type RGB is ( Red, Green, Blue ); -- 3 literals (enumerators) |
---|
42 | \end{ada} |
---|
43 | Object initialization and assignment are restricted to the enumerators of this type. |
---|
44 | 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. |
---|
45 | To explicitly set enumerator values, \emph{all} enumerators must be set in \emph{ascending} order, \ie there is no auto-initialization. |
---|
46 | \begin{ada} |
---|
47 | type RGB is ( Red, Green, Blue ); |
---|
48 | @for RGB use ( Red => 10, Green => 20, Blue => 30 );@ -- ascending order |
---|
49 | \end{ada} |
---|
50 | Hence, the position, value, label tuples are: |
---|
51 | \begin{ada} |
---|
52 | (0, 10, RED) (1, 20, GREEN) (2, 30, BLUE) |
---|
53 | \end{ada} |
---|
54 | 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). |
---|
55 | |
---|
56 | Like C, Ada enumerators are unscoped, \ie enumerators declared inside of an enum are visible (projected) into the enclosing scope. |
---|
57 | The enumeration operators are the ordering operators, @=@, @<@, @<=@, @=@, @/=@, @>=@, @>@, where the ordering relationship is given implicitly by the sequence of enumerators, which is always ascending. |
---|
58 | |
---|
59 | Ada enumerators are overloadable. |
---|
60 | \begin{ada} |
---|
61 | type Traffic_Light is ( @Red@, Yellow, @Green@ ); |
---|
62 | \end{ada} |
---|
63 | Like \CFA, Ada uses an advanced type-resolution algorithm, including the left-hand side of assignment, to disambiguate among overloaded names. |
---|
64 | \VRef[Figure]{f:AdaEnumeration} shows how ambiguity is handled using a cast, \eg \lstinline[language=ada]{RGB'(Red)}. |
---|
65 | |
---|
66 | \begin{figure} |
---|
67 | \begin{ada} |
---|
68 | with Ada.Text_IO; use Ada.Text_IO; |
---|
69 | procedure test is |
---|
70 | type RGB is ( @Red@, Green, Blue ); |
---|
71 | type Traffic_Light is ( @Red@, Yellow, Green ); -- overload |
---|
72 | procedure @Red@( Colour : RGB ) is begin -- overload |
---|
73 | Put_Line( "Colour is " & RGB'Image( Colour ) ); |
---|
74 | end Red; |
---|
75 | procedure @Red@( TL : Traffic_Light ) is begin -- overload |
---|
76 | Put_Line( "Light is " & Traffic_Light'Image( TL ) ); |
---|
77 | end Red; |
---|
78 | begin |
---|
79 | @Red@( Blue ); -- RGB |
---|
80 | @Red@( Yellow ); -- Traffic_Light |
---|
81 | @Red@( @RGB'(Red)@ ); -- ambiguous without cast |
---|
82 | end test; |
---|
83 | \end{ada} |
---|
84 | \caption{Ada Enumeration Overload Resolution} |
---|
85 | \label{f:AdaEnumeration} |
---|
86 | \end{figure} |
---|
87 | |
---|
88 | Ada provides an alias mechanism, \lstinline[language=ada]{renames}, for aliasing types, which is useful to shorten package names. |
---|
89 | \begin{ada} |
---|
90 | OtherRed : RGB renames Red; |
---|
91 | \end{ada} |
---|
92 | which suggests a possible \CFA extension to @typedef@. |
---|
93 | \begin{cfa} |
---|
94 | typedef RGB.Red OtherRed; |
---|
95 | \end{cfa} |
---|
96 | |
---|
97 | There are three pairs of inverse enumeration pseudo-functions (attributes): @'Pos@ and @'Val@, @'Enum_Rep@ and @'Enum_Val@, and @'Image@ and @'Value@, |
---|
98 | \begin{cquote} |
---|
99 | \lstDeleteShortInline@ |
---|
100 | \setlength{\tabcolsep}{15pt} |
---|
101 | \begin{tabular}{@{}ll@{}} |
---|
102 | \begin{ada} |
---|
103 | RGB'Pos( Red ) = 0; |
---|
104 | RGB'Enum_Rep( Red ) = 10; |
---|
105 | RGB'Image( Red ) = "RED"; |
---|
106 | \end{ada} |
---|
107 | & |
---|
108 | \begin{ada} |
---|
109 | RGB'Val( 0 ) = Red |
---|
110 | RGB'Enum_Val( 10 ) = Red |
---|
111 | RGB'Value( "Red" ) = Red |
---|
112 | \end{ada} |
---|
113 | \end{tabular} |
---|
114 | \lstMakeShortInline@ |
---|
115 | \end{cquote} |
---|
116 | These attributes are important for IO. |
---|
117 | An enumeration type @T@ also has the following attributes: @T'First@, @T'Last@, @T'Range@, @T'Pred@, @T'Succ@, @T'Min@, and @T'Max@, producing an intuitive result based on the attribute name. |
---|
118 | |
---|
119 | Ada allows the enumerator label to be a character constant. |
---|
120 | \begin{ada} |
---|
121 | type Operator is ( '+', '-', '*', '/' ); |
---|
122 | \end{ada} |
---|
123 | which is syntactic sugar for the label and not character literals from the predefined type @Character@. |
---|
124 | The purpose is strictly readability using character literals rather than names. |
---|
125 | \begin{ada} |
---|
126 | Op : Operator := '+'; |
---|
127 | if Op = '+' or else Op = '-' then ... ; |
---|
128 | elsif Op = '*' or else Op = '/' then ... ; end if; |
---|
129 | \end{ada} |
---|
130 | Interestingly, arrays of character enumerators can be treated as strings. |
---|
131 | \begin{ada} |
---|
132 | Ops : array( 0..3 ) of Operator; |
---|
133 | Ops := @"+-*/"@; -- string assignment to array elements |
---|
134 | Ops := @"+-" & "*/"@; -- string concatenation and assignment |
---|
135 | \end{ada} |
---|
136 | Ada's @Character@ type is defined as a character enumeration across all Latin-1 characters. |
---|
137 | |
---|
138 | Ada's boolean type is also a special enumeration, which can be used in conditions. |
---|
139 | \begin{ada} |
---|
140 | type Boolean is (False, True); -- False / True not keywords |
---|
141 | @Flag@ : Boolean; |
---|
142 | if @Flag@ then ... -- conditional |
---|
143 | \end{ada} |
---|
144 | Since only types derived from @Boolean@ can be a conditional, @Boolean@ is essentially a builtin type. |
---|
145 | |
---|
146 | Ada provides \emph{consecutive} subtyping of an enumeration using \lstinline[language=ada]{range}. |
---|
147 | \begin{ada} |
---|
148 | type Week is ( Mon, Tue, Wed, Thu, Fri, Sat, Sun ); |
---|
149 | subtype Weekday is Week @range Mon .. Fri@; |
---|
150 | subtype Weekend is Week @range Sat .. Sun@; |
---|
151 | Day : Week; |
---|
152 | \end{ada} |
---|
153 | Hence, the ordering of the enumerators is crucial to provide the necessary ranges. |
---|
154 | |
---|
155 | An enumeration type can be used in the Ada \lstinline[language=ada]{case} (all enumerators must appear or a default) or iterating constructs. |
---|
156 | \begin{cquote} |
---|
157 | \lstDeleteShortInline@ |
---|
158 | \setlength{\tabcolsep}{15pt} |
---|
159 | \begin{tabular}{@{}ll@{}} |
---|
160 | \begin{ada} |
---|
161 | case Day is |
---|
162 | when @Mon .. Fri@ => ... ; |
---|
163 | when @Sat .. Sun@ => ... ; |
---|
164 | end case; |
---|
165 | \end{ada} |
---|
166 | & |
---|
167 | \begin{ada} |
---|
168 | case Day is |
---|
169 | when @Weekday@ => ... ; -- subtype ranges |
---|
170 | when @Weekend@ => ... ; |
---|
171 | end case; |
---|
172 | \end{ada} |
---|
173 | \end{tabular} |
---|
174 | \end{cquote} |
---|
175 | |
---|
176 | \begin{cquote} |
---|
177 | \setlength{\tabcolsep}{12pt} |
---|
178 | \begin{tabular}{@{}lll@{}} |
---|
179 | \begin{ada} |
---|
180 | for Day in @Mon .. Sun@ loop |
---|
181 | ... |
---|
182 | end loop; |
---|
183 | \end{ada} |
---|
184 | & |
---|
185 | \begin{ada} |
---|
186 | for Day in @Weekday@ loop |
---|
187 | ... |
---|
188 | end loop; |
---|
189 | \end{ada} |
---|
190 | & |
---|
191 | \begin{ada} |
---|
192 | for Day in @Weekend@ loop |
---|
193 | ... |
---|
194 | end loop; |
---|
195 | \end{ada} |
---|
196 | \end{tabular} |
---|
197 | \lstMakeShortInline@ |
---|
198 | \end{cquote} |
---|
199 | |
---|
200 | An enumeration type can be used as an array dimension and subscript. |
---|
201 | \begin{ada} |
---|
202 | Lunch : array( @Week@ ) of Time; |
---|
203 | for Day in Week loop |
---|
204 | Lunch( @Day@ ) := ... ; -- set lunch time |
---|
205 | end loop; |
---|
206 | \end{ada} |
---|
207 | |
---|
208 | |
---|
209 | \section{\CC} |
---|
210 | \label{s:C++RelatedWork} |
---|
211 | \lstnewenvironment{c++}[1][]{\lstset{language=[GNU]C++,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
212 | |
---|
213 | \CC is largely backwards compatible with C, so it inherited C's enumerations. |
---|
214 | However, the following non-backwards compatible changes have been made. |
---|
215 | |
---|
216 | \begin{cquote} |
---|
217 | 7.2 Change: \CC objects of enumeration type can only be assigned values of the same enumeration type. |
---|
218 | In C, objects of enumeration type can be assigned values of any integral type. \\ |
---|
219 | Example: |
---|
220 | \begin{c++} |
---|
221 | enum color { red, blue, green }; |
---|
222 | color c = 1; $\C{// valid C, invalid C++}$ |
---|
223 | \end{c++} |
---|
224 | \textbf{Rationale}: The type-safe nature of \CC. \\ |
---|
225 | \textbf{Effect on original feature}: Deletion of semantically well-defined feature. \\ |
---|
226 | \textbf{Difficulty of converting}: Syntactic transformation. (The type error produced by the assignment can be automatically corrected by applying an explicit cast.) \\ |
---|
227 | \textbf{How widely used}: Common. |
---|
228 | \end{cquote} |
---|
229 | |
---|
230 | \begin{cquote} |
---|
231 | 7.2 Change: In \CC, the type of an enumerator is its enumeration. |
---|
232 | In C, the type of an enumerator is @int@. \\ |
---|
233 | Example: |
---|
234 | \begin{c++} |
---|
235 | enum e { A }; |
---|
236 | sizeof(A) == sizeof(int) $\C{// in C}$ |
---|
237 | sizeof(A) == sizeof(e) $\C{// in C++}$ |
---|
238 | /* and sizeof(int) is not necessary equal to sizeof(e) */ |
---|
239 | \end{c++} |
---|
240 | \textbf{Rationale}: In \CC, an enumeration is a distinct type. \\ |
---|
241 | \textbf{Effect on original feature}: Change to semantics of well-defined feature. \\ |
---|
242 | \textbf{Difficulty of converting}: Semantic transformation. \\ |
---|
243 | \textbf{How widely used}: Seldom. The only time this affects existing C code is when the size of an enumerator is taken. |
---|
244 | Taking the size of an enumerator is not a common C coding practice. |
---|
245 | \end{cquote} |
---|
246 | |
---|
247 | Hence, the values in a \CC enumeration can only be its enumerators (without a cast). |
---|
248 | While the storage size of an enumerator is up to the compiler, there is still an implicit cast to @int@. |
---|
249 | \begin{c++} |
---|
250 | enum E { A, B, C }; |
---|
251 | E e = A; |
---|
252 | int i = A; i = e; $\C{// implicit casts to int}$ |
---|
253 | \end{c++} |
---|
254 | \CC{11} added a scoped enumeration, \lstinline[language=c++]{enum class} (or \lstinline[language=c++]{enum struct}), where the enumerators are accessed using type qualification. |
---|
255 | \begin{c++} |
---|
256 | enum class E { A, B, C }; |
---|
257 | E e = @E::@A; $\C{// qualified enumerator}$ |
---|
258 | e = B; $\C{// error: B not in scope}$ |
---|
259 | \end{c++} |
---|
260 | \CC{20} supports explicit unscoping with a \lstinline[language=c++]{using enum} declaration. |
---|
261 | \begin{c++} |
---|
262 | enum class E { A, B, C }; |
---|
263 | @using enum E;@ |
---|
264 | E e = A; $\C{// direct access}$ |
---|
265 | e = B; $\C{// direct access}$ |
---|
266 | \end{c++} |
---|
267 | \CC{11} added the ability to explicitly declare the underlying \emph{integral} type for \lstinline[language=c++]{enum class}. |
---|
268 | \begin{c++} |
---|
269 | enum class RGB @: long@ { Red, Green, Blue }; |
---|
270 | enum class rgb @: char@ { Red = 'r', Green = 'g', Blue = 'b' }; |
---|
271 | enum class srgb @: signed char@ { Red = -1, Green = 0, Blue = 1 }; |
---|
272 | \end{c++} |
---|
273 | There is no implicit conversion from the \lstinline[language=c++]{enum class} type to its declared type. |
---|
274 | \begin{c++} |
---|
275 | rgb crgb = rgb::Red; |
---|
276 | char ch = rgb::Red; ch = crgb; $\C{// error}$ |
---|
277 | \end{c++} |
---|
278 | Finally, enumerations can be used in the @switch@ statement but there is no mechanism to iterate through an enumeration. |
---|
279 | An enumeration type cannot declare an array dimension but can be used as a subscript. |
---|
280 | There is no mechanism to subtype or inherit from enumerations. |
---|
281 | |
---|
282 | |
---|
283 | \section{C\raisebox{-0.7ex}{\LARGE$^\sharp$}\xspace} % latex bug: cannot use \relsize{2} so use \LARGE |
---|
284 | \label{s:Csharp} |
---|
285 | \lstnewenvironment{csharp}[1][]{\lstset{language=[Sharp]C,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
286 | |
---|
287 | % https://www.tutorialsteacher.com/codeeditor?cid=cs-mk8Ojx |
---|
288 | |
---|
289 | \Csharp is a dynamically-typed programming-language with a scoped, integral enumeration-type similar to the C/\CC enumeration. |
---|
290 | \begin{csharp} |
---|
291 | enum Weekday : byte { Mon, Tue, Wed, Thu@ = 10@, Fri, Sat, Sun@,@ }; |
---|
292 | \end{csharp} |
---|
293 | The default underlying type is @int@, with auto-incrementing, implicit/explicit initialization, terminator comma, and optional integral typing (default @int@). |
---|
294 | A method cannot be defined in an enumeration type. |
---|
295 | 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. |
---|
296 | \begin{csharp} |
---|
297 | int day = (int)Weekday.Fri; $\C{// day == 10}$ |
---|
298 | Weekday weekday = (Weekdays)42; $\C{// weekday == 42, logically invalid}$ |
---|
299 | Console.WriteLine( Weekday.Fri ); $\C{// print Fri}$ |
---|
300 | string mon = Weekday.Mon.ToString(); $\C{// mon == "Mon"}$ |
---|
301 | \end{csharp} |
---|
302 | |
---|
303 | The @Enum.GetValues@ pseudo-method retrieves an array of the enumeration constants for looping over an enumeration type or variable (expensive operation). |
---|
304 | \begin{csharp} |
---|
305 | foreach ( Weekday constant in @Enum.GetValues@( typeof(Weekday) ) ) { |
---|
306 | Console.WriteLine( constant + " " + (int)constant ); // label, position |
---|
307 | } |
---|
308 | \end{csharp} |
---|
309 | |
---|
310 | The @Flags@ attribute creates a bit-flags enumeration, allowing bitwise operators @&@, @|@, @~@ (complement), @^@ (xor). |
---|
311 | \begin{csharp} |
---|
312 | @[Flags]@ public enum Weekday { |
---|
313 | None = 0x0, Mon = 0x1, Tue = 0x2, Wed = 0x4, |
---|
314 | Thu = 0x8, Fri = 0x10, Sat = 0x20, Sun = 0x40, |
---|
315 | Weekend = @Sat | Sun@, |
---|
316 | Weekdays = @Mon | Tue | Wed | Thu | Fri@ |
---|
317 | } |
---|
318 | Weekday meetings = @Weekday.Mon | Weekday.Wed@; // 0x5 |
---|
319 | \end{csharp} |
---|
320 | |
---|
321 | \VRef[Figure]{CsharpFreeVersusClass} shows an enumeration with free routines for manipulation, and embedding the enumeration and operations into an enumeration class. |
---|
322 | The key observation is that an enumeration class is just a structuring mechanism without any additional semantics. |
---|
323 | |
---|
324 | % https://learn.microsoft.com/en-us/dotnet/api/system.enum?view=net-8.0 |
---|
325 | |
---|
326 | \begin{figure} |
---|
327 | \centering |
---|
328 | \lstDeleteShortInline@ |
---|
329 | \begin{tabular}{@{}l|l@{}} |
---|
330 | \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ |
---|
331 | \hline |
---|
332 | \begin{csharp} |
---|
333 | public class Program { |
---|
334 | |
---|
335 | enum Weekday { |
---|
336 | Mon, Tue, Wed, Thu, Fri, Sat, Sun }; |
---|
337 | |
---|
338 | static bool isWeekday( Weekday wd ) { |
---|
339 | return wd <= Weekday.Fri; |
---|
340 | } |
---|
341 | static bool isWeekend( Weekday wd ) { |
---|
342 | return Weekday.Sat <= wd; |
---|
343 | } |
---|
344 | |
---|
345 | |
---|
346 | public static void Main() { |
---|
347 | Weekday day = Weekday.Sat; |
---|
348 | |
---|
349 | Console.WriteLine( isWeekday( day ) ); |
---|
350 | Console.WriteLine( isWeekend( day ) ); |
---|
351 | } |
---|
352 | } |
---|
353 | \end{csharp} |
---|
354 | & |
---|
355 | \begin{csharp} |
---|
356 | public class Program { |
---|
357 | public @class@ WeekDay : Enumeration { |
---|
358 | public enum Day { |
---|
359 | Mon, Tue, Wed, Thu, Fri, Sat, Sun }; |
---|
360 | public enum Day2 : Day { |
---|
361 | XXX, YYY }; |
---|
362 | Day day; |
---|
363 | public bool isWeekday() { |
---|
364 | return day <= Day.Fri; |
---|
365 | } |
---|
366 | public bool isWeekend() { |
---|
367 | return day > Day.Fri; |
---|
368 | } |
---|
369 | public WeekDay( Day d ) { day = d; } |
---|
370 | } |
---|
371 | public static void Main() { |
---|
372 | WeekDay cday = new |
---|
373 | WeekDay( WeekDay.Day.Sat ); |
---|
374 | Console.WriteLine( cday.isWeekday() ); |
---|
375 | Console.WriteLine( cday.isWeekend() ); |
---|
376 | } |
---|
377 | } |
---|
378 | \end{csharp} |
---|
379 | \end{tabular} |
---|
380 | \lstMakeShortInline@ |
---|
381 | \caption{\Csharp: Free Routine Versus Class Enumeration} |
---|
382 | \label{CsharpFreeVersusClass} |
---|
383 | \end{figure} |
---|
384 | |
---|
385 | |
---|
386 | \section{Golang} |
---|
387 | \lstnewenvironment{Go}[1][]{\lstset{language=Go,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
388 | |
---|
389 | Golang provides pseudo-enumeration similar to classic Pascal \lstinline[language=pascal]{const}, binding a name to a constant literal/expression. |
---|
390 | \begin{Go} |
---|
391 | const ( R = 0; G; B ) $\C{// implicit: 0 0 0}$ |
---|
392 | const ( Fred = "Fred"; Mary = "Mary"; Jane = "Jane" ) $\C{// explicit: Fred Mary Jane}$ |
---|
393 | const ( S = 0; T; USA = "USA"; U; V = 3.1; W ) $\C{// type change, implicit/explicit: 0 0 USA USA 3.1 3.1}$ |
---|
394 | \end{Go} |
---|
395 | Constant names are unscoped and must be unique (no overloading). |
---|
396 | The first enumerator \emph{must} be explicitly initialized; |
---|
397 | subsequent enumerators can be implicitly or explicitly initialized. |
---|
398 | Implicit initialization is the previous (predecessor) enumerator value. |
---|
399 | |
---|
400 | Auto-incrementing is supported by the keyword \lstinline[language=Go]{iota}, available only in the \lstinline[language=Go]{const} declaration. |
---|
401 | The \lstinline[language=Go]{iota} is a \emph{per \lstinline[language=golang]{const} declaration} integer counter, starting at zero and implicitly incremented by one for each \lstinline[language=golang]{const} identifier (enumerator). |
---|
402 | \begin{Go} |
---|
403 | const ( R = @iota@; G; B ) $\C{// implicit: 0 1 2}$ |
---|
404 | const ( C = @iota + B + 1@; G; Y ) $\C{// implicit: 3 4 5}$ |
---|
405 | \end{Go} |
---|
406 | An underscore \lstinline[language=golang]{const} identifier advances \lstinline[language=Go]{iota}. |
---|
407 | \begin{Go} |
---|
408 | const ( O1 = iota + 1; @_@; O3; @_@; O5 ) // 1, 3, 5 |
---|
409 | \end{Go} |
---|
410 | Auto-incrementing stops after an explicit initialization. |
---|
411 | \begin{Go} |
---|
412 | const ( Mon = iota; Tue; Wed; // 0, 1, 2 |
---|
413 | @Thu = 10@; Fri; Sat; Sun ) // 10, 10, 10, 10 |
---|
414 | \end{Go} |
---|
415 | Auto-incrementing can be restarted with an expression containing \emph{one} \lstinline[language=Go]{iota}. |
---|
416 | \begin{Go} |
---|
417 | const ( V1 = iota; V2; @V3 = 7;@ V4 = @iota@; V5 ) // 0 1 7 3 4 |
---|
418 | const ( Mon = iota; Tue; Wed; // 0, 1, 2 |
---|
419 | @Thu = 10;@ Fri = @iota - Wed + Thu - 1@; Sat; Sun ) // 10, 11, 12, 13 |
---|
420 | \end{Go} |
---|
421 | Note, \lstinline[language=Go]{iota} is advanced for an explicitly initialized enumerator, like the underscore @_@ identifier. |
---|
422 | |
---|
423 | Basic switch and looping are possible. |
---|
424 | \begin{cquote} |
---|
425 | \lstDeleteShortInline@ |
---|
426 | \setlength{\tabcolsep}{15pt} |
---|
427 | \begin{tabular}{@{}ll@{}} |
---|
428 | \begin{Go} |
---|
429 | day := Mon; |
---|
430 | switch day { |
---|
431 | case Mon, Tue, Wed, Thu, Fri: |
---|
432 | fmt.Println( "weekday" ); |
---|
433 | case Sat, Sun: |
---|
434 | fmt.Println( "weekend" ); |
---|
435 | } |
---|
436 | \end{Go} |
---|
437 | & |
---|
438 | \begin{Go} |
---|
439 | |
---|
440 | for i := Mon; i <= Sun; i += 1 { |
---|
441 | fmt.Println( i ) |
---|
442 | } |
---|
443 | |
---|
444 | |
---|
445 | |
---|
446 | \end{Go} |
---|
447 | \end{tabular} |
---|
448 | \lstMakeShortInline@ |
---|
449 | \end{cquote} |
---|
450 | However, the loop prints the values from 0 to 13 because there is no actual enumeration. |
---|
451 | |
---|
452 | |
---|
453 | \section{Java} |
---|
454 | \lstnewenvironment{Java}[1][]{\lstset{language=Java,morekeywords={enum,assert,strictfp}, |
---|
455 | escapechar=\$,moredelim=**[is][\color{red}]{!}{!},}\lstset{#1}}{} |
---|
456 | |
---|
457 | Every enumeration in Java is an enumeration class. |
---|
458 | For a basic enumeration |
---|
459 | \begin{Java} |
---|
460 | enum Weekday { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; |
---|
461 | Weekday day = Weekday.Sat; |
---|
462 | \end{Java} |
---|
463 | the scoped enumerators are an ordered list of @final@ methods of type integer, ordered left to right starting at 0, increasing by 1. |
---|
464 | The value of an enumeration instance is restricted to the enumeration's enumerators. |
---|
465 | There is an implicit @int@ variable in the enumeration used to store the value of an enumeration instance. |
---|
466 | The position (ordinal) and label are accessible, where the value is the same as the position. |
---|
467 | \begin{Java} |
---|
468 | System.out.println( day.!ordinal()! + " " + day.!name()! ); // 5 Sat |
---|
469 | \end{Java} |
---|
470 | There is an inverse function @valueOf@ from string to enumerator. |
---|
471 | \begin{Java} |
---|
472 | day = Weekday.valueOf( "Wed" ); |
---|
473 | \end{Java} |
---|
474 | There are no implicit conversions to/from an enumerator and its underlying type. |
---|
475 | 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. |
---|
476 | |
---|
477 | \begin{figure} |
---|
478 | \centering |
---|
479 | \lstDeleteShortInline@ |
---|
480 | \begin{tabular}{@{}l|l@{}} |
---|
481 | \multicolumn{1}{@{}c|}{non-object oriented} & \multicolumn{1}{c@{}}{object oriented} \\ |
---|
482 | \hline |
---|
483 | \begin{Java} |
---|
484 | enum Weekday !{! |
---|
485 | Mon, Tue, Wed, Thu, Fri, Sat, Sun !}!; |
---|
486 | |
---|
487 | static boolean isWeekday( Weekday wd ) { |
---|
488 | return wd.ordinal() <= Weekday.Fri.ordinal(); |
---|
489 | } |
---|
490 | static boolean isWeekend( Weekday wd ) { |
---|
491 | return Weekday.Fri.ordinal() < wd.ordinal(); |
---|
492 | } |
---|
493 | |
---|
494 | public static void main( String[] args ) { |
---|
495 | Weekday day = Weekday.Sat; |
---|
496 | System.out.println( isWeekday( day ) ); |
---|
497 | System.out.println( isWeekend( day ) ); |
---|
498 | } |
---|
499 | \end{Java} |
---|
500 | & |
---|
501 | \begin{Java} |
---|
502 | enum Weekday !{! |
---|
503 | Mon, Tue, Wed, Thu, Fri, Sat, Sun; |
---|
504 | |
---|
505 | public boolean isWeekday() { |
---|
506 | return ordinal() <= Weekday.Fri.ordinal(); |
---|
507 | } |
---|
508 | public boolean isWeekend() { |
---|
509 | return Weekday.Fri.ordinal() < ordinal(); |
---|
510 | } |
---|
511 | !}! |
---|
512 | public static void main( String[] args ) { |
---|
513 | WeekDay day = WeekDay.Sat; |
---|
514 | System.out.println( day.isWeekday() ); |
---|
515 | System.out.println( day.isWeekend() ); |
---|
516 | } |
---|
517 | \end{Java} |
---|
518 | \end{tabular} |
---|
519 | \lstMakeShortInline@ |
---|
520 | \caption{Java: Free Routine Versus Class Enumeration} |
---|
521 | \label{f:JavaFreeVersusClass} |
---|
522 | \end{figure} |
---|
523 | |
---|
524 | 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. |
---|
525 | \begin{Java} |
---|
526 | enum Weekday { |
---|
527 | Mon!(1)!, Tue!(2)!, Wed!(3)!, Thu!(4)!, Fri!(5)!, Sat!(6)!, Sun!(7)!; // must appear first |
---|
528 | private !long! day; $\C{// underlying enumeration type}$ |
---|
529 | private Weekday( !long! d ) { day = d; } $\C{// used to initialize enumerators}$ |
---|
530 | }; |
---|
531 | Weekday day = Weekday.Sat; |
---|
532 | \end{Java} |
---|
533 | If an enumerator initialization is a runtime expression, the expression is executed once at the point the enumeration is declaraed. |
---|
534 | |
---|
535 | The position, value, and label are accessible. |
---|
536 | \begin{Java} |
---|
537 | System.out.println( !day.ordinal()! + " " + !day.day! + " " + !day.name()! ); // 5 6 Sat |
---|
538 | \end{Java} |
---|
539 | The constructor is private so only initialization or assignment can be used to set an enumeration, which ensures only corresponding enumerator values are allowed. |
---|
540 | |
---|
541 | An enumeration can appear in a @switch@ statement, but no ranges. |
---|
542 | \begin{Java} |
---|
543 | switch ( day ) { |
---|
544 | case Mon: case Tue: case Wed: case Thu: case Fri: |
---|
545 | System.out.println( "weekday" ); |
---|
546 | break; |
---|
547 | case Sat: case Sun: |
---|
548 | System.out.println( "weekend" ); |
---|
549 | break; |
---|
550 | } |
---|
551 | \end{Java} |
---|
552 | Like \Csharp, looping over an enumeration is done using method @values@, which returns the array of enumerator values (expensive operation). |
---|
553 | \begin{Java} |
---|
554 | for ( Weekday iday : Weekday.values() ) { |
---|
555 | System.out.print( iday.ordinal() + iday.day + " " + iday.name() + ", " ); |
---|
556 | } |
---|
557 | 0 1 Mon, 1 2 Tue, 2 3 Wed, 3 4 Thu, 4 5 Fri, 5 6 Sat, 6 7 Sun, |
---|
558 | \end{Java} |
---|
559 | |
---|
560 | As well, Java provides an @EnumSet@ where the underlying type is an efficient set of bits, one per enumeration \see{\Csharp \lstinline{Flags}, \VRef{s:Csharp}}, providing (logical) operations on groups of enumerators. |
---|
561 | There is also a specialized version of @HashMap@ with enumerator keys, which has performance benefits. |
---|
562 | |
---|
563 | Enumeration inheritence is disallowed because an enumeration is @final@. |
---|
564 | |
---|
565 | |
---|
566 | |
---|
567 | \section{Modula-3} |
---|
568 | |
---|
569 | |
---|
570 | |
---|
571 | \section{Rust} |
---|
572 | \lstnewenvironment{rust}[1][]{\lstset{language=Rust,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
573 | |
---|
574 | Enumerations |
---|
575 | \begin{rust} |
---|
576 | Syntax |
---|
577 | Enumeration : |
---|
578 | enum IDENTIFIER GenericParams? WhereClause? { EnumItems? } |
---|
579 | |
---|
580 | EnumItems : |
---|
581 | EnumItem ( , EnumItem )* ,? |
---|
582 | |
---|
583 | EnumItem : |
---|
584 | OuterAttribute* Visibility? |
---|
585 | IDENTIFIER ( EnumItemTuple | EnumItemStruct )? EnumItemDiscriminant? |
---|
586 | |
---|
587 | EnumItemTuple : |
---|
588 | ( TupleFields? ) |
---|
589 | |
---|
590 | EnumItemStruct : |
---|
591 | { StructFields? } |
---|
592 | |
---|
593 | EnumItemDiscriminant : |
---|
594 | = Expression |
---|
595 | \end{rust} |
---|
596 | 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. |
---|
597 | |
---|
598 | Enumerations are declared with the keyword enum. |
---|
599 | |
---|
600 | An example of an enum item and its use: |
---|
601 | \begin{rust} |
---|
602 | enum Animal { |
---|
603 | Dog, |
---|
604 | Cat, |
---|
605 | } |
---|
606 | |
---|
607 | let mut a: Animal = Animal::Dog; |
---|
608 | a = Animal::Cat; |
---|
609 | \end{rust} |
---|
610 | Enum constructors can have either named or unnamed fields: |
---|
611 | \begin{rust} |
---|
612 | enum Animal { |
---|
613 | Dog(String, f64), |
---|
614 | Cat { name: String, weight: f64 }, |
---|
615 | } |
---|
616 | |
---|
617 | let mut a: Animal = Animal::Dog("Cocoa".to_string(), 37.2); |
---|
618 | a = Animal::Cat { name: "Spotty".to_string(), weight: 2.7 }; |
---|
619 | \end{rust} |
---|
620 | In this example, Cat is a struct-like enum variant, whereas Dog is simply called an enum variant. |
---|
621 | |
---|
622 | An enum where no constructors contain fields are called a field-less enum. For example, this is a fieldless enum: |
---|
623 | \begin{rust} |
---|
624 | enum Fieldless { |
---|
625 | Tuple(), |
---|
626 | Struct{}, |
---|
627 | Unit, |
---|
628 | } |
---|
629 | \end{rust} |
---|
630 | If a field-less enum only contains unit variants, the enum is called an unit-only enum. For example: |
---|
631 | \begin{rust} |
---|
632 | enum Enum { |
---|
633 | Foo = 3, |
---|
634 | Bar = 2, |
---|
635 | Baz = 1, |
---|
636 | } |
---|
637 | \end{rust} |
---|
638 | |
---|
639 | \subsection{Discriminants} |
---|
640 | |
---|
641 | Each enum instance has a discriminant: an integer logically associated to it that is used to determine which variant it holds. |
---|
642 | |
---|
643 | 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. |
---|
644 | |
---|
645 | \subsection{Assigning discriminant values} |
---|
646 | |
---|
647 | \subsection{Explicit discriminants} |
---|
648 | |
---|
649 | In two circumstances, the discriminant of a variant may be explicitly set by following the variant name with = and a constant expression: |
---|
650 | |
---|
651 | if the enumeration is "unit-only". |
---|
652 | |
---|
653 | if a primitive representation is used. For example: |
---|
654 | \begin{rust} |
---|
655 | #[repr(u8)] |
---|
656 | enum Enum { |
---|
657 | Unit = 3, |
---|
658 | Tuple(u16), |
---|
659 | Struct { |
---|
660 | a: u8, |
---|
661 | b: u16, |
---|
662 | } = 1, |
---|
663 | } |
---|
664 | \end{rust} |
---|
665 | |
---|
666 | \subsection{Implicit discriminants} |
---|
667 | |
---|
668 | 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. |
---|
669 | \begin{rust} |
---|
670 | enum Foo { |
---|
671 | Bar, // 0 |
---|
672 | Baz = 123, // 123 |
---|
673 | Quux, // 124 |
---|
674 | } |
---|
675 | |
---|
676 | let baz_discriminant = Foo::Baz as u32; |
---|
677 | assert_eq!(baz_discriminant, 123); |
---|
678 | \end{rust} |
---|
679 | |
---|
680 | \subsection{Restrictions} |
---|
681 | |
---|
682 | It is an error when two variants share the same discriminant. |
---|
683 | \begin{rust} |
---|
684 | enum SharedDiscriminantError { |
---|
685 | SharedA = 1, |
---|
686 | SharedB = 1 |
---|
687 | } |
---|
688 | |
---|
689 | enum SharedDiscriminantError2 { |
---|
690 | Zero, // 0 |
---|
691 | One, // 1 |
---|
692 | OneToo = 1 // 1 (collision with previous!) |
---|
693 | } |
---|
694 | \end{rust} |
---|
695 | It is also an error to have an unspecified discriminant where the previous discriminant is the maximum value for the size of the discriminant. |
---|
696 | \begin{rust} |
---|
697 | #[repr(u8)] |
---|
698 | enum OverflowingDiscriminantError { |
---|
699 | Max = 255, |
---|
700 | MaxPlusOne // Would be 256, but that overflows the enum. |
---|
701 | } |
---|
702 | |
---|
703 | #[repr(u8)] |
---|
704 | enum OverflowingDiscriminantError2 { |
---|
705 | MaxMinusOne = 254, // 254 |
---|
706 | Max, // 255 |
---|
707 | MaxPlusOne // Would be 256, but that overflows the enum. |
---|
708 | } |
---|
709 | \end{rust} |
---|
710 | |
---|
711 | \subsection{Accessing discriminant} |
---|
712 | |
---|
713 | \begin{rust} |
---|
714 | Via mem::discriminant |
---|
715 | \end{rust} |
---|
716 | 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. |
---|
717 | Casting |
---|
718 | |
---|
719 | 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.: |
---|
720 | \begin{rust} |
---|
721 | enum Enum { |
---|
722 | Foo, |
---|
723 | Bar, |
---|
724 | Baz, |
---|
725 | } |
---|
726 | |
---|
727 | assert_eq!(0, Enum::Foo as isize); |
---|
728 | assert_eq!(1, Enum::Bar as isize); |
---|
729 | assert_eq!(2, Enum::Baz as isize); |
---|
730 | \end{rust} |
---|
731 | Field-less enums can be casted if they do not have explicit discriminants, or where only unit variants are explicit. |
---|
732 | \begin{rust} |
---|
733 | enum Fieldless { |
---|
734 | Tuple(), |
---|
735 | Struct{}, |
---|
736 | Unit, |
---|
737 | } |
---|
738 | |
---|
739 | assert_eq!(0, Fieldless::Tuple() as isize); |
---|
740 | assert_eq!(1, Fieldless::Struct{} as isize); |
---|
741 | assert_eq!(2, Fieldless::Unit as isize); |
---|
742 | \end{rust} |
---|
743 | \begin{rust} |
---|
744 | #[repr(u8)] |
---|
745 | enum FieldlessWithDiscrimants { |
---|
746 | First = 10, |
---|
747 | Tuple(), |
---|
748 | Second = 20, |
---|
749 | Struct{}, |
---|
750 | Unit, |
---|
751 | } |
---|
752 | |
---|
753 | assert_eq!(10, FieldlessWithDiscrimants::First as u8); |
---|
754 | assert_eq!(11, FieldlessWithDiscrimants::Tuple() as u8); |
---|
755 | assert_eq!(20, FieldlessWithDiscrimants::Second as u8); |
---|
756 | assert_eq!(21, FieldlessWithDiscrimants::Struct{} as u8); |
---|
757 | assert_eq!(22, FieldlessWithDiscrimants::Unit as u8); |
---|
758 | \end{rust} |
---|
759 | |
---|
760 | \subsection{Pointer casting} |
---|
761 | |
---|
762 | If the enumeration specifies a primitive representation, then the discriminant may be reliably accessed via unsafe pointer casting: |
---|
763 | \begin{rust} |
---|
764 | #[repr(u8)] |
---|
765 | enum Enum { |
---|
766 | Unit, |
---|
767 | Tuple(bool), |
---|
768 | Struct{a: bool}, |
---|
769 | } |
---|
770 | |
---|
771 | impl Enum { |
---|
772 | fn discriminant(&self) -> u8 { |
---|
773 | unsafe { *(self as *const Self as *const u8) } |
---|
774 | } |
---|
775 | } |
---|
776 | |
---|
777 | let unit_like = Enum::Unit; |
---|
778 | let tuple_like = Enum::Tuple(true); |
---|
779 | let struct_like = Enum::Struct{a: false}; |
---|
780 | |
---|
781 | assert_eq!(0, unit_like.discriminant()); |
---|
782 | assert_eq!(1, tuple_like.discriminant()); |
---|
783 | assert_eq!(2, struct_like.discriminant()); |
---|
784 | \end{rust} |
---|
785 | |
---|
786 | \subsection{Zero-variant enums} |
---|
787 | |
---|
788 | Enums with zero variants are known as zero-variant enums. As they have no valid values, they cannot be instantiated. |
---|
789 | \begin{rust} |
---|
790 | enum ZeroVariants {} |
---|
791 | \end{rust} |
---|
792 | Zero-variant enums are equivalent to the never type, but they cannot be coerced into other types. |
---|
793 | \begin{rust} |
---|
794 | let x: ZeroVariants = panic!(); |
---|
795 | let y: u32 = x; // mismatched type error |
---|
796 | \end{rust} |
---|
797 | |
---|
798 | \subsection{Variant visibility} |
---|
799 | |
---|
800 | 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. |
---|
801 | \begin{rust} |
---|
802 | macro_rules! mac_variant { |
---|
803 | ($vis:vis $name:ident) => { |
---|
804 | enum $name { |
---|
805 | $vis Unit, |
---|
806 | |
---|
807 | $vis Tuple(u8, u16), |
---|
808 | |
---|
809 | $vis Struct { f: u8 }, |
---|
810 | } |
---|
811 | } |
---|
812 | } |
---|
813 | |
---|
814 | // Empty `vis` is allowed. |
---|
815 | mac_variant! { E } |
---|
816 | |
---|
817 | // This is allowed, since it is removed before being validated. |
---|
818 | #[cfg(FALSE)] |
---|
819 | enum E { |
---|
820 | pub U, |
---|
821 | pub(crate) T(u8), |
---|
822 | pub(super) T { f: String } |
---|
823 | } |
---|
824 | \end{rust} |
---|
825 | |
---|
826 | |
---|
827 | \section{Swift} |
---|
828 | \lstnewenvironment{swift}[1][]{\lstset{language=Swift,escapechar=\$,moredelim=**[is][\color{red}]{@}{@},}\lstset{#1}}{} |
---|
829 | |
---|
830 | % https://www.programiz.com/swift/online-compiler |
---|
831 | |
---|
832 | 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. |
---|
833 | \begin{swift} |
---|
834 | enum Many { |
---|
835 | case Mon, Tue, Wed, Thu, Fri, Sat, Sun // basic enumerator |
---|
836 | case code( String ) // string enumerator |
---|
837 | case tuple( Int, Int, Int ) // tuple enumerator |
---|
838 | }; |
---|
839 | var day = Many.Sat; // qualification to resolve type |
---|
840 | print( day ); |
---|
841 | day = .Wed // no qualification after type resolved |
---|
842 | print( day ); |
---|
843 | day = .code( "ABC" ); |
---|
844 | print( day ); |
---|
845 | day = .tuple( 1, 2, 3 ); |
---|
846 | print( day ); |
---|
847 | |
---|
848 | Sat |
---|
849 | Wed |
---|
850 | code("ABC") |
---|
851 | tuple(1, 2, 3) |
---|
852 | \end{swift} |
---|
853 | |
---|
854 | |
---|
855 | 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. |
---|
856 | |
---|
857 | If you are familiar with C, you will know that C enumerations assign related names to a set of integer values. |
---|
858 | Enumerations in Swift are much more flexible, and don't have to provide a value for each case of the enumeration. |
---|
859 | If a value (known as a raw value) is provided for each enumeration case, the value can be a string, a character, or a value of any integer or floating-point type. |
---|
860 | |
---|
861 | Alternatively, enumeration cases can specify associated values of any type to be stored along with each different case value, much as unions or variants do in other languages. |
---|
862 | You can define a common set of related cases as part of one enumeration, each of which has a different set of values of appropriate types associated with it. |
---|
863 | |
---|
864 | Enumerations in Swift are first-class types in their own right. |
---|
865 | They adopt many features traditionally supported only by classes, such as computed properties to provide additional information about the enumeration's current value, and instance methods to provide functionality related to the values the enumeration represents. |
---|
866 | Enumerations can also define initializers to provide an initial case value; |
---|
867 | can be extended to expand their functionality beyond their original implementation; and can conform to protocols to provide standard functionality. |
---|
868 | |
---|
869 | For more about these capabilities, see Properties, Methods, Initialization, Extensions, and Protocols. |
---|
870 | |
---|
871 | \paragraph{Enumeration Syntax} |
---|
872 | |
---|
873 | |
---|
874 | Note: |
---|
875 | Swift enumeration cases don't have an integer value set by default, unlike languages like C and Objective-C. |
---|
876 | In the CompassPoint example above, @north@, @south@, @east@ and @west@ don't implicitly equal 0, 1, 2 and 3. |
---|
877 | Instead, the different enumeration cases are values in their own right, with an explicitly defined type of CompassPoint. |
---|
878 | |
---|
879 | Multiple cases can appear on a single line, separated by commas: |
---|
880 | \begin{swift} |
---|
881 | enum Planet { |
---|
882 | case mercury, venus, earth, mars, jupiter, saturn, uranus, neptune |
---|
883 | } |
---|
884 | \end{swift} |
---|
885 | Each enumeration definition defines a new type. |
---|
886 | Like other types in Swift, their names (such as @CompassPoint@ and @Planet@) start with a capital letter. |
---|
887 | Give enumeration types singular rather than plural names, so that they read as self-evident: |
---|
888 | \begin{swift} |
---|
889 | var directionToHead = CompassPoint.west |
---|
890 | \end{swift} |
---|
891 | The type of @directionToHead@ is inferred when it's initialized with one of the possible values of @CompassPoint@. |
---|
892 | Once @directionToHead@ is declared as a @CompassPoint@, you can set it to a different @CompassPoint@ value using a shorter dot syntax: |
---|
893 | \begin{swift} |
---|
894 | directionToHead = .east |
---|
895 | \end{swift} |
---|
896 | The type of @directionToHead@ is already known, and so you can drop the type when setting its value. |
---|
897 | This makes for highly readable code when working with explicitly typed enumeration values. |
---|
898 | |
---|
899 | \paragraph{Matching Enumeration Values with a Switch Statement} |
---|
900 | |
---|
901 | You can match individual enumeration values with a switch statement: |
---|
902 | \begin{swift} |
---|
903 | directionToHead = .south |
---|
904 | switch directionToHead { |
---|
905 | case .north: |
---|
906 | print("Lots of planets have a north") |
---|
907 | case .south: |
---|
908 | print("Watch out for penguins") |
---|
909 | case .east: |
---|
910 | print("Where the sun rises") |
---|
911 | case .west: |
---|
912 | print("Where the skies are blue") |
---|
913 | } |
---|
914 | // Prints "Watch out for penguins" |
---|
915 | \end{swift} |
---|
916 | You can read this code as: |
---|
917 | \begin{quote} |
---|
918 | "Consider the value of directionToHead. |
---|
919 | In the case where it equals @.north@, print "Lots of planets have a north". |
---|
920 | In the case where it equals @.south@, print "Watch out for penguins"." |
---|
921 | |
---|
922 | ...and so on. |
---|
923 | \end{quote} |
---|
924 | As described in Control Flow, a switch statement must be exhaustive when considering an enumeration's cases. |
---|
925 | If the case for @.west@ is omitted, this code doesn't compile, because it doesn't consider the complete list of @CompassPoint@ cases. |
---|
926 | Requiring exhaustiveness ensures that enumeration cases aren't accidentally omitted. |
---|
927 | |
---|
928 | When it isn't appropriate to provide a case for every enumeration case, you can provide a default case to cover any cases that aren't addressed explicitly: |
---|
929 | \begin{swift} |
---|
930 | let somePlanet = Planet.earth |
---|
931 | switch somePlanet { |
---|
932 | case .earth: |
---|
933 | print("Mostly harmless") |
---|
934 | default: |
---|
935 | print("Not a safe place for humans") |
---|
936 | } |
---|
937 | // Prints "Mostly harmless" |
---|
938 | \end{swift} |
---|
939 | |
---|
940 | \paragraph{Iterating over Enumeration Cases} |
---|
941 | |
---|
942 | For some enumerations, it's useful to have a collection of all of that enumeration's cases. |
---|
943 | You enable this by writing @CaseIterable@ after the enumeration's name. |
---|
944 | Swift exposes a collection of all the cases as an allCases property of the enumeration type. |
---|
945 | Here's an example: |
---|
946 | \begin{swift} |
---|
947 | enum Beverage: CaseIterable { |
---|
948 | case coffee, tea, juice |
---|
949 | } |
---|
950 | let numberOfChoices = Beverage.allCases.count |
---|
951 | print("\(numberOfChoices) beverages available") |
---|
952 | // Prints "3 beverages available" |
---|
953 | \end{swift} |
---|
954 | In the example above, you write @Beverage.allCases@ to access a collection that contains all of the cases of the @Beverage@ enumeration. |
---|
955 | You can use @allCases@ like any other collection -- the collection's elements are instances of the enumeration type, so in this case they're Beverage values. |
---|
956 | The example above counts how many cases there are, and the example below uses a for-in loop to iterate over all the cases. |
---|
957 | \begin{swift} |
---|
958 | for beverage in Beverage.allCases { |
---|
959 | print(beverage) |
---|
960 | } |
---|
961 | // coffee |
---|
962 | // tea |
---|
963 | // juice |
---|
964 | \end{swift} |
---|
965 | The syntax used in the examples above marks the enumeration as conforming to the @CaseIterable@ protocol. |
---|
966 | For information about protocols, see Protocols. |
---|
967 | |
---|
968 | \paragraph{Associated Values} |
---|
969 | The examples in the previous section show how the cases of an enumeration are a defined (and typed) value in their own right. |
---|
970 | You can set a constant or variable to Planet.earth, and check for this value later. |
---|
971 | However, it's sometimes useful to be able to store values of other types alongside these case values. |
---|
972 | This additional information is called an associated value, and it varies each time you use that case as a value in your code. |
---|
973 | |
---|
974 | You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed. |
---|
975 | Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages. |
---|
976 | |
---|
977 | For example, suppose an inventory tracking system needs to track products by two different types of barcode. |
---|
978 | Some products are labeled with 1D barcodes in UPC format, which uses the numbers 0 to 9. |
---|
979 | Each barcode has a number system digit, followed by five manufacturer code digits and five product code digits. |
---|
980 | These are followed by a check digit to verify that the code has been scanned correctly: |
---|
981 | |
---|
982 | Other products are labeled with 2D barcodes in QR code format, which can use any ISO 8859-1 character and can encode a string up to 2,953 characters long: |
---|
983 | |
---|
984 | It's convenient for an inventory tracking system to store UPC barcodes as a tuple of four integers, and QR code barcodes as a string of any length. |
---|
985 | |
---|
986 | In Swift, an enumeration to define product barcodes of either type might look like this: |
---|
987 | \begin{swift} |
---|
988 | enum Barcode { |
---|
989 | case upc(Int, Int, Int, Int) |
---|
990 | case qrCode(String) |
---|
991 | } |
---|
992 | \end{swift} |
---|
993 | This can be read as: |
---|
994 | \begin{quote} |
---|
995 | "Define an enumeration type called Barcode, which can take either a value of upc with an associated value of type @(Int, Int, Int, Int)@, or a value of @qrCode@ with an associated value of type @String@." |
---|
996 | \end{quote} |
---|
997 | This definition doesn't provide any actual @Int@ or @String@ values -- it just defines the type of associated values that Barcode constants and variables can store when they're equal to @Barcode.upc@ or @Barcode.qrCode@. |
---|
998 | |
---|
999 | You can then create new barcodes using either type: |
---|
1000 | \begin{swift} |
---|
1001 | var productBarcode = Barcode.upc(8, 85909, 51226, 3) |
---|
1002 | \end{swift} |
---|
1003 | This example creates a new variable called @productBarcode@ and assigns it a value of @Barcode.upc@ with an associated tuple value of @(8, 85909, 51226, 3)@. |
---|
1004 | |
---|
1005 | You can assign the same product a different type of barcode: |
---|
1006 | \begin{swift} |
---|
1007 | productBarcode = .qrCode("ABCDEFGHIJKLMNOP") |
---|
1008 | \end{swift} |
---|
1009 | At this point, the original @Barcode.upc@ and its integer values are replaced by the new @Barcode.qrCode@ and its string value. |
---|
1010 | Constants and variables of type Barcode can store either a @.upc@ or a @.qrCode@ (together with their associated values), but they can store only one of them at any given time. |
---|
1011 | |
---|
1012 | You can check the different barcode types using a switch statement, similar to the example in Matching Enumeration Values with a Switch Statement. |
---|
1013 | This time, however, the associated values are extracted as part of the switch statement. |
---|
1014 | You extract each associated value as a constant (with the let prefix) or a variable (with the var prefix) for use within the switch case's body: |
---|
1015 | \begin{swift} |
---|
1016 | switch productBarcode { |
---|
1017 | case .upc(let numberSystem, let manufacturer, let product, let check): |
---|
1018 | print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).") |
---|
1019 | case .qrCode(let productCode): |
---|
1020 | print("QR code: \(productCode).") |
---|
1021 | } |
---|
1022 | // Prints "QR code: ABCDEFGHIJKLMNOP." |
---|
1023 | \end{swift} |
---|
1024 | If all of the associated values for an enumeration case are extracted as constants, or if all are extracted as variables, you can place a single let or var annotation before the case name, for brevity: |
---|
1025 | \begin{swift} |
---|
1026 | switch productBarcode { |
---|
1027 | case let .upc(numberSystem, manufacturer, product, check): |
---|
1028 | print("UPC : \(numberSystem), \(manufacturer), \(product), \(check).") |
---|
1029 | case let .qrCode(productCode): |
---|
1030 | print("QR code: \(productCode).") |
---|
1031 | } |
---|
1032 | // Prints "QR code: ABCDEFGHIJKLMNOP." |
---|
1033 | \end{swift} |
---|
1034 | |
---|
1035 | \paragraph{Raw Values} |
---|
1036 | |
---|
1037 | The barcode example in Associated Values shows how cases of an enumeration can declare that they store associated values of different types. |
---|
1038 | As an alternative to associated values, enumeration cases can come prepopulated with default values (called raw values), which are all of the same type. |
---|
1039 | |
---|
1040 | Here's an example that stores raw ASCII values alongside named enumeration cases: |
---|
1041 | \begin{swift} |
---|
1042 | enum ASCIIControlCharacter: Character { |
---|
1043 | case tab = "\t" |
---|
1044 | case lineFeed = "\n" |
---|
1045 | case carriageReturn = "\r" |
---|
1046 | } |
---|
1047 | \end{swift} |
---|
1048 | Here, the raw values for an enumeration called ASCIIControlCharacter are defined to be of type Character, and are set to some of the more common ASCII control characters. |
---|
1049 | Character values are described in Strings and Characters. |
---|
1050 | |
---|
1051 | Raw values can be strings, characters, or any of the integer or floating-point number types. |
---|
1052 | Each raw value must be unique within its enumeration declaration. |
---|
1053 | |
---|
1054 | Note |
---|
1055 | |
---|
1056 | Raw values are not the same as associated values. |
---|
1057 | Raw values are set to prepopulated values when you first define the enumeration in your code, like the three ASCII codes above. |
---|
1058 | The raw value for a particular enumeration case is always the same. |
---|
1059 | Associated values are set when you create a new constant or variable based on one of the enumeration's cases, and can be different each time you do so. |
---|
1060 | Implicitly Assigned Raw Values |
---|
1061 | |
---|
1062 | When you're working with enumerations that store integer or string raw values, you don't have to explicitly assign a raw value for each case. |
---|
1063 | When you don't, Swift automatically assigns the values for you. |
---|
1064 | |
---|
1065 | For example, when integers are used for raw values, the implicit value for each case is one more than the previous case. |
---|
1066 | If the first case doesn't have a value set, its value is 0. |
---|
1067 | |
---|
1068 | The enumeration below is a refinement of the earlier Planet enumeration, with integer raw values to represent each planet's order from the sun: |
---|
1069 | |
---|
1070 | \begin{swift} |
---|
1071 | enum Planet: Int { |
---|
1072 | case mercury = 1, venus, earth, mars, jupiter, saturn, uranus, neptune |
---|
1073 | } |
---|
1074 | \end{swift} |
---|
1075 | In the example above, Planet.mercury has an explicit raw value of 1, Planet.venus has an implicit raw value of 2, and so on. |
---|
1076 | |
---|
1077 | When strings are used for raw values, the implicit value for each case is the text of that case's name. |
---|
1078 | |
---|
1079 | The enumeration below is a refinement of the earlier CompassPoint enumeration, with string raw values to represent each direction's name: |
---|
1080 | \begin{swift} |
---|
1081 | enum CompassPoint: String { |
---|
1082 | case north, south, east, west |
---|
1083 | } |
---|
1084 | \end{swift} |
---|
1085 | In the example above, CompassPoint.south has an implicit raw value of "south", and so on. |
---|
1086 | |
---|
1087 | You access the raw value of an enumeration case with its rawValue property: |
---|
1088 | \begin{swift} |
---|
1089 | let earthsOrder = Planet.earth.rawValue |
---|
1090 | // earthsOrder is 3 |
---|
1091 | |
---|
1092 | let sunsetDirection = CompassPoint.west.rawValue |
---|
1093 | // sunsetDirection is "west" |
---|
1094 | \end{swift} |
---|
1095 | |
---|
1096 | \paragraph{Initializing from a Raw Value} |
---|
1097 | |
---|
1098 | If you define an enumeration with a raw-value type, the enumeration automatically receives an initializer that takes a value of the raw value's type (as a parameter called rawValue) and returns either an enumeration case or nil. |
---|
1099 | You can use this initializer to try to create a new instance of the enumeration. |
---|
1100 | |
---|
1101 | This example identifies Uranus from its raw value of 7: |
---|
1102 | \begin{swift} |
---|
1103 | let possiblePlanet = Planet(rawValue: 7) |
---|
1104 | // possiblePlanet is of type Planet? and equals Planet.uranus |
---|
1105 | \end{swift} |
---|
1106 | Not all possible Int values will find a matching planet, however. |
---|
1107 | Because of this, the raw value initializer always returns an optional enumeration case. |
---|
1108 | In the example above, possiblePlanet is of type Planet?, or "optional Planet." |
---|
1109 | Note |
---|
1110 | |
---|
1111 | The raw value initializer is a failable initializer, because not every raw value will return an enumeration case. |
---|
1112 | For more information, see Failable Initializers. |
---|
1113 | |
---|
1114 | If you try to find a planet with a position of 11, the optional Planet value returned by the raw value initializer will be nil: |
---|
1115 | \begin{swift} |
---|
1116 | let positionToFind = 11 |
---|
1117 | if let somePlanet = Planet(rawValue: positionToFind) { |
---|
1118 | switch somePlanet { |
---|
1119 | case .earth: |
---|
1120 | print("Mostly harmless") |
---|
1121 | default: |
---|
1122 | print("Not a safe place for humans") |
---|
1123 | } |
---|
1124 | } else { |
---|
1125 | print("There isn't a planet at position \(positionToFind)") |
---|
1126 | } |
---|
1127 | // Prints "There isn't a planet at position 11" |
---|
1128 | \end{swift} |
---|
1129 | This example uses optional binding to try to access a planet with a raw value of 11. |
---|
1130 | The statement if let somePlanet = Planet(rawValue: 11) creates an optional Planet, and sets somePlanet to the value of that optional Planet if it can be retrieved. |
---|
1131 | In this case, it isn't possible to retrieve a planet with a position of 11, and so the else branch is executed instead. |
---|
1132 | |
---|
1133 | \paragraph{Recursive Enumerations} |
---|
1134 | |
---|
1135 | A recursive enumeration is an enumeration that has another instance of the enumeration as the associated value for one or more of the enumeration cases. |
---|
1136 | You indicate that an enumeration case is recursive by writing indirect before it, which tells the compiler to insert the necessary layer of indirection. |
---|
1137 | |
---|
1138 | For example, here is an enumeration that stores simple arithmetic expressions: |
---|
1139 | \begin{swift} |
---|
1140 | enum ArithmeticExpression { |
---|
1141 | case number(Int) |
---|
1142 | indirect case addition(ArithmeticExpression, ArithmeticExpression) |
---|
1143 | indirect case multiplication(ArithmeticExpression, ArithmeticExpression) |
---|
1144 | } |
---|
1145 | \end{swift} |
---|
1146 | You can also write indirect before the beginning of the enumeration to enable indirection for all of the enumeration's cases that have an associated value: |
---|
1147 | \begin{swift} |
---|
1148 | indirect enum ArithmeticExpression { |
---|
1149 | case number(Int) |
---|
1150 | case addition(ArithmeticExpression, ArithmeticExpression) |
---|
1151 | case multiplication(ArithmeticExpression, ArithmeticExpression) |
---|
1152 | } |
---|
1153 | \end{swift} |
---|
1154 | This enumeration can store three kinds of arithmetic expressions: a plain number, the addition of two expressions, and the multiplication of two expressions. |
---|
1155 | The addition and multiplication cases have associated values that are also arithmetic expressions -- these associated values make it possible to nest expressions. |
---|
1156 | For example, the expression (5 + 4) * 2 has a number on the right-hand side of the multiplication and another expression on the left-hand side of the multiplication. |
---|
1157 | Because the data is nested, the enumeration used to store the data also needs to support nesting -- this means the enumeration needs to be recursive. |
---|
1158 | The code below shows the ArithmeticExpression recursive enumeration being created for (5 + 4) * 2: |
---|
1159 | \begin{swift} |
---|
1160 | let five = ArithmeticExpression.number(5) |
---|
1161 | let four = ArithmeticExpression.number(4) |
---|
1162 | let sum = ArithmeticExpression.addition(five, four) |
---|
1163 | let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2)) |
---|
1164 | \end{swift} |
---|
1165 | A recursive function is a straightforward way to work with data that has a recursive structure. |
---|
1166 | For example, here's a function that evaluates an arithmetic expression: |
---|
1167 | \begin{swift} |
---|
1168 | func evaluate(_ expression: ArithmeticExpression) -> Int { |
---|
1169 | switch expression { |
---|
1170 | case let .number(value): |
---|
1171 | return value |
---|
1172 | case let .addition(left, right): |
---|
1173 | return evaluate(left) + evaluate(right) |
---|
1174 | case let .multiplication(left, right): |
---|
1175 | return evaluate(left) * evaluate(right) |
---|
1176 | } |
---|
1177 | } |
---|
1178 | |
---|
1179 | print(evaluate(product)) |
---|
1180 | // Prints "18" |
---|
1181 | \end{swift} |
---|
1182 | This function evaluates a plain number by simply returning the associated value. |
---|
1183 | It evaluates an addition or multiplication by evaluating the expression on the left-hand side, evaluating the expression on the right-hand side, and then adding them or multiplying them. |
---|
1184 | |
---|
1185 | |
---|
1186 | \section{Python} |
---|
1187 | |
---|
1188 | \section{Algebraic Data Type} |
---|