cli  
Article Series

This article series discuss more than 30 different programming languages. Please read overview before you read any of the details.

Playing with Records Related Articles.

Where to Discuss?

Local Group

Preface

Goal: A practical case to collect unique record fields using Free Pascal.

From Delphi to Retirement

Story of my life.

I have been using Pascal since a was a teenager, starting from Turbo Pascal, and then continue learning. For a while I use Pascal for many project, from data acquisition, to multhreaded accounting.

It has been real fun helping other jedi. I know insight most people don’t know. Then I suddenly stop using Delphi in 2007, and decide to move completely to open source.

I refused the idea of using unlicensed stuff. I decide to make myself clean. So I migrate from windows to linux, from photoshop to inkscape, from desktop coder to web developer.

Even with my expertise, it is simply hard to come back to Pascal. I knew about Kylix project, but never even try it. I tried Lazarus many times, but never have time to get into it. All I can do is watch form a far. I completely retired form Pascal.

Ressurection

14 years gap.

Times goes by, after 14 years, I decide to relearn Free Pascal, for nostalgic reason.

I choose free pascal, among other Pascal dialect. So here I am a Pascal veteran, starting from scratch.

Difference with Other Language.

No functional stuff.

It is a little lower level than other language, so the code is longer than usual.

To solve a particular issuse, you can have at least two approach, whether pure free pascal, or the object pascal one.

You can even use visual component library, along with the good Lazarus IDE, But this is beyond our topic.

Reference Reading

Source Examples

You can obtain source examples here:


Common Use Case

Task: Get the unique tag string

Please read overview for more detail.

Prepopulated Data

Songs and Poetry

Prepare the data type, for building the data structure.

{$mode objfpc}{$H+}

type TSong = record
  Title : string;
  Tags  : array of string;
end;

Then, fill the data itself, as below:

const
  Songs : array of TSong =
  ( ( Title : 'Cantaloupe Island';
      Tags  : ('60s', 'jazz') ),
    ( Title : 'Let It Be';
      Tags  : ('60s', 'rock') ),
    ( Title : 'Knockin'' on Heaven''s Door';
      Tags  : ('70s', 'rock') ),
    ( Title : 'Emotion';
      Tags  : ('70s', 'pop') ),
    ( Title : 'The River';
      Tags  : () )
  );

Pascal Solution

The Answer

As said, you can use either pure free pascal, or the object pascal one. In object oriented manners, you this could be as long function below:

function TSongList3.GetUniqueTags(): TTags;
var
  SL : TStringList;
  ASong  : PSong;
  Tag : ANSIString;
begin
  SL := TStringList.Create;
  SL.Duplicates := dupIgnore;
  SL.Sorted := true;

  for ASong in Self do
    for Tag in ASong^.Tags do
      SL.Add(Tag);

  Result := SL.ToStringArray(0, Count-1);

  SL.Free;
end;

Enough with introduction, at this point we should go straight to coding.

Environment

Instead of using FP IDE or Lazarus, You can use any IDE, such as geany or ViM.

No need any special setup. Just run and voila..!

But later we are going to learn, on how to configure fpc.cfg anyway.

Compiled

Free Pascal is a compiled language. We have to compile the code first, before we run the code.

❯ fpc 01-tags-a.pp
Free Pascal Compiler version 3.2.2 [2022/03/02] for x86_64
Copyright (c) 1993-2021 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling 01-tags-a.pp
Linking 01-tags-a
11 lines compiled, 0.7 sec

Pascal: Free Pascal Compiler


1: Array of String

Different choice for different situation.

Before building a records, I begin with simple array of string. But beware of the differences.

Evolution

Pascal originally has static array, but now has dynamic array.

Pascal originally has string type, but now has ANSIString type. So now the string type may refer to, either ANSIString and ShortString, depends on the {$H} switch.

Now that we can have array of string, do not forget that Object Pascal has TSTringList class.

To make this even more complex, Pascal is more like a low level language, compared to python and friend. There is now way to easily dump the variable. We have to do it ourself.

To make this easy to comprehend. Le’t go straight to example code.

Static Array of Short String

Without any {$H} switch, the default is the original short string.

const
  Tags : array [0..4] of string =
    ('rock', 'jazz', 'rock', 'pop', 'pop');
var
  I: integer;

begin
  for I := 0 to 4 do
    Write(Tags[I] + ' ');
  WriteLn();
end.

With the result similar as below:

❯ ./01-tags-a
rock jazz rock pop pop 

Pascal: Static Array of Short String

Dynamic Array of ANSI String

Instead of fixed size array, we can use dynamic array.

With ANSI string, We can utilize Join function from SysUtils unit. This way, we can dump array in pretty format.

