「D言語/関数」の版間の差分
削除された内容 追加された内容
→スコープ: シャドウイングは許されない(コンパイルエラー) タグ: 2017年版ソースエディター |
トップレベルのインスタンスの識別子は、シャドウイング禁止の対象外。内部関数のローカル変数と包含する関数のローカル変数との間もシャドウイングではない。コードにhttps://run.dlang.io/ のフォーッマッターを適用。また、C言語風の前方参照を回避するコーディングスタイルを廃止。 タグ: 2017年版ソースエディター |
||
1 行
== 関数 ==
関数とは一連の動作ををまとめた手続きであり、0個以上の引数を受け取り、1つの戻値を返すこと
; [https://paiza.io/projects/hJbYomhq4UMlWl4EE3YgfQ?language=kotlin コード例]:<syntaxhighlight lang=d line>
import std.stdio;
void main()
{ writeln("main内");
auto n = 21;
n.foo().writeln;
bar();
}
16 行
{
writeln("foo内");
return 2 * n;
}
24 行
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
main内
42
bar内
</syntaxhighlight>
=== 関数の定義・呼び出し方 ===
=== 関数の戻値型の型推論 ===
関数の戻値型に、キーワード <code>auto</code> を使うことで、戻値型を型推論することができます。
;[https://paiza.io/projects/rOX7ozOQz8CbnxhqPHy4XA?language=d コード例]:<syntaxhighlight lang=d line highlight=10>
import std.stdio;
{
}
auto foo(int i)
{
return
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
9
</syntaxhighlight>
: int 型の変数と int 型のリテラル同士の加算なので int が型推論されます。
== スコープブロック文 ==
69 ⟶ 59行目:
スコープブロックは入れ子にすること可能です。
;[https://paiza.io/projects/
import std.stdio;
void main()
{
func0();
writef("aはmainでいま%d\n", a);
}
int a = 99;
void func0()
{ int a = 2; // 9行の a は、トップレベルなので シャドウイング にあたらない。
writef("aはfunc0でいま%d\n", a);
// 新たなスコープ
{
// int a = 42; ←
}
writef("aはfunc0内の内側のスコープでいま%d\n", a);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>:<syntaxhighlight lang=text>
aはfunc0でいま2
aはfunc0内の内側のスコープでいま2
aはfunc0内の内側のスコープでいま2
aはmainでいま99
</syntaxhighlight>
== 内部関数 ==
130 ⟶ 93行目:
D言語では、関数の内部で関数を定義でき、利用できます。D言語では、ほぼあらゆる言語の要素がいたるスコープで記述でき、例えば、<code>import</code> ですらも特定のスコープ内のみに適用する、といったことが可能です。内部関数は、それのほんの一例です。
;コード例:<syntaxhighlight lang=d line>
import std.stdio;
void main()
{
int x = 0;
// ↓ これが内部関数
void
{
x++;
writeln("inside! X = ", x);
}
// 内部関数はここで終わり
writeln("1回目");
writeln("2回目");
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
1回目
inside! X = 1
2回目
inside! X = 2
</syntaxhighlight>
:標準C言語とC++には、2020年のいまのところ内部関数はありません。C#には2017年ごろ、C#7にて内部関数が追加されました(なお、C#の内部関数の記法はD言語のそれとは大きく違う)。
=== 内部関数と変数の有効範囲について ===
内部関数の中にある変数の扱いは、通常のスコープと
====
;[https://paiza.io/projects/mD2lc7V3K_vYYS3sy-bUVA?language=d コード例]:<syntaxhighlight lang=d line>
import std.stdio;
void main()
{
outer();
writef("mainに帰還。aはいま%d\n", a);
}
int a = 7;
void
{
void inner()
{
int a = 3;
writef("内部関数の中で aはいま%d\n", a);
}
inner(); // 利用の際は呼び出すのを忘れないように
writef("関数では aはいま%d\n", a);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
内部関数の中で aはいま3
関数では aはいま5
mainに帰還。aはいま7
</syntaxhighlight>
: 関数スコープの識別子の解決は、スコープの内側から外側に向かってトップレベルまで行なわれます。
: トップレベルまで探査して見つからなければ、未定義変数の参照となります。
: これは、コンパイル時に静的に行なわれます。
: 他の言語の経験がある人であれば「クロージャー」だと言えば判りやすいでしょうか。
==== 内部関数からトップレベルの変数を参照 ====
いっぽう、再宣言しない場合、外側のスコープにある変数を書き換えます。
;内部関数から内包する関数のローカル変数を参照:<syntaxhighlight lang=
import std.stdio;
void main()
{
outer();
writef("mainに帰還。aはいま%d\n", a);
}
int a = 7;
void
{
{
a = 3; // int が無く、再宣言なしの単なる代入命令
writef("内部関数の中で aはいま%d\n", a);
}
inner(); // 利用の際は呼び出すのを忘れないように
writef("関数では aはいま%d\n", a);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text line>
内部関数の中で aはいま3
関数
mainに帰還。aはいま
</syntaxhighlight>
: 3行目のaの値に注目してください。上記コードの内部関数では再宣言しないで代入したので、mainに帰還した際にも、変数aが7でなく3に置き換わっています。
== セキュリティ・レベル ==
C言語に無い特徴として、D言語の「関数」には
* @system * @trusted * @safe の3種類のセキュリティ・レベル 何も指定しない場合、レベルは @system になっています。
:<syntaxhighlight lang=
int foo() @system
{
252 ⟶ 209行目:
}
</syntaxhighlight>
のように指定します。
;@system で宣言された関数(ディフォルト)
:C言語の関数のように、全ての言語機能にアクセスできます。
;@safe で宣言された関数
:ポインタの利用が禁止されます。
:@safe または @trusted な関数だけしか呼び出しできません。
:(@system レベルの関数を呼び出しできません)
;コード例:<syntaxhighlight lang=d line>
import std.stdio;
void main(){
writeln( foo() );
}
int foo() @system
271 ⟶ 229行目:
return 28;
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
28
</syntaxhighlight>
;うごくコード例2:<syntaxhighlight lang=d line>
import std.stdio;
void main(){
276 ⟶ 240行目:
writeln( foo() );
}
int foo2() @safe
296 ⟶ 250行目:
return foo2();
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
35
</syntaxhighlight>
;禁止されているコード例(コンパイルエラー):<syntaxhighlight lang=d line highlight=15>
import std.stdio;
void main()
{ }
int foo2() @system
{
return 35;
}
321 ⟶ 271行目:
return foo2();
}
</syntaxhighlight>
;コンパイル結果:<syntaxhighlight lang=text>
onlineapp.d(15): Error: `@safe` function `onlineapp.foo` cannot call `@system` function `onlineapp.foo2`
onlineapp.d(8): `onlineapp.foo2` is declared here
</syntaxhighlight>
338 ⟶ 284行目:
D言語の契約プログラミングでは、要求事項を見たさない入力または出力がされた際、プログラムの実行を停止する。また、静的な契約 (static assert) も存在し、こちらはコンパイル時間数実行が可能な範囲においてコンパイル時にチェックされ、条件が満たされない場合はコンパイルがエラーとなる。
;コード例(自然数に対して階乗を計算するプログラム):<syntaxhighlight lang=d line>
import std.stdio;
void main()
writeln(factorial(5));
}
int factorial(int n)
in
{
assert(n >= 0); // 階乗への入力は非負整数でなければならない
}
out (result)
{
assert(result >= n); // n! ≧ n
assert(n <= 3 || result > n ^^ 2); // n ≧ 4 のとき、n! > (nの2乗)
}
do
{
if
return 1;
return n * factorial(n - 1);
}
</syntaxhighlight>
;実行結果:<syntaxhighlight lang=text>
120
</syntaxhighlight>
:解説や書式
;書式:<syntaxhighlight lang=d line>
in
{
assert(入力の要求事項);
}
out (result)
{ assert(出力の要求事項);
}
do
{
return 出力内容;
}
</syntaxhighlight>
: のように書く。
: 例として、もし writeln( factorial(4) ); で、引数を4でなく、たとえば「-7」など負の数にすると、入力の要求事項を満たさなくなるので、実行時エラーになる。
;エラーの例:<syntaxhighlight lang=text>
core.exception.AssertError@onlineapp.d(12): Assertion failure
----------------
??:? _d_assertp [0x56223707d810]
./onlineapp.d:12 int onlineapp.factorial(int) [0x56223707c3fe]
./onlineapp.d:6 _Dmain [0x56223707c3ca]
</syntaxhighlight>
:2022年現在、C言語には、直接契約プログラミングをサポートしてはいないが、assert()関数とマクロセットで契約プログラミングを実現する試みがある、
契約プログラミングを公式にサポートしている言語はいまのところ少ない。(非標準のライブラリなどでサポートされている言語はそこそこあるが、しかしプログラム言語の機能としてサポートされている言語は、かなり少ない。)
401 ⟶ 348行目:
readf() などの関数を使うと、ユーザーによるキーボードからの入力を受け付ける。それを使って数値をいろいろと入力して、契約プログラミングのコードをテストしてみるとする。
;コード例:<syntaxhighlight lang=d line>
import std.stdio;
void main()
writeln("Please input number");
int some;
readf("%d", &some);
writef("you input %d\n", some);
}
float bbb(float num)
in
{
assert(num >= 0);
}
out (result)
{
assert(result >= 0);
}
do
{
return num + 3;
}
</syntaxhighlight>
実際にテストしてみると、何も入力していない間は、いったんコンパイルできて実行できても、「-4」などマイナスの数を入力すると、そこで実行を停止する。
(コマンド rdmd でも コマンド dmd でも同様。)
437 ⟶ 382行目:
core.exception.AssertError@hello.d(5): Assertion failure
のようにエラーメッセージが表示される。
もちろん、プラスの「5」など契約に適合した数値を入れているかぎりは、動作する。
443 ⟶ 387行目:
これは、「'''契約をユーザーの入力のチェックに使ってはいけない'''」という決まりに反しており、契約の誤った使い方である。もちろん、決まりを破ってでも契約によるエラーメッセージを出したい理由があるならばそうすることもできるが、特段の理由がない限りは避けるべきである。
::註:この例がファジングを意図していると解すべきではない。
== 参考文献 ==
<references />
[[カテゴリ:D言語]]
|