手続きと関数

Object Pascalにおいて、手続き(プロシージャ)とは戻り値のないサブプログラム、つまりVBでいうSubプロシージャ、C/C++のvoid型関数のことを言い、関数とは戻り値のあるサブプログラム、つまりVBでいうFunctionプロシージャのことを言います。

手続きと関数
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

// 名前に応じたメッセージを作成する関数
function CreateMessage( name: string ) : string;
begin

    result := 'Hello, ' + name + '.';

end;

// 名前に応じてメッセージを表示するプロシージャ
procedure OutputMessage( name: string );
begin

    Writeln( CreateMessage( name ) );

end;

// メインプログラム
begin

    // メッセージを表示
    OutputMessage( 'santa marta' );
    OutputMessage( 'Guest' );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
Hello, santa marta.
Hello, Guest.
Press any key to continue

まず、全てのサブプログラムはメインプログラムの前に記述されている必要があります。 それ以外の部分、戻り値の型指定、パラメータの指定方法などは問題ないと思います。 ただ、関数で戻り値を返す場合は、暗黙の変数resultに戻り値を代入するため、returnなどは使用しません。 また、VBと同じように関数名に値を代入することで関数の戻り値とする事もできます。

このプログラムでは、メインプログラムからOutputMessageプロシージャを呼び出し、さらにそのOutputMessage プロシージャはCreateMessage関数を呼び出しています。 プロシージャ・関数と名称こそ異なれど、その概念は他の言語と変わりないと言えます。

ローカル変数とスコープ

サブプログラムの中でローカル変数を宣言、使用できます。 サブプログラム宣言の直後にvar文で変数を宣言します。 使用の際は他の変数と全く同じです。 また、スコープの概念も他の言語と同じで、サブプログラム内で宣言された変数はそのサブプログラムの中においてのみ有効です。

ローカル変数とスコープ
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var
    value: integer;


// ローカル変数valueの値を変更し、表示するプロシージャ
procedure SetNewValue( newValue: integer );
var
    value: integer;

begin
    value := newValue;

    Writeln( value );

end;


// 変数valueの値を表示するプロシージャ 
procedure OutputValue();
begin
    Writeln( value );

end;


// メインプログラム
begin

    value := 3;

    OutputValue;

    SetNewValue( 5 );

    OutputValue;

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
3
5
3
Press any key to continue

このように、ローカル変数と同じ名前の変数がメインプログラムで宣言されていても、明確に区別されます。

参照渡し、値渡し、定数パラメータ

Object Pascalのサブプログラムの引数は、VB.NET、C#、C/C++などと同様に既定で値渡しです。 参照渡しにする場合は var キーワードを付加します。 また、パラメータを定数としてサブプログラム内での変更を許可しない場合はconstをつけます。

参照渡し、値渡し、定数パラメータ
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var
    intValue: integer;


// 参照渡し
procedure CallByReference( var value: integer );
begin

    value := value + 1;

end;


// 値渡し
procedure CallByValue( value: integer );
begin

    value := value + 1;

end;


// 定数パラメータ
procedure ConstantParameter( const value: integer );
begin

    // エラー: 代入できない左辺値です
    // value := value + 1;

end;


// 現在の値を表示するプロシージャ
procedure OutputValue;
begin

    Writeln( 'intValue = ', intValue );

end;


// メインプログラム
begin

    intValue := 15;
    OutputValue;

    CallByReference( intValue );
    OutputValue;

    CallByValue( intValue );
    OutputValue;

    ConstantParameter( intValue );
    OutputValue;

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
intValue = 15
intValue = 16
intValue = 16
intValue = 16
Press any key to continue

実行結果のとおり、値渡しで渡されたパラメータの値は、サブプログラム内で変更することができます。 また、定数パラメータに対して値を代入しようとするコードは、コンパイル時にエラーとなります。

定数

定数はconst文で宣言します。 Delphiでは他の言語同様、定数の名前は全て大文字にすることが多いようです。

定数
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

