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


Copyright © TrustFm.net 1998-2024 - Made by TrustFm - All Rights Reserved Worldwide