|
| Enumerations |
| The provision of enumerations is a big plus for Delphi. They make for readable and reliable code. An enumeration is simply a fixed range of named values. For example, the Boolean data type is itself an enumeration, with two possible values : True and False. If you try to assign a different value to a boolean variable, the code will not compile. | |
| Defining enumerations |
| When you want to use an enumeration variable, you must define the range of possible values in an enumeration type first (or use an existing enumeration type, such as boolean). Here is an example: | |
| type TSuit = (Hearts, Diamonds, Clubs, Spades); // Defines enumeration range var suit : TSuit; // Defines enumeration variable begin suit := Clubs; // Set to one of the values end;
|
|
| The TSuit type definition creates a new Delphi data type that we can use as a type for any new variable in our program. (If you define types that you will use many times, you can place them in a Unit file and refer to this in a uses statement in any program that wants to use them). We have defined an enumeration range of names that represent the suits of playing cards. | |
| We have also defined a suit variable of that TSuit type, and have assigned one of these values. Note that there are no quote marks around these enumeration values - they are not strings, and they take no storage. | |
| In fact, each of the enumeration values is equated with a number. The TSuit enumeration will have the following values assigned : | |
| Hearts = 0 , Diamonds = 1 , Clubs = 2 , Spades = 3 | |
| And you can use these values instead of the enumeration values, although this loses many of the benefits of enumerations. You can even override the default number assignments: | |
| type TSuit = (Hearts=13, Diamonds, Clubs=22, Spades); var suit : TSuit; begin suit := Clubs; end;
|
|
| The enumeration values assigned are now : | |
| Hearts = 13 , Diamonds = 14 , Clubs = 22 , Spades = 23 | |
| Using enumeration numbers |
| Since enumeration variables and values can also be treated as numbers (ordinals), we can use them in expressions : | |
| type TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun); // Enumeration values var today : TDay; weekend : Boolean; begin today := Wed; // Set today to be Wednesday if today > Fri // Ask if it is a weekend day then weekend := true else weekend := false; end;
|
|
| today is set to Wed which has ordinal value = 3 weekend is set to false since Wed (3) <= Fri (5)
|
|
| And we can also use them in loops (for more on looping, see the Looping tutorial). Here is an example : | |
| type TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun); // Enumeration values var day : TDay; // Enumeration variable begin for day := Mon to Fri do begin // day has each of the values Mon to Fri ( 1 to 5) in 5 iterations // of this loop, allowing you to whatever you want. end; end;
|
|
| In the loop above, the value of day can be treated as a number or an enumeration value. To treat as a number, you must use the Ord function. | |
| A word of warning |
| One word of warning : each of the values in an enumeration must be unique in a program. This restriction allows you to assign an enumeration value without having to qualify the type it is defined in. | |
| SubRanges |
| SubRange data types take a bit of getting used to, although they are simple in principle. With the standard ordinal (integer and character) types you are allowed a finite range of values. For example, for the byte type, this is 0 to 255. SubRanges allow you to define your own type with a reduced range of values. For example: | |
| type TSmallNum = 0..9; var smallNum : TSmallNum; begin smallNum := 5; // Allowed smallNum := 10; // Not allowed smallNum := -1; // Not allowed end;
|
|
| Delphi will not compile code that has assignments outside of the given range. | |
| You can also have subranges of characters: | |
| type TUpper = 'A'..'Z'; TLower = 'a'..'z'; TDigit = '0'..'9'; var upper : TUpper; lower : TLower; digit : TDigit; begin upper := 'G'; // Allowed lower := 'g'; // Allowed digit := '7'; // Allowed upper := 'g'; // Not allowed lower := '7'; // Not allowed digit := 4; // Not allowed end;
|
|
| Finally, we can even have a subrange of an enumeration, because enumerations are ordinal types: | |
| type TDay = (Mon=1, Tue, Wed, Thu, Fri, Sat, Sun); // Enumeration values TWeekDays = Mon..Fri; // Enumeration subranges TWeekend = Sat..Sun;
|
|
| Sets |
| What is a set? |
| Sets are another way in which Delphi is set apart from other languages. Whereas enumerations allow a variable to have one, and only one, value from a fixed number of values, sets allow you to have any combination of the given values - none, 1, some, or all. | |
| A set variable therefore holds a set of indicators. Up to 255 indicators. An indicator is set on when the variable has that value defined. This may be a bit tricky to understand, so here is an example: | |
| type TDigits = set of '1'..'9'; // Set of numeric digit characters var digits : TDigits; // Set variable myChar : char; begin // At the start, digits has all set values switched off // So let us switch some on. Notice how we can switch on single // values, and ranges, all in the one assignment: digits := ['2', '4'..'7']; // Now we can test to see what we have set on: for myChar := '1' to '9' do if myChar In digits then ShowMessageFmt('''%s'' is in digits',[myChar]) else ShowMessageFmt('''%s'' is not in digits',[myChar]) end;
|
|
| The In operator tests to see if a set contains a value. The ShowMessageFmt function used above displays data in a dialog box. Click on the In and ShowMessageFmt items in the code above to learn more. The data shown is as follows: '1' is not in digits '2' is in digits '3' is not in digits '4' is in digits '5' is in digits '6' is in digits '7' is in digits '8' is not in digits '9' is not in digits
|
|
| Including and excluding set values |
| Notice in the code above that we assigned (switched on) a set of values in a set variable. Delphi provides a couple of routines that allow you to include (switch on) or exclude (switch off) individual values without affecting other values: | |
| type // We define a set by type - bytes have the range : 0 to 255 TNums = set of Byte; var nums : TNums; begin nums := [20..50]; // Switch on a range of 31 values Include(nums, 12); // Switch on an additional value : 12 Exclude(nums, 35); // Switch off a value : 35 end;
|
|
| nums now has the following values set : 12 , 20..34 , 36..50
|
|
| Set operators |
| Just as with numbers, sets have primitive operators: | |
| + The union of two sets * The intersection of two sets - The difference of two sets = Tests for identical sets <> Tests for non-identical sets >= Is one set a subset of another <= Is one set a superset of another
|
|
| These operators give great flexibility in set handling: | |
| type TNums = set of 1..9; var nums1, nums2, nums3, nums4, nums5, nums6 : TNums; begin nums1 := [1,2,3]; nums2 := [1,2,4]; nums3 := [1,2,3,4,5,6,7,8,9]; nums4 := nums1 + nums2; // nums4 now [1,2,3,4] nums5 := nums1 * nums2; // nums5 now [1,2] nums6 := nums1 - nums2; // nums6 now [3] // Test for equality if nums1 = nums2 then ShowMessage('nums1 = nums2') else ShowMessage('nums1 <> nums2'); // Test for inequality if nums1 <> nums3 then ShowMessage('nums1 <> nums3') else ShowMessage('nums1 = nums3'); // Is nums1 a subset of nums3? if nums1 <= nums3 then ShowMessage('nums1 is a subset of nums3') else ShowMessage('nums1 is not a subset of nums3'); // Is nums1 a superset of nums3? if nums1 >= nums3 then ShowMessage('nums1 is a superset of nums3') else ShowMessage('nums1 is not a superset of nums3'); end;
|
|
| nums1 <> nums2 nums1 <> nums3 nums1 is a subset of nums3 nums1 is not a superset of nums3
|
|
| | | | |