const
    CONSTANT_VALUE = 5;
    ENDOFPROGRAM_MESSAGE = 'Press any key to continue';

var
    variableValue: integer;

begin

    variableValue := CONSTANT_VALUE;

    Writeln( 'variableValue = ', CONSTANT_VALUE );

    // 終了メッセージを表示
    Write( ENDOFPROGRAM_MESSAGE );
    Readln;

end.
実行結果
variableValue = 5
Press any key to continue

また、当然ですが、定数への値の代入は許可されません。

列挙型

列挙型の概念も他の言語と変わりませんが、その動作は多少異なります。

列挙型
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    Musumes = ( Aibon = 0,
                Nono,
                Rikacchi,
                Konkon,
                Marippe );

var

    intValue: integer;
    musume: Musumes;
    musumeName: array[Musumes] of string = ('加護亜依',
                                            '辻希美',
                                            '石川梨華',
                                            '紺野あさ美',
                                            '矢口真里' );

begin

    // 'Integer' と 'Musumes' には互換性がありません
    // intValue := Aibon;

    // 'Musumes' と 'Integer' には互換性がありません
    // musume := 3;


    // 列挙型の値の代入
    musume := Rikacchi;

    // 各列挙型メンバの値
    Writeln( 'Order of Aibon: ',   Ord(Aibon) );
    Writeln( 'Order of Marippe: ', Ord(Marippe) );
    Writeln( 'Order of musume: ',  Ord(musume) );

    Writeln;

    // 列挙型の使用方法
    Writeln( musumeName[Musumes(0)] );
    Writeln( musumeName[Nono] );
    Writeln( musumeName[musume] );

    Writeln;

    musume := Musumes(4);

    // 現在のmusumeの値 
    Writeln( 'musume = ', Ord(musume) );

    // musumeの値をディクリメント
    Dec(musume);
    Write( musumeName[musume] ); Writeln( ', musume = ', Ord(musume) );

    // musumeの値をインクリメント
    Inc(musume);
    Write( musumeName[musume] ); Writeln( ', musume = ', Ord(musume) );

    // Nonoの一つ前の値を取得
    Writeln( musumeName[Pred(Nono)] );

    // Nonoの一つ前の、一つ後の値を取得
    Writeln( musumeName[Succ(Pred(Nono))] );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
Order of Aibon: 0
Order of Marippe: 4
Order of musume: 2

加護亜依
辻希美
石川梨華

musume = 4
紺野あさ美, musume = 3
矢口真里, musume = 4
加護亜依
辻希美
Press any key to continue

この結果の通り、他の言語とは異なり列挙型はその他の数値型と互換性がないというのが最大の特徴といえます。 その他の宣言、使用法などはほとんど同じです。 ただ、直接数値型として扱うことができないので、インクリメント・ディクリメントなどを行うサポート関数がいくつか存在します。

文字列型配列の宣言時、添え字の部分に直接列挙型を指定していますが、こうすることにより、配列の上限と下限が自動的に列挙型の上限と下限に指定されます。 しかし、こうするとIntegerなどの整数値で添え字を指定することができなくなります。 また、整数値で添え字の範囲を指定して配列を宣言すると、添え字に列挙型を直接指定する事はできなくなります。

文字列操作

Object Pascalでの文字列の動作は、.NET Frameworkの文字列型と非常に似た動作をします。

文字列操作
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var

    s1, s2, s3: string;
    s         : string;
    i, l      : integer;

begin

    s1 := 'モー';
    s2 := 'ニング';
    s3 := '娘。';

    // 連結
    s := s1 + s3;
    Write( s, 'は' );

    // 挿入 (s2をsの5バイト目に)
    Insert( s2, s, 5 );
    Writeln( s, 'の略です。' );

    // コピー
    s1 := Copy( s, 0, Length(s) );

    // 削除 ( sの5バイト目からs2の長さ分を )
    Delete( s, 5, Length(s2) );

    Writeln( s1, 'は', s, 'と略されます。' );

    // 代入
    s := s1;

    // インデックスを取得
    Writeln( '文字「', s3, '」は', s, 'の',
             Pos( s3, s ), 'バイト目にあります。' );

    // 空文字を代入
    s3 := '';

    // インデクサ的アクセス
    i := 1;
    l := Length(s);

    while ( i + 1 <= l ) do
    begin
        s3 := s[i] + s[i+1];

        Writeln( s3 );

        i := i + 2;
    end;

    // 0番目の要素は参照できません
    //   - LengthまたはSetLengthを使って下さい
    //    Writeln( s[0] );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
