布団が俺を呼んでいる

丘山大一のぶろぐ

Delphi 戻り値色々

関数の戻り値の指定とその時の戻り値。
適当な実装をした場合、戻り値は不定になるのか? というか不定って何?
というような適当実験。
今回長い。


その1

function TForm1.Hoge: string; 
begin

end; 

実装
無名メソッドではなく、普通のメソッド。
実装は空っぽ。

結果
空文字。


その2

function TForm1.Hoge: string;
begin
  Result := (function : string
             begin

             end)();

  if not Result.IsEmpty then Exit('123');

  Result := (function : string
               begin
                 Result := 'abc';
             end)();
end;

実装
「実装が空っぽ」の無名メソッドを呼び出し、
その時の戻り値が空かどうか調べ、(空でないならExit)
最後まで到達したら無名メソッドで「abc」を返します。

結果
abc

考察
「実装が空っぽ」の無名メソッドであっても、変な値を返すわけではないようです。


その3

function TForm1.Hoge: string;
var
  i : Integer;
begin
  for i := 0 to 1 do
  begin
    Result := (function : string
               begin

               end)();

    if not Result.IsEmpty then Exit('123');

    Result := (function : string
               begin
                 Result := 'abc';
               end)();
  end;
end;

実装
「実装が空っぽ」の無名メソッドを呼び出し、
その時の戻り値が空かどうか調べ、(空でないならExit)
最後まで到達したら無名メソッドで「abc」を返します。
上記をループで2回実行します。

結果
123

考察
デバッグすると分かりますが、一回目のループでのResultは
・Result := 空
・Result := abc
であり、
二回目のループで
・Result := abc のまま!
・Exit句で123 で抜ける
という動作をします。
一個目の無名メソッドが「何もしない何か」あるいは「abcを返す」ようです。


その4

function TForm1.Hoge: string;
var
  i : Integer;
begin
  for i := 0 to 1 do
  begin
    Result := (function : string
               begin
                 Result := '';
               end)();

    if not Result.IsEmpty then Exit('123');

    Result := (function : string
               begin
                 Result := 'abc';
               end)();
  end;
end;

実装
「空文字を返す」の無名メソッドを呼び出し、
その時の戻り値が空かどうか調べ、(空でないならExit)
最後まで到達したら無名メソッドで「abc」を返します。
上記をループで2回実行します。

結果
abc

考察
一個目の無名メソッドで「Result := '';」を明示すれば、きちんと空文字を返すようになり、本体メソッド結果はabcになります。


その5

function TForm1.Hoge: string;
var
  i : Integer;
begin
  for i := 0 to 1 do
  begin
    if i = 1 then
    begin
      Result := (function : string
                 begin

                 end)()
                 +
                (function : string
                 begin

                 end)();
      Exit;
    end;

    Result := (function : string
               begin
                 Result := 'abc';
               end)();
  end;
end;

実装
1回目のループではResult にabcを入れて、
2回目のループで実装が空っぽの無名メソッドの戻り値をくっつけてExit。

結果

考察
ループさせた時の「実装が空っぽ」の無名メソッドは、先ほどみた限りでは「何もしない何か」or「abcを返す」でしたが、今回は「空」が返ってきます。


その6

function TForm1.Hoge: string;
type
  TAct = reference to function : string;
var
  act : TAct;
begin
  act := (function : string
          begin

          end);

  Result := (function : string
             begin
               Result := 'abc';
             end)();
end;

実装
無名メソッドの代入先を宣言してみます。
宣言だけして、使用しません。

結果
abc

考察
まあ当たり前ですね。


その7

function TForm1.Hoge: string;
type
  TAct = reference to function : string;
var
  act : TAct;
begin
  act := (function : string
          begin

          end);

  Result := (function : string
             begin
               Result := 'abc';
             end)();
  Result := act;
end;

実装
無名メソッドの代入先を宣言してみます。
一度別の値を無名メソッドで入れて、
最後に空っぽのメソッドを呼び出します。

結果
abc

考察
またしても「何もしない何か」or「abcを返す」となりました。


その8

function TForm1.Hoge: string;
type
  TAct = reference to function(n :Integer) : string;
var
  act : TAct;
  i : Integer;
begin
  act := (function(n :Integer):string
          begin
            if n = 0 then Exit('あいうえお');
            if n = 1 then Exit;
          end);

  for i := 0 to 1 do
  begin
    Result := act(i);
  end;
end;

実装
引数に応じて戻り値を返す無名メソッドです。
n = 0 の時は「あいうえお」
n = 1 の時は特に戻り値を指定しません。
この無名メソッドをループで2回呼び出します。

結果
あいうえお

考察
2回目の結果が「何もしない何か」or「あいうえおを返す」となりました。


その9

function TForm1.Fuga(n: Integer): string;
begin
  if n = 0 then Exit('あいうえお');
  if n = 1 then Exit;
end;

function TForm1.Hoge: string;
var
  i : Integer;
begin
  for i := 0 to 1 do
  begin
    Result := Fuga(i);
  end;
