「D言語/関数」の版間の差分

削除された内容 追加された内容
編集の要約なし
内部関数
253 行
 
このように、契約プログラミングは、けっしてコンパイルだけを契約違反で停止するのではなく、コンパイル以降も監視しており、実行中でも契約違反があればコードの実行を停止する。
 
 
 
== 関数内の変数の記憶の性質 ==
D言語では、関数の内部における変数の有効範囲の性質が、C言語とはやや違います。
 
まず、D言語ではユーザが自分で関数を作成することができ(「ユーザ定義関数」という)、そのユーザ定義関数内でいろいろな処理を記述できます。
 
では、変数の有効範囲の例を下記コードで見ていきましょう。
 
 
;コード例
<syntaxhighlight lang="D">
import std.stdio;
 
int a = 99;
 
void kansu(){
int a = 2;
 
writef("aはkansuでいま%d\n", a);
}
 
 
void main()
{
kansu();
writef("mainでは、aはいま%d\n", a);
}
</syntaxhighlight>
 
;実行結果
<pre>
aはkansuでいま2
mainでは、aはいま99
</pre>
 
 
D言語において、関数の内部でなんらかの変数が「int」などの型宣言をして定義された場合、たとえ以前に同名の変数が宣言されていても、それとは別の変数が新たに作成されます。
 
 
作成された変数は、その関数の中でだけ有効です。
 
なので、上記の例では、kansuを抜けてmainに戻ると、変数aは99に戻ってしまいます。
 
;代入だけの場合
いっぽう、関数の内部で、「int」などをつけて新規に宣言するのではなく、単に a=2 みたいに代入式を書いた場合、D言語では、既存の同名の変数を上書きします。(C言語とは、ここらへんの仕組みが違います。)
 
;コード例
<syntaxhighlight lang="D">
import std.stdio;
 
int a = 99;
 
void kansu(){
a = 2; // int が無い
 
writef("aはkansuでいま%d\n", a);
}
 
 
void main()
{
kansu();
writef("mainでは、aはいま%d\n", a);
}
</syntaxhighlight>
 
;実行結果
<pre>
aはkansuでいま2
mainでは、aはいま2
</pre>
 
上記の例では、関数 kansu では新規宣言されておらず単なる代入なので、外部の変数を上書きします。
 
その結果、aの値がmainでも「2」に変わります。
 
 
C言語風に説明するなら「ローカル変数として使いたいなら、ユーザ定義関数内で再度その変数を宣言する必要がある」という事になります。
 
 
 
== 内部関数 ==
=== 基本 ===
D言語では、関数の内部で、べつの関数を定義でき、利用できる。この仕組みを内部関数という。
 
けっして単に処理ブロックが単に<code>{ }</code>カッコで 入れ子(ネスト)構造になってるだけでなく(それなら標準Cでも可能)、さらにその内側の関数を呼び出して利用できるのが、内部関数の特徴のひとつである(標準Cなどの関数の入れ子ブロックでは、これが不可能)。
 
;コード例
<syntaxhighlight lang="D">
import std.stdio;
 
void main()
{
// ↓ これが内部関数
void naibu() {
writeln("inside!");
}
// 内部関数はここで終わり
 
naibu();
}
</syntaxhighlight>
 
;表示結果
inside!
 
 
通常の関数の定義と同様に、そのまま書いていい。
 
また、関数を使用するには、その関数の名前を
関数名();
の書式で書けばいいだけである。
 
標準C言語とC++には、2020年のいまのところ内部関数は無い。C#には2017年ごろ、C#7にて内部関数が追加された。
 
 
=== 内部関数と変数の有効範囲について ===
内部関数の中で定義した変数の寿命は、内部関数の中だけです。その内部関数の置き場所の関数ですら、もはや失効しています。
 
文章では説明が難しいので、下記コードと実行結果をみてください。
 
;コード例
<syntaxhighlight lang="D">
import std.stdio;
 
int a=7;
 
void soto(){
void naibu() {
int a=3;
writef("内部関数の中で aはいま%d\n", a);
}
naibu(); // 利用の際は呼び出すのを忘れないように
writef("内部関数の隣りでは aはいま%d\n", a);
}
 
 
void main()
{
soto();
writef("mainに帰還。aはいま%d\n", a);
}
</syntaxhighlight>
 
;表示結果
<pre>
内部関数の中で aはいま3
内部関数の隣りでは aはいま7
mainに帰還。aはいま7
</pre>
 
内部関数の置き場所になっているsoto ですら、もはやaの値は(内部関数で定義した3ではなく)7に戻っています。