モー娘。はモーニング娘。の略です。
モーニング娘。はモー娘。と略されます。
文字「娘。」はモーニング娘。の11バイト目にあります。
モ
ー
ニ
ン
グ
娘
。
Press any key to continue

このようにかなり柔軟な文字列操作を行うことができます。 Delete、Insert、Lengthなどがインスタンスメソッドではなくサポート関数として存在することを除いては、先程述べたようにほとんど.NET Frameworkの文字列型と同じといっていいと思います。 しかし、一つだけ違う点を上げるとすれば、文字列型に対する添え字の下限は0ではなく1ということです。 0番目の要素にアクセス使用とするとエラーになります。 これには深い意味があるようですが、それはこの次で考察します。

ShortString, AnsiString, WideString

stringとはAnsiStringのエイリアスです。 Object Pascalの文字列型にはShortString, AnsiString, WideStringの三種類があります。 また、先程述べたインデックス0の意味も調べてみます。 この例ではインデックス0を参照するために変数の値をインデックスに指定しますが、直接0を指定しようとするとエラーになります。

ShortString
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var

    ss: ShortString;
    sa: AnsiString;
    sw: WideString;
    i : integer;
    ls, la, lw: integer;

begin

    // 10バイトの文字列を代入
    ss := '0123456789';
    sa := ss;
    sw := sa;

    // ss(ShortString)を一バイトずつ表示
    Write( 'ss: ' );

    ls := Length( ss );
    for i := 1 to ls do
        Write( ss[i] );

    Writeln;

    // sa(AnsiString)を一文字(一バイト)ずつ表示
    Write( 'sa: ' );

    la := Length( sa );
    for i := 1 to la do
        Write( sa[i] );

    Writeln;

    // sw(WideString)を一文字ずつ表示
    Write( 'sw: ' );

    lw := Length( sw );
    for i := 1 to lw do
        Write( sw[i] );

    Writeln;

    // 長さと0番目の要素を文字ではなく数値として表示
    i := 0;
    Writeln( 'ss[0] is ', Ord( ss[i] ), ', Length of ss is ', ls );
    Writeln( 'sa[0] is ', Ord( sa[i] ), ', Length of sa is ', la );
    Writeln( 'sw[0] is ', Ord( sw[i] ), ', Length of sw is ', lw );

    // AnsiStringとWideStringの違い
    sa := '全角文字';
    sw := sa;

    la := Length( sa );
    lw := Length( sw );

    Writeln( sa, ' Length: ', la );
    Writeln( sw, ' Length: ', lw );

    // sw(WideString)を一文字ずつ表示
    for i := 1 to lw do
        Writeln( sw[i] );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
ss: 0123456789
sa: 0123456789
sw: 0123456789
ss[0] is 10, Length of ss is 10
sa[0] is 0, Length of sa is 10
sw[0] is 0, Length of sw is 10
全角文字 Length: 8
全角文字 Length: 4
全
角
文
字
Press any key to continue

まず、三種類の文字列型の意味を説明すると、ShortStringは以前のPascalで用いられていた文字列型との互換性のために残された短い文字列型と呼ばれるもので、静的に256バイトが割り当てられ0バイト目(Object Pascalの用語では第一バイト)に文字列全体の長さが格納され、残りの255バイトに実際の文字列が格納されます。 これはC/C++などのnull で終わるchar型配列とは異なる文字列型の実装方法です。 しかし、実際に文字列を用いる場合は255バイトでは不足するおそれがあります。