end;

実装
無名メソッドを止めてみた

結果
あいうえお

考察
2回目の結果が「何もしない何か」or「あいうえおを返す」となりました。


その10

function TForm1.Fuga(n: Integer): string;
begin
  if n = 0 then Exit('あいうえお');
  if n = 1 then Exit;
end;

function TForm1.FugaFuga(n: Integer): string;
begin
  if n = 0 then Exit('かきくけこ');
  if n = 1 then Exit;
end;

function TForm1.Hoge: string;
begin
  Result := Fuga(0);
  Result := FugaFuga(1);
end;

実装
似ている二つのメソッドを実装、呼び出してみた。
引数0の時は戻り値を指定して返す。
引数1の時は戻り値を指定しない。

結果
あいうえお

考察
2回目の結果が「何もしない何か」or「別メソッドの結果を返す」となりました。



というわけで


なんとなくわかると思いますが、
・メソッドの戻り値にはデフォルト値がある(今回はstringなので空文字)
・戻り値を明示しない場合、「何もしない」
・戻り値を明示せず、かつメソッドの戻り値を直参照するとデフォルト値を返す
のような動きをします。
「不定」というのは変な値になることではなく、戻り値として参照できる値を返す、といった結果になるようです。
Resultに格納されているアドレスや関数の呼び出し順序なんかに依存してるんでしょう。たぶん。
(ここにきて超絶適当)

Result を初期化するクセをつけておけばいいというだけの話でした。



OBJECT PASCAL HANDBOOK―マルチデバイス開発ツールDelphiのためのプログラミング言語完全ガイド

新品価格
¥6,480から
(2017/8/16 20:52時点)

コメントを書く

布団が俺を呼んでいる | 空文字を判定する最も効率のいいやり方は? (Delphi編)

布団が俺を呼んでいる

丘山大一のぶろぐ

空文字を判定する最も効率のいいやり方は? (Delphi編)

現在いるプロジェクトではDelphiなる言語を使っています。
んで、他の人のソースを読んでいると、空文字かどうかの判定の仕方には色んな書き方があるのだなあと思います。
代表的なのは、
(1) Result := s.IsEmpty;
(2) Result := s = '';
(3) Result := s.Length = 0;
(4) Result := s = EmptyStr;
等でしょうか。
ちなみに、私は基本的にIsEmptyメソッドをなるべく使う派です。
「''」と記述すると、後で読み返した時に
「これは本当に空文字で判定したいのだろうか?
 それとも実は半角スペースと比較するべきところを間違えてしまったのだろうか?」
等と疑わせてしまうこと、また本当にそのように間違える可能性が出てきてしまうためです。
 あとIsEmptyを使った方がコード上からカッコが減りやすいというのもある。IDEが貧弱なRadStudioではこれが割と重要。
 
さて、そうは言っても、「IsEmpty使おうぜ!」と主張しても使ってくれる人はなかなか増えません。
わざわざ書くのが面倒+間違いやすい「''」で書く人がほとんどです。

むう。何故だ。

と思っていると、MSDNの中で、(C#ですが)空文字判定方法について記述してある箇所を見つけました。
https://msdn.microsoft.com/ja-jp/library/ms182279.aspx?f=255&MSPPError=-2147217396
な、なるほど!
きっと速度差があるからみんな「''」を使うんだ!
理由があったんだね!

というわけで、それぞれの実行時間はどれくらいなのか。

2147483647 回ループを回してテストしてみました(適当な値をとるのが面倒だったので、Integer.MaxValue)。
以下、単位はミリ秒。
(1) IsEmpty
①37430
②37197
③37296
(2) 「''」
①36414
②36193
③36258
(3) Length
①40755
②40704
③40771
 
最速と最遅で比較すると、
(2) Result := s = '';
が最遅に比べ4578ミリ秒早いという結果になりました。
一回当たりおよそ
4578 / 2147483647 ≠
5000 / 2000000000 ≠
5 / 2000000 ≠ 0.0000025 ミリ秒早い。
ということになります。

おおー、なるほど、確かに早い。
なるほど、同じ結果が出るなら早い方を採用するのは理に適っている。
うん……正しい……
いや、たしかに正しいんだが……

全然変わんねえよ (´・ω・`)

実質0秒だよコレ。
これ以上速度を気にしなければならない要件なら、そもそもDelphi使わないわな。
それこそCとかアセンブラとか、もっと高速な言語を採用するわ。
やっぱり「''」と比較するコーディングは、昔から書いている人の単なる惰性な気がするなあ……。
一応、「早い」ということは分かったから、無暗にIsEmptyを進めるのは止めるけど……
やっぱり私はIsEmptyでいこう……。

※注意!
私の開発環境での結果+面倒だったのでデフォルト設定のDebugビルド結果です。
ロジックや書き方によってなんぼでも結果は変わることをお断りしておきます。



Delphi XE2プログラミング入門

新品価格
¥4,104から
(2016/1/18 20:58時点)

コメントを書く