先日参加した「わんくま同盟大阪勉強会#58」の「LINQ再入門」の例題がわかりやすかったので、Delphiで同じことをしてみました。
お題
- Project Euler Problem2
- フィボナッチ数列の項は前の2つの項の和である. 最初の2項を1, 2 とすれば, 最初の10項は以下の通りである.
- 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
- 数列の項の値が400万以下の, 偶数値の項の総和を求めよ.
回答サンプル
function Problem1(Limit: Integer): Integer;
var
Sum, A, B, Temp: Integer;
begin
//合計処理
Sum := 0;
//数列の生成処理
A := 1;
B := 0;
while B < Limit do //終了条件
begin
//数列の生成処理
Temp := A;
A := B;
B := A + Temp;
if B mod 2 = 0 then //抽出条件
begin
Inc(Sum, B); //合計処理
end;
end;
Result := Sum; //合計処理
end;
このコードは、「数列の生成処理」「終了条件」「抽出条件」「合計処理」が入り交じっています。
リファクタリング
delphi-collを使ってリファクタリングしてみます。
uses
Collections.Base,
Collections.Lists;
function Problem2(Limit: Integer): Integer;
var
FibonacchiNumber: ISequence<Integer>;
begin
//数列の生成処理
FibonacchiNumber:= TFibonacchiNumber.Create;
Result := FibonacchiNumber.TakeWhile( //終了条件
function(Arg: Integer): Boolean
begin
Exit(Arg < Limit);
end).Where( //抽出条件
function(Arg: Integer): Boolean
begin
Exit(Arg mod 2 = 0);
end).Aggregate( //合計処理
function(Arg1, Arg2: Integer): Integer
begin
Exit(Arg1 + Arg2);
end);
end;
同じ処理がまとまったので、ソースコードが読みやすくなりました。
数列の生成処理
フィボナッチ数列の値を生成するTFibonacchiNumberは次のようになります。
uses
Collections.Base,
Collections.Lists;
type
TFibonacchiNumber = class(Collections.Base.TSequence<Integer>)
private type
TFibonacchiNumberEnumerator = class(TInterfacedObject,
Collections.Base.IEnumerator<Integer>)
private
A: Integer;
B: Integer;
public
constructor Create;
function GetCurrent: Integer;
function MoveNext: Boolean;
property Current: Integer read GetCurrent;
end;
public
function GetEnumerator: Collections.Base.IEnumerator<Integer>; override;
end;
{ TFibonacchiNumber }
function TFibonacchiNumber.GetEnumerator: Collections.Base.IEnumerator<Integer>;
begin
Result := TFibonacchiNumberEnumerator.Create;
end;
{ TFibonacchiNumber.TFibonacchiNumberEnumerator }
constructor TFibonacchiNumber.TFibonacchiNumberEnumerator.Create;
begin
A := 1;
B := 0;
end;
function TFibonacchiNumber.TFibonacchiNumberEnumerator.GetCurrent: Integer;
begin
Result := B;
end;
function TFibonacchiNumber.TFibonacchiNumberEnumerator.MoveNext: Boolean;
var
Temp: Integer;
begin
Result := True;
Temp := A;
A := B;
B := A + Temp;
end;