そこで新たに用意されたのが、長い文字列と呼ばれるもので、これにはANSI文字を格納するAnsiStringとUnicode文字を扱うWideStringがあり、それぞれ2ギガ・バイトまでの文字列を扱うことができます。 これらの型は4バイトのメモリを占有するポインタ型で、文字列の終端にはヌルが付加されます。 先頭の4バイトには文字列の長さを保持するための2バイトと、残り2バイト分には参照カウンタが含まれます。

また、どの文字列型もインデックスは1から始まり、互換性の問題からShortStringのインデックス0が長さを表すのに対して、 AnsiStringとWideStringではインデックス0は意味を成しません。 また、これらの文字列型の一文字を表す型にChar, AnsiChar, WideCharがあります。 CharはAnsiCharと同義です。 実行結果から分かるとおり、WideString型の一つのインデックスの表す文字は2バイト(実際にはWideChar型)となります。 また、長さもバイト数ではなく文字数で返されます。

集合型 その1

集合型はC/C++, VBなどの言語にはない、Object Pascalの特徴的な型です。 その名の通り、ある要素の集合を扱います。

集合型 その1
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // エラー: 集合型は256個以下の要素しかもてません
    // IntegerValues = set of 0..1000;

    // エラー: 集合型は256個以下の要素しかもてません
    // IntegerValues = set of integer;

    // Byte型の集合
    // Byte型はちょうど0から255の256個
    ByteValues = set of Byte;

    // AからZの集合
    LargeLetters = set of 'A'..'Z';

var
    AToN   : LargeLetters;
    OToZ   : LargeLetters;
    Decades: ByteValues;

// AからNの集合に含まれるかをチェックするプロシージャ
procedure IsAToN( letter: Char );
begin

    Write( letter, 'はAからNに' );

    if letter in AToN then
        Writeln( '含まれます' )
    else
        Writeln( '含まれません' );

end;

// OからZの集合に含まれるかをチェックするプロシージャ
procedure IsOToZ( letter: Char );
begin

    Write( letter, 'はOからZに' );

    if letter in OToZ then
        Writeln( '含まれます' )
    else
        Writeln( '含まれません' );

end;

// 1から10の値かをチェックするプロシージャ
procedure IsDecades( value: Byte );
begin

    Write( value, 'は1から10の値' );

    if value in Decades then
        Writeln( 'です' )
    else
        Writeln( 'ではありません' );

end;

// メインプログラム
begin

    // AからNの大文字アルファベットを表す集合
    AToN := ['A'..'N'];

    // OからZの大文字アルファベットを表す集合
    OToZ := ['O'..'Z'];

    // 1から10までの整数の集合
    Decades := [1..10];

    // その値が集合に含まれるかチェック
    IsAToN( 'K' );
    IsOToZ( 'A' );

    IsAToN( 'i' );
    IsOToZ( 'r' );

    IsDecades( 6 );
    IsDecades( 0 );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
KはAからZに含まれます
AはOからZに含まれません
iはAからNに含まれません
rはOからZに含まれません
6は1から10の値です
0は1から10の値ではありません
Press any key to continue

まず、集合型を宣言する際の注意点として、集合の要素は256個を越えてはいけないと言うことです。 例えば、Integer型の値全てを扱うような集合は要素数が256を越えるので宣言できませんが、Byte型であれば0から255までの256個の値しか持ち得ないので、集合型として利用できます。 これは型から直接集合型を宣言する場合ですが、特定の値の範囲を用いて集合型を宣言することができます。 ただ、いずれの場合も「set of」で何の集合であるかを指定します。

また、宣言した集合型は、変数として宣言・使用されます。 宣言された集合型の変数に集合に含まれる値を指定します。 空集合を作る際には[]を代入します。 そして、ある集合の中に特定の要素が含まれているかを調べるには in 演算子を用います。 この演算子の結果はBoolean型ですが、一般的に考えて in 演算子はif文で用いることが多いでしょう。