uses SysUtils;

const
  Tags : array of ANSIString =
    ('rock', 'jazz', 'rock', 'pop', 'pop');

begin 
  WriteLn(ANSIString.Join(', ', Tags));
end.

With the result similar as below:

❯ ./01-tags-b
rock, jazz, rock, pop, pop

Pascal: Dynamic Array of ANSI String

Local Directives

We can make our life easier by using local directives. The detail for long string directive is in official documentation.

Instead of ANSIString.Join(), we can utilize any varibale name such as s.Join().

{$mode objfpc}
{$H+}
uses SysUtils;

const
  Tags : array of string =
    ('rock', 'jazz', 'rock', 'pop', 'pop');
var
  S: string = '';

begin 
  WriteLn(S.Join(', ', Tags));
end.

With the result similar as below:

❯ ./01-tags-c
rock, jazz, rock, pop, pop

Pascal: Local Directives

Split and Join

We can also use Split function to initialize the array.

uses Classes, SysUtils;

var
  S, T : ANSIString;
  Tags : array of ANSIString;
begin
  S := 'rock:jazz:rock:pop:pop';
  Tags := S.Split(':');
  WriteLn(T.Join(', ', Tags) + '.');
end.

With the result similar as below:

❯ ./01-tags-d
rock, jazz, rock, pop, pop.

Pascal: Split and Join

Beware of the unitialized T variable. I intentionally use this T variable, So you know what the problem is.

❯ fpc 01-tags-d.pp
Compiling 01-tags-d.pp
01-tags-d.pp(9,11) Warning: Variable "T" of a managed type does not seem to be initialized
Linking 01-tags-d
11 lines compiled, 0.5 sec
1 warning(s) issued

Pascal: Uninitialized Variable Warning

You can omit using T, by using S.Join, or even ANSIString.Join, or if you wish use {$H+}switch, then usestring.Join directly.

Manual Dump Function

For more complex data structure, we might need to make our own variable dump. So prepare before things began to be more complex. Consider start from simple array as below:

function DumpArray(Tags: array of string): string;
var
  Index, Last: integer;
  S: string = '';
begin
  Last := length(Tags)-1;
  for Index := 0 to Last do
  begin
    S := S + Tags[Index];
    if Index<>Last then
      S := S + ', '
    else 
      S := S + '.';
  end;
  DumpArray := S;
end;

Pascal: Manual Dump Function: Routine

Now we can call the function directly, in main program entry point.

const
  Tags : array of string =
    ('rock', 'jazz', 'rock', 'pop', 'pop');

begin
  WriteLn(DumpArray(Tags));
end.

With the result similar as below:

❯ ./01-tags-e
rock, jazz, rock, pop, pop.

Pascal: Manual Dump Function: Main

Notice the dot at the end of the output.

Object Pascal: TStringList (alt 1)

Instead of pure free pascal, we can also take advantage of object pascal. The object to handle this is called TStringList.

This one below is intentionally, a pretty bad example of using TStringList.

Prepare the header: uses, const, and variable definition.

uses Classes, SysUtils;

const
  S : ANSIString = 'rock:jazz:rock:pop:pop';

var
  S1 : TStrings;
  S2, S3 : ANSIString;
  SA : TStringArray;

Pascal: Object Pascal: TStringList: Header

Then program entry point.

begin
  SA := S.Split(':');

  S1 := TStringList.Create;  
  S1.AddStrings(SA);

  S2 := S1.GetText();
  S3 := StringReplace(s2,
    sLineBreak, ':',[rfReplaceAll]);
  WriteLn(S3);

  S1.Free;
end.

With the result similar as below:

❯ ./01-tags-f
rock:jazz:rock:pop:pop:

Pascal: Object Pascal: TStringList: Main

Object Pascal: TStringList (alt 2)

We are going to use TStringList in a more proper fashioned, by using its own method to solve our problem.

Prepare the header: uses, const and variable definition.

uses Classes, SysUtils;

const
  S : ANSIString = 'rock:jazz:rock:pop:pop';

var
  SL : TStrings;
  SA : TStringArray;

Pascal: Object Pascal: TStringList: Header

Then program entry point.

begin
  SL := TStringList.Create;  
  SL.AddStrings(S.Split(':'));

  SA := SL.ToStringArray(0, 4);
  WriteLn(AnsiString.Join(', ', SA) + '.');

  SL.Free;
end.

With the result similar as below:

❯ ./01-tags-g
rock, jazz, rock, pop, pop.

Pascal: Object Pascal: TStringList: Main

This is, I believe, easier to be read.


What is Next 🤔?

We are going to move on to record structure topic.

Consider continue reading [ Pascal - Playing with Records - Part Two ].