Learn Object Pascal
Part 8 - Custom datatypes : Enumerations, subranges and sets
In the section before we have seen how to create "virtual" custom datatypes that are "smaller" than an already known datatype.In this and in the next tutorials we will see "bigger" data types using multiple already known basic datatypes.
A new datatype must declared after of the uses of our unit.
The keyword type is used in order to define our new datatype.
Once defined the new datatype we can use it by defining a variable for this new datatype.
For new datatypes is good practice to be named like TName_Of_New_Datatype, in our case TDay.
Let's take a look how we can define TDay and how we can declare a new variable of this new data type.
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
...
type
TDay = ( dSunday, dMonday, dTuesday, dWednesday, dThursday, dFriday, dSaturday );
var
Form1: TForm1;
...
implementation
...
procedure TForm1.Button1Click(Sender: TObject);
Var
Day : TDay; //A day has type of TDay
AnotherDay : TDay; //Another day of type TDay
begin
Day := dMonday; // Set the day
...
The first "bigger" datatype that we will discuss is the enumeration datatypes. Enumerations is a ordered list of fixed values.
Notice that the enumeration values can also be treated as numbers (ordinals).
These values can not be changes on run time. Think of them like constant list.
All of the values of an enumeration usually have a common characteristic.
It is a good practice to add a prefix at the values of each enumeration type.
Example : TKnownColor = ( kcRed, kcGreen, kcBlue, kcWhite, kcBlack );
This will help you to remember that the value kcWhite is contained in the enumeration TKnownColor
Below we can see how we can define enumerations :
type
TDay = ( dSunday, dMonday, dTuesday, dWednesday, dThursday, dFriday, dSaturday );
// Enumeration values
TKnownColor = ( kcRed, kcGreen, kcBlue, kcWhite, kcBlack );
TSize = ( sSmall, sMedium, sBig )
Let's now define and use some enumerations. Since an enumeration is a ordered list we can use operators like > < = or loops
Ord(Value) : Gives the index(integer) of the element
Succ(Value) : Go to the next element after value
Pred(Value) : Go to the previous element after value
Inc(Value,X) : move X elements forward from the value
Dec(Value,X) : move X elements backward from the value
Low(Value) get first (lowest) element
High(Value) get last (highest) element
In order to set a day we can write :
Day := dMonday; // Set the day
Ord(Day) : Gives the index(integer) of the element so we need to use IntToStr in order to print the index.
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 1 (start from 0)
If we want to get the value of the element (day) then we have to use the
GetEnumName included at typinfo. GetEnumName needs as input a TypeInfo and the index value (ordered value) in order to return the name of the value as a string.
TDay type is a TDay type but we can treat it as TypeInfo by using a so called casting method : TypeInfo(TDay).
If you do not understand this function do not panic, for now :
Memo1.Lines.Add( GetEnumName(TypeInfo(TDay), Ord(Day)) );
prints out the name of the "indexted" day.
This is not the point of this tutorial.
So let's move on. More about casting in later tutorials.
Since the enumeration values of TDay are ordinal we can use functions line Inc, Dec etc ...
Also we can use loops like for, while ...
In the example below you should not have any problems in order to find out what is going on :
uses
...
typinfo; //added by us in order to use GetEnumName
{Enumeration types :
An enumeration is a fixed range of named values.
The enumeration values can also be treated as numbers (ordinals).
The enumeration values can NOT be changed at runtime }
type
//TDay = a new data type !
TDay = ( dSunday, dMonday, dTuesday, dWednesday, dThursday, dFriday, dSaturday );
// Enumeration values
//remember the first day of the week is Sunday and the last is Saturday
//Enumeration values (like Monday) can also be treated as numbers (ordinals)
TKnownColor = ( kcRed, kcGreen, kcBlue, kcWhite, kcBlack );
//Enumeration values for TKnownColor enumeration type
procedure TForm1.Button1Click(Sender: TObject);
Var
//enumeration vars :
Day : TDay; //A day has type of TDay
begin
Day := dMonday; // Set the day
//Ord(Day) : Gives the index(integer) of the element
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 1 (start from 0)
//Get the value of the day
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)));
//Another way to get the vaue of the day
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), GetEnumValue(TypeInfo(TDay),'Monday')));
/////////////////////////////////////////////
//Succ-Pred
Day := Succ(Day); // Set the next day
//Pred(Day) goes one day back
//Ord(Day) : Gives the index of the element
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 2
//Get the value of the day
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)));
/////////////////////////////////////////
//Inc-Dec
Inc(Day,2); // move two days after
//Dec(Day,3) //goes three days back
//Ord(Day) : Gives the index of the element
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 4
//Get the value of the day
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)));
///////////////////////////////////////
//another way to set the day :
Day := TDay(5);
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 5
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day))); //Friday
////////////////////////////////////////
//set the lowest day
//Low-High
Day := TDay(Low(Day));
//Day := TDay(High(Day));
Memo1.Lines.Add(IntToStr(Ord(Day))); //prints 0
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)));
///////////////////////////////////////////////
//Loops in enumerated types
//print all working days
for Day:=dMonday to dFriday do begin
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)) + ': is a working day');
end;
//print weekend
for Day in TDay do begin
if (Day = dSunday) or (Day = dSaturday) then begin
Memo1.Lines.Add(GetEnumName(TypeInfo(TDay), Ord(Day)) + ': is a weekend day');
end;
end;
end;
Notice how works the "for Day in TDay" loop.
Subrange types allow you to define your own char, integer or any enumerated (ordinal) type with a reduced range of values.
Subranges are also good defining repetitive ordinal lists like alphabet letters or increased integers.
We could have defined TCharLower like this (simple enumerated list)
type
TCharLower = (a,b,c,d,e,...); //not good practice
But we can use subranges
type
TCharUpper = 'A' .. 'Z'; //subranges using chars.
//better practice !
Like in enumarations once defined TCharUpper for example we can use it by defining a local variable like this : ACharSmall : TCharLower;
Subranges can also re-use preveusly fefined enumerated values :
Ex : TWeekDays = dMonday .. dFriday; //Enumeration subrange
An example of subranges can be found below :
{Subranges types :
SubRanges allow you to define your own char, integer or
any enumerated (ordinal) type with a reduced range of values.}
type
TCharUpper = 'A' .. 'Z'; //subranges using chars.
TCharLower = 'a' .. 'z'; //subranges using chars.
TOneDigit = 0 .. 9; //subranges using integer.
TTwoDigit = 0 .. 99;
TThreeDigit = 0 .. 999;
//subranges using floats are not possible
TWeekDays = dMonday .. dFriday; //Enumeration subrange
procedure TForm1.Button1Click(Sender: TObject);
Var
//subrange vars :
ACharSmall : TCharLower;
ThreeDigitNumber : TThreeDigit;
AWeekDay : TWeekDays;
begin
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
//subrange
ACharSmall:='x';
ThreeDigitNumber:=456;
AWeekDay:=dWednesday;
Memo1.Lines.Add(ACharSmall + '=' + IntToStr(ThreeDigitNumber) +
' Index no. of Wednesday: ' + IntToStr(Ord(AWeekDay)));
end;
Sets are dynamic enumeration groups.
Can be changed on runtime unlike the enumerated types.
See sets as groups of same type values that it is possible to add or remove values from them.
Sets can be defined directly using an already basic data type :
TSetBytes = set of Byte; //set of bytes
or by defining a subrage :
TSetOneDigit = set of 0 .. 9;
of by using an already defined subrange :
TSetTwoDigitSet = set of TTwoDigit;
or by using an already defined enumeration :
TSetKnownColor = set of TKnownColor;
We can modify a set on runtime using the functions :
Include and Exclude
We can do logical group operations like :
union(+ A ∪ B) difference(- A\B) and intersection(* A ∩ B)
Also we can :
check if a group is superset(>=) or subset(<=)
Equality(=) or not equality (<>)
See a self explanatory example below :
{Set types :
Sets are dynamic enumeration groups. Can be changed on runtime}
type
TSetBytes = set of Byte; //set of bytes
TSetOneDigit = set of 0 .. 9; // set defined directly
TSetTwoDigitSet = set of TTwoDigit; //set with subrange defined before
TSetCharsFromBtoG = set of 'b' .. 'g'; //set of chars defined directly
TSetKnownColor = set of TKnownColor; //set with enumeration defined before
procedure TForm1.Button1Click(Sender: TObject);
Var
//set vars :
SetBytes : TSetBytes;
OneByte : Byte;
SetOneDigit : TSetOneDigit;
SetCharsFromBtoG : TSetCharsFromBtoG;
OneChar : Char;
KnownColor : TKnownColor;
SetKnownColor : TSetKnownColor;
SetRGBColor : TSetKnownColor;
SetBWColor : TSetKnownColor;
begin
//////////////////////////////////////////////
//////////////////////////////////////////////
//////////////////////////////////////////////
//set
//a set can be inizialized like this :
SetBytes:=[1,2,3]; //set inizialized with 1,2,3
//and can be changed like this :
Include(SetBytes,4);//incude at set the value 4
Exclude(SetBytes,1);//exclude from set value 1
for OneByte in SetBytes do begin
Memo1.Lines.Add('byte :' + IntToStr(OneByte) + ' is included in SetBytes');
end;
//another example using one digit values
SetOneDigit:=[2,3,4..7];//same as [2,3,4,5,6,7]
Exclude(SetOneDigit, 5);
Include(SetOneDigit,1);
Include(SetOneDigit,8);
Include(SetOneDigit,9);
for OneByte in SetOneDigit do begin
Memo1.Lines.Add('byte :' + IntToStr(OneByte) + ' is included in SetOneDigit');
end;
//another example with chars
SetCharsFromBtoG:=['b','c'..'e']; //['b','c','d','e']
Include(SetCharsFromBtoG,'f');
Include(SetCharsFromBtoG,'z'); //this char can not be included
for OneChar in SetCharsFromBtoG do begin
Memo1.Lines.Add('char :' + OneChar + ' is included in SetCharsFromBtoG');
end;
//set example using known colors enumeration
SetKnownColor:=[kcRed, kcGreen, kcWhite, kcBlack];
Include(SetKnownColor,kcBlue);
Exclude(SetKnownColor,kcGreen);
for KnownColor in SetKnownColor do begin
Memo1.Lines.Add('know color :' +
GetEnumName(TypeInfo(TKnownColor ), Ord(KnownColor)) + ' is included in SetKnownColor');
end;
Memo1.Lines.Add('----------------');
//Set operators examples :
SetKnownColor := []; //empty
SetRGBColor := [kcRed, kcGreen, kcBlue];
SetBWColor := [kcWhite, kcBlack];
//Union +
//all known colors are the fundamental RGB colors + black and white
SetKnownColor := SetRGBColor + SetBWColor; //union of sets
for KnownColor in SetKnownColor do begin
Memo1.Lines.Add('know color :' +
GetEnumName(TypeInfo(TKnownColor ), Ord(KnownColor)) + ' is included in SetKnownColor');
end;
//Difference -
Memo1.Lines.Add('------Remove Black and White color------');
SetKnownColor := SetKnownColor - SetBWColor; //Difference of sets
for KnownColor in SetKnownColor do begin
Memo1.Lines.Add('know color :' +
GetEnumName(TypeInfo(TKnownColor ), Ord(KnownColor)) + ' is included in SetKnownColor');
end;
//Intersection *
Memo1.Lines.Add('------Intersection------');
Exclude(SetKnownColor,kcBlue);
Include(SetKnownColor,kcBlack); //Now SetKnownColor has
//kcRed, kcGreen, kcBlack
SetKnownColor := SetKnownColor * SetRGBColor; //intersection
//kcRed, kcGreen, kcBlack U kcRed, kcGreen Blue = kcRed, kcGreen
for KnownColor in SetKnownColor do begin
Memo1.Lines.Add('know color :' +
GetEnumName(TypeInfo(TKnownColor ), Ord(KnownColor)) + ' is included in SetKnownColor');
end;
//superset >=
SetKnownColor := [kcRed, kcGreen, kcBlue, kcWhite, kcBlack];
if SetKnownColor >= SetBWColor then begin
Memo1.Lines.Add('SetKnownColor is a superset of SetBWColor');
end;
//subset <=
if SetBWColor <= SetKnownColor then begin
Memo1.Lines.Add('SetBWColor is a subset of SetKnownColor');
end;
//different <>
if SetKnownColor <> SetBWColor then begin
Memo1.Lines.Add('SetKnownColor is different from SetBWColor');
end;
//equal =
SetKnownColor := [kcBlue, kcRed, kcGreen]; //the order is indifferent
//SetRGBColor := [kcRed, kcGreen, kcBlue]; these two sets are equal !
if SetKnownColor = SetRGBColor then begin
Memo1.Lines.Add('SetKnownColor and SetKnownColor are equal');
end;
end;
You can download the program of this section from here That's all for now In the next tutorial we will talk about Records.
Part 1: Introduction - Part 2: Hello World - Part 3: Data types, constants and variables - Part 4: Operators - Part 5: Decisions and Loops - Part 6: Procedures and Functions - Part 7: Custom datatypes - [[ Part 8: Enumerations, subranges and sets ]] - Part 9: Records