この例では集合型の機能のうち、ほんの一部しか使っていません。 これ以外の機能はこれ以降で引き続き説明していきます。

集合型 その2

集合型ではその包括関係を調べることもできます。

集合型 その2
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // byte型の集合
    SetOfByte = set of byte;

var
    AllValues: SetOfByte;
    Values1:   SetOfByte;
    Values2:   SetOfByte;
    Values3:   SetOfByte;
    Values4:   SetOfByte;

// メインプログラム
begin

    // 1〜20までの数全体を表す集合
    AllValues := [1..20];

    // 素数
    Values1 := [2, 3, 5, 7, 11, 13, 17, 19];

    // 2の倍数
    Values2 := [2, 4, 6, 8, 10, 12, 14, 16, 18, 20];

    // 奇数
    Values3 := [1, 3, 5, 7, 9, 11, 13, 15, 17, 19];

    // 4の倍数
    Values4 := [4, 8, 12, 20];


    Writeln( '素数は奇数の部分集合か ', Values1 >= Values3 );

    Writeln( '4の倍数は2の倍数の部分集合か ', Values2 >= Values4 );

    Writeln( '素数と2の倍数とは等しいか ', Values1 = Values2 );

    Writeln( '奇数と2の倍数とは等しくないか ', Values3 <> Values2 );

    Writeln( '奇数と2の倍数の補集合とは等しいか ', Values3 = ( AllValues - Values2 ) );


    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
素数は奇数の部分集合か FALSE
4の倍数は2の倍数の部分集合か TRUE
素数と2の倍数とは等しいか FALSE
奇数と2の倍数とは等しくないか TRUE
奇数と2の倍数の補集合とは等しいか TRUE
Press any key to continue

この例にあるように、>=, <=, =, <> などの各演算子を用いて集合同士の包括関係などを知ることができます。 この例では補集合を求めるために全集合からある集合を「減算」していますが、これについては次の説で説明します。

集合型 その3

先ほど少し触れたように、集合型では集合同士の和・積・差を求めることもできます。

集合型 その3
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // 列挙型
    Musumes = ( Iida, Abe, Yasuda, Yaguchi,
                Kago, Tsuji, Ishikawa, Yoshizawa,
                Konno, Takahashi, Ogawa, Niigaki );

    // 娘。の集合型
    SetOfMusume = set of Musumes;

var
    Fullnames: array[Musumes] of string = ( '飯田圭織', '安倍なつみ', '保田圭', '矢口真里',
                                            '加護亜依', '辻希美', '石川梨華', '吉澤ひとみ',
                                            '紺野あさ美', '高橋愛', '小川麻琴', '新垣里沙' );
    Musume:      SetOfMusume;
    Minimoni:    SetOfMusume;
    Petitmoni:   SetOfMusume;
    Tanpopo:     SetOfMusume;


// 集合にある要素を列挙するプロシージャ
procedure EnumerateMember( member: SetOfMusume );
var
    m: Musumes;
    c: integer;

begin

    c := 0;

    for m := Low(Musumes) to High(Musumes) do
    begin
        if m in member then
        begin
            Write( Fullnames[m], ', ' );
            c := c + 1;
        end;
    end;

    Writeln( '以上', c, '名' );
    
end;


// メインプログラム
begin

    // 娘。全体を表す集合
    Musume := [Low(Musumes)..High(Musumes)];

    // ミニモニ。 (新旧含めて)
    Minimoni := [Yaguchi, Kago, Tsuji, Takahashi];

    // プッチモニ。
    Petitmoni := [Yasuda, Yoshizawa, Ogawa];

    // タンポポ
    Tanpopo := [Iida, Yaguchi, Ishikawa, Kago, Konno, Niigaki];


    // ミニモニ。とタンポポだったメンバー
    EnumerateMember( Minimoni * Tanpopo );

    // ミニモニ。かプッチモニだったメンバー
    EnumerateMember( Minimoni + Petitmoni );

    // タンポポ以外のメンバー
    EnumerateMember( Musume - Tanpopo );

    // いずれのユニットにも含まれなかったメンバー
    EnumerateMember( Musume - ( Minimoni + Petitmoni + Tanpopo ) );


    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
