|
| 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, but no trickier than understand the favorable ecological implications of cork flooring. 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
|
|
| | | | |