#Delphi Starter Edition チュートリアルシリーズ シーズン2 第5回 「配列 と レコード」
2017年1月23日より 「Delphi / C++Builder Starter チュートリアルシリーズ」 シーズン2、全9回、3月27日まで、毎週月曜日、Delphiパートが 17時00分~17時20分、 リアルタイム放送スペシャルコンテンツが5分~10分、C++Builderパートが 17時30分~17時50分の時間割でお送りしています。(今回、第5回はDelphiの部の補講があり、18時30分終了となります)
無料でダウンロード & 利用できる開発環境のDelphi / C++Builder Starter エディションを使用して、プログラミング言語のDelphi (Object Pascal ), C++の基礎を学ぶオンラインセッションです。
##使用したスライドなどの情報を掲載します。
###Webセミナースライド
第5回 「配列 と レコード」のスライドは下記アドレスよりご参照いただけます。
###アジェンダ
- 今日のねらい
- 配列とは何か、またその使い方を知る
- レコードとは何か、またその使い方を知る
- 実施内容
- 配列の定義、宣言、使用例
- 静的・動的配列の違いと参照型について学ぶ
- レコードの定義、宣言、使用例
- 補講
- ポインタについて学ぶ
- メソッド付きレコードの定義、宣言、使用例
- ついでにバリアントを少々
###開発環境インストール
Delphi / C++Builder Starter チュートリアルシリーズ シーズン1 第1回 ‟無料で始めよう アプリ作成„をご参考になり、開発環境をインストールしてください。
##以下、Delphi Webセミナーの本筋
###「配列」データ型
- 配列データ型とは? どんなもので、何に使えるのか?
- データ型をリスト化 (連続した列として取り扱える)
- 「インデックス」と呼ばれるものを使用して配列内の一つを指定してアクセス
- 多次元配列も可能
例:Integer
型の : 変数識別子iDate
の配列の図
識別子と インデックス |
iDate[1] | iDate[2] | iDate[3] | iDate[4] | iDate[5] | iDate[6] | iDate[7] |
---|---|---|---|---|---|---|---|
データ | 28 | 26 | 23 | 30 | 32 | 31 | 26 |
-
配列の種類には「静的配列」と「動的配列」
- 静的配列: type内の定義やvar内の変数宣言時に配列数を決めている配列
- 動的配列: 定義や宣言時には長さを決めず、実装時に配列の長さを指定
-
Low
関数,High
関数 ,Length
関数,Copy
関数が有効に使える
###静的配列の定義と変数宣言方法
- 静的配列の定義
-
type
ブロックで配列データ型となる識別子と、どんなデータ型の配列(array
)であるか宣言 - 配列数が
type
定義内でインデックスとして指定されるため、配列の数をあらかじめ知ることができる
-
//静的データ配列の定義文法
type //typeブロック
配列データ型定義識別子 = array [インデックス(部分範囲指定で配列数指定)] of データ型;
//静的データ配列の定義コード例
type
TDayTemperaturs = array [1..24] of Integer; //[a..x]なども範囲指定で使用可能
配列データを実際に使用する前の配列変数宣言
var //静的データ配列定義を使用する際の変数宣言文法
データ配列変数識別子 : 定義した配列データ型識別子;
var //静的データ配列定義を使用する際のコード例
myDayTemp1 : TDayTemperaturs;
###静的配列のアクセスと使用例
- データ配列変数を宣言後…
-
[ ]
でくくったインデックスを変数に付加してアクセス - インデックスはType定義の時に設定した部分範囲の値で指定
-
TDayTemperaturs=array[1..31] of Integer;
なら[1]~[31]がインデックス
//どこかのprocedure 内 (一か月の気温を保持する配列例)
var //静的データ配列定義を使用する際のコード例
myDayTemp1 : TDayTemperaturs; //配列変数識別子 : データ配列定義識別子
begin
myDayTemp[1] := 30; //並列変数識別子 [インデックス番号] でアクセス
myDayTemp[2] := 32;
//省略…
myDayTemp[24] := 29;
//以下はダメな例
{ myDayTemp[32] := 28; //これはインデックスの範囲外なのでコンパイル時にエラーとなる }
end;
###動的配列の定義と変数宣言方法
- 動的配列の定義
-
type
ブロックで配列データ型となる識別子と、どんなデータ型の配列(array
)であるか宣言 -
type
ブロック内の定義ではインデックス数を指定しない
-
//動的データ配列の定義文法
type //typeブロック
配列データ型識別子 = array of データ型; //配列のインデックスを指定しない
//動的データ配列の定義コード例
type
TDayTemperaturs = array of Integer;
配列データを実際に使用する前の変数宣言
var //動的データ配列定義を使用する際の変数宣言文法
データ配列変数識別子 : 定義した配列データ型;
var //動的データ配列定義を使用する際のコード例
myDayTemp1 : TDayTemperaturs;
###動的配列のアクセスと使用例
- データ配列変数を宣言後…
-
SetLength(配列変数, 配列数)
を指定して配列の長さ決定 - インデックスは 0 から 配列の長さ -1 の値となる
-
SetLength(myDayTemp1,28);
なら[0]~[27]がインデックスとなる
-
//どこかのprocedure 内
var //動的データ配列定義を使用する際のコード例
myDayTemp1 : TDayTemperaturs;
I : Integer;
begin //SetLength (動的配列変数識別子, セットしたい長さ);
SetLength(myDayTemp1,28); //長さを28に設定。動的配列のインデックスは[0]~[27]となる
for I := Low(myDayTemp1) to High(myDayTemp1) do //配列にはLow, Highはとても有効
myDayTemp1[I] := [I]; //配列変数識別子 [インデックス]でアクセス
end;
###静的配列と動的配列の変数識別子の扱いの違い
-
静的配列と動的配列で異なる配列変数識別子の意味と扱われ方
- 静的配列の変数の識別子は配列に入っている値の変数そのもの(いままでの 変数の扱いと同じ)
- 動的配列の変数の識別子は、その配列のデータが入っているポインタを(入れ物の場所・アドレス)示している**「参照型」**。
-
配列変数識別子に[インデックス]を付加して要素にアクセスした場合には、動的・静的配列変数識別しでも配列に入っている値にアクセスできる
###参照型とは?
###続・参照型
###続々・参照型
###関数・手続きの引数(パラメータ)として配列を使用
- 配列の数を指定せずにルーチンのパラメータとして使用可能(オープン配列パラメータと呼ばれる)
- 動的配列変数の参照を変えないように パラメータに
const
キーワードを付けて宣言 - ルーチンのパラメータに
const
キーワードを付加することにより変更不可となる - ルーチン呼び出し時に自由に配列数を決定できる
function Goukei(const Ai: array of Integer): Integer; //オープン配列をパラメータに設定
var
I : Integer;
begin
Result := 0;
for I := Low(Ai) to High(Ai) do //引数として配列数が未定なのでLow/Highを使用しておく
Result := Result + Ai[I]; //配列のすべてを合計する処理
end;
//オープン配列パラメータを持つルーチンの使用例
Goukei([10, Y, 27*I]); //[]でくくることにより配列扱いとなる (Y,IはInteger型の変数)
Goukei(di); //動的配列変数を引数とすることもできる(di は array of Integer の動的配列変数)
###「レコード」データ型
- 複数のデータ型の集めて、データの型として再定義したもの
-
type
ブロックで定義してvar
で識別子を宣言してから使用 - 定義したレコードをさらに配列として定義することもできる
//レコード定義の構文
type
レコードデータ型定義識別子 = record //レコードデータ型識別子を定義
レコード内に保持するデータ識別子1: データ型; //レコード内のフィールド定義(保持データ)
レコード内に保持するデータ識別子2: データ型;
レコード内に保持するデータ識別子3: データ型;
end; //レコード定義はend; で閉じる
//レコード定義のコード例
type
TMyDate= record //TMyDate というレコードのデータ型識別子定義
Year: Integer; //Yearというフィールド名とそのデータ型(Integer)を定義
Month: Byte; //Monthというフィールド名とそのデータ型(Byte)を定義
Day: Byte; //Dayというフィールド名とそのデータ型(Byte)を定義
end;
###「レコード」データ型 の使用例
- 定義したレコード使用する場合のコード例
- レコード変数名.フィールド識別子で各フィールドにアクセス
- 代入演算子で同じレコードデータ型の値をコピーできる(参照型ではなく、値がコピーされる)
//レコードデータ型の変数宣言
var
Birthday, ADay : TMyDate; //TMyDateレコードデータ型の変数識別子 Birthday, Adayを宣言
//レコードデータ変数の使用例
begin
Birthday.Year := 1995; //レコード変数.(ドット)フィールド名でアクセス可能
Birthday.Month := 2;
Birthday.Day := 14;
Aday := BirthDay; //レコードを代入演算子で代入すると、値がそのままコピーされる
Aday.Year := 2017;
ShowMessage(Birthday.Year.ToString); //”1995”が表示される – Adayの変更に影響されない
ShowMessage(Aday.Year.ToString); //”2017”が表示される
end;
###レコード配列の使用
- 定義したレコードを配列として使用可能
- 動的配列、静的配列いずれも可能
- 参照型、値型の扱いは配列データ型と同じ
//レコードデータ型の配列変数識別子宣
type
TListMyDate = array of TMyDate; //TMyDateレコードデータ型の(動的)配列を定義
var
ListMyDate : TListMyDate; //レコードデータ型の(動的)配列の変数識別子を宣言
{typeブロックで定義をせずにvarブロックで ListMyDate: array of TMyDate と宣言してもOK }
//レコード配列変数の使用例(動的配列の使用例)
begin
SetLength(ListMyDate,3); //SetLengthで3つの配列をセット(Indexは[0]~[2])
ListMyDate[0].Year := 1995; //使用法は配列と同じインデックスで配列の要素にアクセス
ListMyDate[0].Month := 2; //インデックス後にフィールド名を指定して各フィールドにアクセス
ListMyDate[0].Day := 14;
end;
####配列、レコードにまつわる機能について参考となるリンク
-
多次元配列
-
レコードとwith文
-
レコードのコンストラクタ
-
可変レコード
-
http://docwiki.embarcadero.com/RADStudio/Berlin/ja/構造化型(Delphi
-
ポインタ型変数に対するNew Desposeでの動的割り当てと破棄
-
演算子のオーバーロード
-
バリアント型諸々
##補講
ここから下は初級者編から少しレベルアップした内容です。
###補講で使用しているサンプルコード
サンプルコードを実際に見れるようにgithubにアップロードしています。
上の図を使って、静的配列変数識別子と動的配列変数識別子のコピー動作(代入演算子:=
を使った代入)の違いを説明しましたが、実際にこのコピー動作を行うサンプルコードは以下のようになります。
var
DoutekiArray, DoutekiArrayCopy: array of Integer; //動的配列変数宣言
SeitekiArray, SeitekiArrayCopy: Array [0 .. 1] of Integer; //静的配列変数宣言
I: Integer;
begin
SetLength(DoutekiArray, 2); //動的配列の元となるものにSetLengthで実領域確保
//動的配列の元、静的配列の元にそれぞれインデックスと同じ数値を入れておく
for I := Low(DoutekiArray) to High(DoutekiArray) do
DoutekiArray[I] := I;
for I := Low(SeitekiArray) to High(SeitekiArray) do
SeitekiArray[I] := I;
//動的配列と静的配列をコピー先の変数に代入
DoutekiArrayCopy := DoutekiArray;
SeitekiArrayCopy := SeitekiArray;
//コピー先の配列変数のインデックス[0]の値を変更
SeitekiArrayCopy[0] := 100;
DoutekiArrayCopy[0] := 100;
//コピー元、コピー先の配列変数のインデックス[0]が示す値を確認
Show('動的配列元[0]:' + DoutekiArray[0].ToString + '/ 動的配列コピー[0]:' +
DoutekiArrayCopy[0].ToString);
Show('静的配列元[0]:' + SeitekiArray[0].ToString + ' / 静的配列コピー[0]:' +
SeitekiArrayCopy[0].ToString);
end;
###ポインタ(ポインタ型)
- データが入っている場所(アドレス)情報が「ポインタ」
- ポインタ「型」は、そのポインタ情報を保持するデータ型
- 通常の値を扱う変数も、そのデータ型のアドレスを扱うためのポインタ型がある
- ポインタ型を定義、宣言するにはそのデータ型の先頭に ^ を付ける
- 変数識別子のポインタ(アドレス)は先頭に@を付けるとそのアドレスを意味する
var
IPointer: ^Integer; //Integer型のポインタを保持する変数識別子を宣言
MyInt: Integer; //Integer型の変数識別子を宣言
begin
MyInt := 100; //Integer型変数に100を代入
//変数識別子の先頭に@をつけることで、その変数のポインタを意味する
IPointer := @MyInt; //Integer型のポインタ変数に、Integer型変数のポインタを代入
//ポインタを保持する変数識別子の後ろに ^ をつけることで実際の値の場所を意味する
IPointer^ := 20; //ポインタ変数が参照している実データの場所に 値20を代入
ShowMessage(MyInt.ToString); //メッセージとして 20 が表示される
end;
###動的配列の操作
- 動的配列変数に対して
SetLength
を使わず直接代入で配列設定可能 -
+
演算子を使って 動的配列の連結が可能 -
Insert
,Delete
関数で挿入、削除が可能 - 動的配列の値をコピーしたい場合には
Copy
var
di, diCopy: array of Integer; //動的配列変数 di の宣言
begin
di := [1, 2, 3]; //動的配列変数 diへの直接代入(Index 0~2の順に数値 1,2,3代入)
di := di + di; //di に di を連結(配列の長さは6となり、中身は1,2,3,1,2,3)
di := di + [4, 5]; //di の配列の後部に さらに2つ直接代入(中身は1,2,3,1,2,3,4,5)
Insert([8, 9], di, 3);//diのIndex[3]に8,9を代入追加(中身は1,2,3,8,9,1,2,3,4,5)
Delete(di, 0, 5); //diのIndex[0]から、配列を5つ削除(1,2,3,8,9,1,2,3,4,5)
diCopy := Copy(di,low(di),Length(di));//Copy(動的配列変数,開始インデックス,コピー長さ)
//diCopyの中身が、1,2,3,4,5となる
end;
ルーチンのパラメータに型可変オープン配列パラメータを使用
-
array of const
をパラメータの型として設定することにより、配列のデータ型を指定せずルーチンを宣言、実装できる -
array of const
の引数は `TVerRecというレコード型の配列として受け渡される -
TVerRec
はデータ型情報を持った「レコード」なので、これでどんなデータ型か判断して処理する
function Goukei(const Args: array of const): Extended; //オープン配列をパラメータに設定
var
I : Integer;
begin
Result := 0;
for I := Low(Args) to High(Args) do //引数として配列数が未定なのでLow/Highを使用しておく
case Args[I].Vtype of
vtInteger: //Integer型の時の処理…
vtChar: //Char型の時の処理…
//省略
end;
-
参考 TVerRecレコード: http://docwiki.embarcadero.com/Libraries/Berlin/ja/System.TVarRec
-
型可変オープン配列パラメータ: http://docwiki.embarcadero.com/RADStudio/Berlin/ja/パラメータ(Delphi)
###メソッド付きレコード
レコード内に紐付けされたfunction
と procedure
を持てる
レコードに紐付けられたルーチンは「メソッド」と呼ばれる
レコード内のフィールドに他ユニットからのアクセス制限を付加
他ユニットからアクセスできないようにするにはprivate
キーワード
他ユニットからアクセスさせる場合にはpublic
キーワード (キーワードを付けていなければ、public
として扱われる)
// メソッド付き レコード定義の構文
type //Typeブロック内にて定義
レコード方定義識別子 = record
private //他unitからのアクセスを制限するキーワード private
フィールド識別子定義: データ型定義; //これらフィールドアは他unitからアクセスできない
//…
procedure ルーチン識別子定義(パラメータ:データ型); //このprocedure も他unitからアクセスできない
public //外部unitから使用させる場合のキーワード public
// type定義内ではメソッドの識別子定義のみ.メソッドの実装はimplementationブロックで行う
Procedure ルーチン識別子定義(パラメータ:データ型); //このprocedure は外部からアクセス可能
function ルーチン識別子定義(パラメータ:データ型): 戻り値データ型; //外部からアクセス可能
end;
###メソッド付きレコード定義の例
- メソッド付きレコード定義の例
- レコード定義内のメソッドは定義のみ。実装は
implementation
ブロックで行う
type
TMyDate = record //TMyDateというレコード識別子の定義
private //他unitからのアクセスを制限するキーワード private
Year: Integer; //内部データ保持用のフィールド の定義。外部unitからのアクセスを制限
Month: Integer;
Day: Integer;
myDateTime: TDateTime;
procedure SetDatetime; //内部処理用のprocedureの宣言外部unitからのアクセスを制限
public //外部unitから使用させる場合のキーワード public
// type定義内ではメソッドの識別子定義のみ.メソッドの実装はimplementationブロックで行う
procedure SetValue(Y, M, D: Integer); //レコードの値セット用のProcedure識別子定義
function GetValue: TDateTime; //レコードの値取得用のfunction識別子定義
end;
###メソッド付きレコード実装と使用の例
implementation //implementation ブロック
{$R *.fmx}
procedure TMyDate.SetDatetime; //レコード定義識別子.(ドット)メソッド定義識別子の形式で実装。TMyDataレコード定義のSetDateimeメソッド実装
begin
//フィールドのYear, Month, Day データを myDateTime(TDateTime型)にエンコードして代入
myDateTime := EncodeDate(Year, Month, Day); //メソッド実装内ではレコードの定義識別子なしでフィールドを使用可能
end;
procedure TMyDate.SetValue(Y, M, D: Integer);//TMyDateレコードのSetValueメソッドの実装
begin
Year := Y; //SetValueの引数として渡されたデータをレコード内のフィールドに代入
Month := M; //メソッド実装内ではレコードの定義識別子なしでフィールド名を使用可能
Day := D;
SetDatetime; //privateキーワードで宣言されていた内部メソッドを呼び出し。メソッド実装内ではレコード定義識別子なしでメソッドを使用可能
end;
function TMyDate.GetValue: TDateTime; //TMyDateレコードのGetValueメソッドの実装
begin
Result := myDatetime; //myDateTimeフィールドを戻り値として戻しているだけ。メソッド実装内ではレコード定義識別子成してフィールドを使用可能
end;
procedure TForm1.Button1Click(Sender: TObject); //メソッド付きレコードの使用例。ここではボタンのクリック時に使用している
var
MyBirthday: TMyDate; //上記で定義、実装しているメソッド付きTMyDataレコード型の変数識別子MyBirthdayを宣言
begin
MyBirthday.SetValue(1995, 2, 14); //メソッド使用時はレコード変数識別子.メソッド名 で使用。 ShowMessage(DateTimeToStr(MyBirthday.GetValue)); //メソッド使用時は レコード変数識別子.メソッド名で使用。
###バリアント
強い型付け言語のdelphi (Object Pascal)にも型なしデータ型があります
- 型なし
Variant
- 様々な型の値を代入できるデータ型
- 実行時変換が妥当な場合には、変換を自動で行う。変換できない場合には「例 外」が発生する
- 実行速度は遅い
var
V: Variant; //バリアント型変数識別子 V の宣言
Str : string; //文字列型変数識別子 Strの宣言
begin
V:=10; //値は整数の10としてバリアント変数Vに代入される
V:='Hello'; //値は文字列の’Hello’としてバリアント変数Vに代入される
V:=12.34; //値は浮動小数点の12.34としてバリアント変数Vに代入される
Str := V; //Strには文字列としての’12.34’が代入される
V := V+20; //Vには 浮動小数点12.34 + 20 の合計 32.34が浮動小数点として代入される
V:= V + Str; //Vには32.34と、’12.34’の浮動小数点としての合計 44.68が代入される
V:= 'Hello'; //Vには文字列’Hello’が代入される
V := V + Str; //Vには文字列’Hello’と文字列’12.34’の連結’Hello12.34’が代入される
end;
##おわりに
第5回 2月20日分の Delphiパート「配列 と レコード」は以上です。
<<シーズン2 第4回の記事はこちら