矢口真里, 加護亜依, 以上2名
保田圭, 矢口真里, 加護亜依, 辻希美, 吉澤ひとみ, 高橋愛, 小川麻琴, 以上7名
安倍なつみ, 保田圭, 辻希美, 吉澤ひとみ, 高橋愛, 小川麻琴, 以上6名
安倍なつみ, 以上1名
Press any key to continue

このようにいとも簡単に集合の和・差・積を求めることができます。 当然のことですが、これらの演算は同じ型の集合型同士でしか演算できません。

最後におまけのサンプルを載せておきます。

集合型 おまけ
// ド・モルガンの定理が成り立っているかを証明する

{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // byte型の集合
    SetOfByte = set of byte;

var
    AllValues:   SetOfByte;
    Values1:     SetOfByte;
    Values2:     SetOfByte;
    CompValues1: SetOfByte;
    CompValues2: SetOfByte;

// メインプログラム
begin

    // 1〜20までの数全体を表す集合
    AllValues := [1..20];

    // 素数
    Values1 := [2, 3, 5, 7, 11, 13, 17, 19];

    // 適当
    Values2 := [1, 5, 6, 7, 9, 12, 14, 15, 17];


    // Values1 の補集合
    CompValues1 := AllValues - Values1;

    // Values2 の補集合
    CompValues2 := AllValues - Values2;


    // ド・モルガンの定理の証明
    Write( 'ド・モルガンの定理は ' );

    if ( CompValues1 * CompValues2 ) = ( AllValues - ( Values1 + Values2 ) ) then
        Writeln( '成り立っている。' )
    else
        Writeln( '成り立っていない。' );


    Write( 'ド・モルガンの定理は ' );

    if ( CompValues1 + CompValues2 ) = ( AllValues - ( Values1 * Values2 ) ) then
        Writeln( '成り立っている。' )
    else
        Writeln( '成り立っていない。' );


    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
ド・モルガンの定理は 成り立っている。
ド・モルガンの定理は 成り立っている。
Press any key to continue

レコード型

レコード型は他の言語で言う構造体に相当するデータ構造です。 宣言方法は列挙型や集合型のようにtype文で「識別子 = record ... end;」として、record と end; の間でメンバの宣言を行います。

レコード型
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // レコード型Musumeの宣言
    Musume = record
        Name: string;
        Age:  integer;
        BirthDate: TDateTime;
    end;

var
    aibon:  Musume;
    konkon: Musume;

// 現在の年齢を計算する関数
function CalcAge( birthDate: TDateTime ) : Word;
var
    age: Word;
    by, bm, bd: Word;
    ny, nm, nd: Word;
    
begin

    DecodeDate( birthDate, by, bm, bd );
    DecodeDate( Now, ny, nm, nd );

    age := ny - by;

    if ( bm > nm ) or ( ( bm = nm ) and ( bd > nd ) ) then
        age := age -1;

    result := age;
    
end;

// Musume型の値を表示するプロシージャ
procedure DisplayMusume( m: Musume );
var
    by, bm, bd: Word;

begin

    DecodeDate( m.BirthDate, by, bm, bd );

    Write( m.Name );
    Write( ' ', by, '年', bm, '月', bd, '日生まれ、' );
    Writeln( m.Age, '歳' );
    
end;

// メインプログラム
begin

    aibon.Name := '加護亜依';
    aibon.BirthDate := EncodeDate( 1988, 2, 7 );
    aibon.Age := CalcAge( aibon.BirthDate );

    with konkon do
    begin
        Name := '紺野あさ美';
        BirthDate := EncodeDate( 1987, 5, 7);
        Age := CalcAge( BirthDate );
    end;

    DisplayMusume( aibon );
    DisplayMusume( konkon );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
加護亜依 1988年2月7日生まれ、15歳
紺野あさ美 1987年5月7日生まれ、15歳
Press any key to continue

レコード型変数の使い方は、C/C++やVBの構造体の使い方と全く同じです。 さらに、Object Pascalではwith文がサポートされています。 ただ、これはVBと違って各メンバにアクセスする際には「.(ドット)」をつけてはいけません。

また、本題とは関係ないのですが、TDateTime型は日付を扱うためのデータ型です(実際にはDouble型のエイリアス)。  EncodeDate, DecodeDateプロシージャは年月日の各数値からTDateTimeの値を設定・取得するためのプロシージャです。 また、Now関数は現在の時刻を取得するための関数です。

あと、全く問題ないと思いますが、一応参考までに述べておくとレコード型は値型です。

レコード型は値型か
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

type
    // レコード型Musumeの宣言
    Musume = record
        { 省略 }
    end;

var
    aibon:  Musume;
    member: Musume;

// 現在の年齢を計算する関数
function CalcAge( birthDate: TDateTime ) : Word;
{ 省略 }

// Musume型の値を表示するプロシージャ
procedure DisplayMusume( m: Musume );
{ 省略 }

// メインプログラム
begin

    aibon.Name := '加護亜依';
    aibon.BirthDate := EncodeDate( 1988, 2, 7 );
    aibon.Age := CalcAge( aibon.BirthDate );

    member := aibon;

    member.Name := 'あいぼん';

    DisplayMusume( aibon );
    DisplayMusume( member );

    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end
実行結果
加護亜依 1988年2月7日生まれ、15歳
あいぼん 1988年2月7日生まれ、15歳
Press any key to continue

ポインタ型

Object Pascalにはポインタ型が存在します。 機能や動作はC/C++のポインタと同様です。

ポインタ型
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var
    // integer型変数
    x, y, t: integer;

    // integerのポインタ型変数
    px, py: ^integer;

begin

    // 変数に値を代入
    x := 3;
    y := 2;

    // 変数のアドレスを取得
    px := @x;
    py := @y;

    // ポインタから値を取得
    t   := px^;
    px^ := py^;
    py^ := t;

    // 値を表示
    Writeln( 'x = ', x );
    Writeln( 'y = ', y );
                      
    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
x = 2
y = 3
Press any key to continue

ありきたりな例ですが、ポインタ型を使用した例です。 まず、ポインタ型変数を宣言する場合には ^ 演算子を用いて「^型」とします。 そして、そのポインタ型変数に代入するための変数のアドレスを取得するには、「@変数」とします。 さらに、ポインタ型変数の指し示すアドレスにある値を取得するには「ポインタ変数^」とします。 宣言時と値の取得時に ^ 演算子をつける位置が異なることに注意して下さい。

また、文字型のポインタであるPCharは、ポインタ値の加減算や等価・非等価の評価も行えます。 しかし、これ以外のポインタ型ではC/C++のようにポインタ値の加減算を行うことはできません。 次の例はPChar型を利用したものです。

PCharの加減算と評価
{$APPTYPE CONSOLE}

program LearningDelphi;

uses
    SysUtils;

var
    // 文字列
    s: string;

    // カウンタ用
    i: integer;

    // 文字型のポインタ型変数
    p, pt: PChar;

begin

    // 値を代入
    s := 'This is a sample of pointer.';

    // アドレスを取得
    p := @s[9];
    pt := p;

    Writeln( 'p^ = ', p^ );

    // ポインタ値を減算
    p := p - 3;

    Writeln( 'p^ = ', p^ );

    // ポインタ値を加算
    p := p + 1;

    Writeln( 'p^ = ', p^ );

    // ポインタ値は等しいか
    Writeln( 'p = pt is ', p = pt );

    // ポインタ自体の値
    Writeln( 'p = ', p );


    // 終了メッセージを表示
    Write( 'Press any key to continue' );
    Readln;

end.
実行結果
p^ = a
p^ = i
p^ = s
p = pt is FALSE
p = s a sample of pointer.
Press any key to continue