One of the Delphi language features not found in other modern languages is the notion of sets.
Delphi's set type is a collection of values of the same ordinal type.
A set is defined using the set of keyword:
Set types are usually defined with subranges.
type TMagicNumber = 1..34; TMagicSet = set of TMagicNumber; var emptyMagicSet : TMagicSet; oneMagicSet : TMagicSet; anotherMagicSet : TMagicSet; begin emptyMagicSet := ; oneMagicSet := [1, 18, 24]; anotherMagicSet := [2, 5, 19]; if 1 in oneMagicSet then ShowMessage('1 is magic, part of oneMagicSet') ; end;
In the above example, the TMagicNumber is a custom subrange type allowing variables of the TMagicNumber type to receive values from 1 to 34. Simply put, a subrange type represents a subset of the values in another ordinal type.
Possible values of the set type are all the subsets of the base type, including the empty set.
A limitation on sets is that they can hold up to 255 elements.
In the above example, the TMagicSet set type is a set of TMagicNumber elements - integer numbers from 1 to 34.
The declaration TMagicSet = set of TMagicNumber is equal to the following declaration: TMagicSet = set of 1..34.
Set type variablesIn the above example, the variables emptyMagicSet, oneMagicSet and anotherMagicSet are sets of TMagicNumber.
To assign a value to a set type variable, use the square brackets and list all the elements of the set. As in:
oneMagicSet := [1, 18, 24];
Note 1: every set type variable can hold the empty set, denoted by .
Note 2: the order of the elements in a set has no meaning, nor is it meaningful for an element (value) to be included twice in a set.
The IN keywordTo test if an element is included in the set (variable) use the IN keyword:
if 1 in oneMagicSet then ...
Set OperatorsThe same way you can sum two numbers, you can have a set that is the sum of two sets. With sets you event have more operators:
- + returns the union of two sets.
- - returns the difference of two sets.
- * returns the intersection of two sets.
- = return true if two sets are equal - have the same elemement.
- <= returns true if the first set is a subset of the second set.
- >= returns true if the first set is a superset of the second set.
- <> returns true if two sets are non-identical.
- IN returns true if an element is included in the set.
Will the ShowMessage procedure be executed? If so, what will be displayed?
emptyMagicSet := oneMagicSet + anotherMagicSet; emptyMagicSet := emptyMagicSet - ; emptyMagicSet := emptyMagicSet + [5,10]; if emptyMagicSet = [2,5,10,18,19,24] then begin emptyMagicSet := emptyMagicSet * oneMagicSet; ShowMessage(DisplayElements(emptyMagicSet)) ; end;
Here's the implementation of the DisplayElements function:
Hint: yes. Displayed: "18 | 24 |".
function DisplayElements(magicSet : TMagicSet) : string; var element : TMagicNumber; begin for element in magicSet do result := result + IntToStr(element) + ' | '; end;
Integers, Characters, BooleansOf course, when creating set types you are not restricted to integer values. Delphi ordinal types include character and boolean values.
To prevent users to type alpha keys, add this line in the OnKeyPress of an edit control:
if Key in ['a'..'z'] + ['A'..'Z'] then Key := #0
Sets with EnumerationsA commonly used scenario in Delphi code is to mix both enumerated types and set types.
Here's an example:
type TWorkDay = (Monday, Tuesday, Wednesday, Thursday, Friday) ; TDaySet = set of TWorkDay; var days : TDaySet; begin days := [Monday, Friday]; days := days + [Tuesday, Thursday] - [Friday]; if Wednesday IN days then ShowMessage('I love Wednesday!') ;
Question: will the message be displayed? Answer: no :(
Sets in Delphi Control PropertiesWhen you need to apply "bold" to the font used in TEdit controls, you either use the Object Inspector or the following code:
The Font's Style property is a set type property! Here's how it is defined:
Font.Style := Font.Style + [fsBold];
So, an enumerated type TFontStyle is used as the base type for the set type TFontStyles. The Style property of the TFont class is of type TFontStyles - therefore a set type property.
type TFontStyle = (fsBold, fsItalic, fsUnderline, fsStrikeOut) ; TFontStyles = set of TFontStyle; ... property Style: TFontStyles ...
Another example includes the result of the MessageDlg function. A MessageDlg function is used to bring up a message box and obtain the user's response. One of the parameters of the function is the Buttons parameter of type TMsgDlgButtons.
TMsgDlgButtons is defined as a set of (mbYes, mbNo, mbOK, mbCancel, mbAbort, mbRetry, mbIgnore, mbAll, mbNoToAll, mbYesToAll, mbHelp).
If you display a message to the user containing Yes, OK and Cancel buttons and you want to execute some code if either the Yes or Ok buttons were clicked you can use the next code:
if MessageDlg('Learning about Sets!', mtInformation, [mbYes, mbOk, mbCancel], 0) in [mrYes, mrOK] then ...
Final word: sets are great. Sets might appear confusing to a Delphi beginner, but as soon as you start using set type variables you will find out they provide much more then it sounded in the the beginning. At least I have :))