文
関数内の実行順序は、文によって制御される。 関数の本体は、0個以上の文の連続で構成される。 実行は語彙順に行われるが、特定の文は効果を遅延させる場合がある。 文には値はなく、その効果のために実行される。
Statement: EmptyStatement NonEmptyStatement ScopeBlockStatement EmptyStatement: ; NoScopeNonEmptyStatement: NonEmptyStatement BlockStatement NoScopeStatement: EmptyStatement NonEmptyStatement BlockStatement NonEmptyOrScopeBlockStatement: NonEmptyStatement ScopeBlockStatement NonEmptyStatement: NonEmptyStatementNoCaseNoDefault CaseStatement CaseRangeStatement DefaultStatement NonEmptyStatementNoCaseNoDefault: LabeledStatement ExpressionStatement DeclarationStatement IfStatement WhileStatement DoStatement ForStatement ForeachStatement SwitchStatement FinalSwitchStatement ContinueStatement BreakStatement ReturnStatement GotoStatement WithStatement SynchronizedStatement TryStatement ScopeGuardStatement AsmStatement MixinStatement ForeachRangeStatement PragmaStatement ConditionalStatement StaticForeachStatement ImportDeclaration
文と宣言の間の文法上の曖昧性は、 宣言が優先されることで解決される。 このような文を括弧で囲むと、 文であることが明確になる。
スコープ文
ScopeStatement: NonEmptyStatement BlockStatement
ローカルシンボル用の新しいスコープが、 NonEmptyStatement またはBlockStatementに
新しいスコープが導入されたとしても、 ローカルシンボルの宣言は、同じ関数内の他のローカルシンボル宣言を隠す(覆い隠す)ことはできない。
void func1(int x) { int x; // 不正、xはパラメータxを影で囲む int y; { int y; } // 不正、yはスコープのyを影で囲む void delegate() dg; dg = { int y; }; // OK、このyは同じ関数内にない struct S { int y; // OK、このyはローカルではなくメンバである } { int z; } { int z; } // OK、このzは他のzをシャドウしていない { int t; } { t++; } // 不正、tは未定義である }
スコープ ブロック 文
ScopeBlockStatement: BlockStatement
スコープブロック文は、BlockStatementの新しいスコープを導入する。
ラベル付き文
文にはラベルを付けることができる。ラベルとは、 文の前に置かれる識別子である。
LabeledStatement: Identifier : Identifier : Statement
空の文も含め、あらゆる文にラベルを付けることができ、 goto文のターゲットとして使用できる。 ラベルを付けた文は、 break または continue文 のターゲットとしても使用できる。
ラベルは、ブロックの最後に続く文がなくても表示される。
ラベルは、宣言、変数、"型"などとは独立した名前空間にある。 それでも、ラベルはローカル宣言と同じ名前を持つことはできない。 ラベルの名前空間は、そのラベルが現れる関数の本体である。 ラベルの名前空間はネストしない。つまり、 ブロック文内のラベルは、そのブロックの外からアクセスできる。
ある関数内のラベルは、別の関数から参照することはできない。
ブロック文
BlockStatement: { } { StatementList } StatementList: Statement Statement StatementList
ブロック文とは、{ } で囲まれた一連の文である。 文は語彙順に実行され、 ブロックの終わりに到達するか、 または文が制御を他の場所に移すまで実行される。
式 文
ExpressionStatement: Expression ;
式が評価される。
(x + x) のように何の効果も持たない式は、 void型にキャストされない限り、式文としては 不正である。
int x; x++; // OK x; // 不正 1+1; // 不正 cast(void)(x + x); // OKxml-ph-0000@deepl.internalxml-ph-0000@deepl.internal
宣言文
宣言文は変数を定義し、 "型"、"テンプレート"、"関数"、"インポート"、 "条件"、"静的 foreach"、"静的 assert" を宣言する。 xml-ph-0000@deepl.internal
DeclarationStatement: StorageClassesopt Declaration
いくつかの宣言文:
int a; // aをint型として宣言し、0に初期化する struct S { } // 構造体sを宣言する alias myint = int;
If文
If文は、条件付きで文を実行するシンプルな機能を提供する。
IfStatement: if ( IfCondition ) ThenStatement if ( IfCondition ) ThenStatement else ElseStatement IfCondition: Expression auto Identifier = Expression scope Identifier = Expression TypeCtors Identifier = Expression TypeCtorsopt BasicType Declarator = Expression ThenStatement: ScopeStatement ElseStatement: ScopeStatement
識別子変数が宣言されている場合は、それが評価される。 それ以外の場合、"式"が評価される。結果は、 opCast!bool() メソッドが定義されている場合、 ブール値がtrue の場合、ThenStatementに転送され 、それ以外の場合、ElseStatementに転送される。
ElseStatement は、最も内側のif 文に関連付けられる。ただし、ElseStatement が関連付けられていないものに限る。
- auto Identifierが指定された場合、変数の型は 式と同じになる。
- TypeCtors Identifierが指定された場合、変数は "式"の型として宣言されるが、TypeCtorsが適用される。
- BasicType形式が指定された場合、変数の型が 通常の変数宣言と同様に宣言される 。
変数のスコープはThenStatementのみである。
import std.regex; if (auto m = matchFirst("abcdef", "b(c)d")) { writefln("[%s]", m.pre); // [a]を表示する writefln("[%s]", m.post); // [ef]を表示する writefln("[%s]", m[0]); // [bcd]を表示する writefln("[%s]", m[1]); // [c]を表示する } else { writeln("no match"); //writeln(m.post); // エラー: 未定義の識別子'm' } //writeln(m.pre); // エラー: 未定義の識別子'm'
While文
WhileStatement: while ( IfCondition ) ScopeStatement
While文は単純なループを実装する。
IfCondition が式である場合、その式が評価され、 ブール値に変換できる型でなければならない。IfConditionがtrue である場合、ScopeStatementが実行される。 ScopeStatementが実行された後、式が再度評価され、 IfCondition が true である場合、ScopeStatementが再度実行される。この処理は、式が false に評価されるまで継続される。
int i = 0; while (i < 10) { foo(i); ++i; }
auto 識別子が提供された場合、それは宣言され、 式の値と型に初期化される。そのスコープは 初期化された時点からScopeStatementの終了時までとなる。
TypeCtorsの識別子が指定された場合、 TypeCtorsで指定された型として宣言され、"式"の値で初期化される。 そのスコープは、初期化された時点から ScopeStatementの終了までとなる。
宣言子が指定された場合、それは宣言され、 "式"の値に初期化される。そのスコープは、初期化された時点から ScopeStatementの終了までとなる。
BreakStatementはループを終了する。
ContinueStatement は、再びIfConditionの評価に直接移行する。
Do文
DoStatement: do ScopeStatement while ( Expression ) ;internalinternal
Do while文は単純なループを実装する。
ScopeStatementが実行される。次に、Expressionが評価され、 ブール値に変換できる型でなければならない。 の場合、ループが再び繰り返される。 この処理が続く。ScopeStatementが実行される。次に、"式"が評価され、 booleanに変換できる型でなければならない。true の場合、ループが再び繰り返される。 これは、"式"が false に評価されるまで続く。
int i = 0; do { foo(i); } while (++i < 10);
BreakStatementはループを終了する。ContinueStatement は 再び式の評価に直接移行する。
For文
For文は、初期化、テスト、およびインクリメント 節を持つループを実装する。
ForStatement: for ( Initialize Testopt ; Incrementopt ) ScopeStatement Initialize: ; NoScopeNonEmptyStatement Test: Expression Increment: Expression
Initializeが実行される。 Testが評価され、booleanに変換できる型でなければならない。 Testがtrue の場合、 ScopeStatementが実行される。実行後、 Incrementが実行される。 その後、Testが再度評価され、true の場合、 ScopeStatementが再度実行される。これは、 Testが false に評価されるまで続く。
BreakStatementはループを終了する。 ContinueStatement は 直接Increment に移行する。
ForStatementは新しいスコープを作成する。 Initializeで変数を宣言した場合、その変数のスコープは ScopeStatementまで拡張される。例えば:
for (int i = 0; i < 10; i++) foo(i);は次のものと同じ意味になります。
{ int i; for (i = 0; i < 10; i++) foo(i); }
ScopeStatementは空の文であってはならない。
for (int i = 0; i < 10; i++) ; // 不正代わりに次のコードを使用すること。
for (int i = 0; i < 10; i++) { }
Initialize は ; のみでよい場合がある。 Test は省略可能であり、省略された場合は、true と評価されたものとして扱われる。
foreach文
foreach 文は一連の値を反復する。
AggregateForeach: Foreach ( ForeachTypeList ; ForeachAggregate ) ForeachStatement: AggregateForeach NoScopeNonEmptyStatement Foreach: foreach foreach_reverse ForeachTypeList: ForeachType ForeachType , ForeachTypeList ForeachType: ForeachTypeAttributesopt BasicType Declarator ForeachTypeAttributesopt Identifier ForeachTypeAttributesopt alias Identifier ForeachTypeAttributes: ForeachTypeAttribute ForeachTypeAttribute ForeachTypeAttributes ForeachTypeAttribute: enum ref scope TypeCtor ForeachAggregate: ExpressionForeachAggregateが評価される。 静的配列、動的配列、連想配列、 構造体、クラス、デリゲート、シーケンスのいずれかである 式として評価されなければならない。 NoScopeNonEmptyStatementが実行される。
ForeachAggregateが評価される。これは、静的配列、動的配列、連想配列、 構造体、クラス、デリゲート、またはシーケンスである式として評価されなければならない。 NoScopeNonEmptyStatementが実行され、
ForeachTypeListで宣言される変数の数は、 集約の種類によって異なる。宣言された変数は、 各反復処理の開始時に設定される。
- 既定では、宣言された単一の変数は現在の要素のコピーである。
- ForeachTypeAttributeがref の場合、その変数は 集約の現在の要素への参照となる。
- ForeachTypeAttribute が scope の場合、その変数は scope意味を持つ。
指定されていない場合、ForeachType変数の型は、 ForeachAggregateの型から推測できる。auto は有効なForeachTypeAttributeではないことに注意。 以下の2つのforeach 文は等価である。
int[] arr = [1, 2, 3]; foreach (int n; arr) writeln(n); foreach (n; arr) // OK、nはintである writeln(n);
集約はループ不変でなければならない。 つまり、 NoScopeNonEmptyStatement
foreach の本体にBreakStatementがある場合、ループを終了する。 ContinueStatement がある場合、すぐに次の反復を開始する。
配列のforeach
集約が静的配列または動的配列の場合、 1つまたは2つの変数を宣言できる。1つの場合、その変数は "値"と呼ばれ、配列の各要素に順次設定される。変数の型を指定する場合は、 配列の要素型と互換性のある型でなければならない( 下記で説明する文字要素の特別な処理を除く)。 値変数は 、ref で宣言すると、配列の要素を変更できる。
2つの変数が宣言されている場合、最初の変数は「インデックス」と呼ばれ 、2番目の変数は上記のように「値」と呼ばれる。 インデックスは ref で宣言することはできない。 インデックスは、各反復処理において配列要素のインデックスに設定される。 インデックスの型は推測できる。
char[] a = ['h', 'i']; foreach (i, char c; a) { writefln("a[%d] = '%c'", i, c); }
動的配列の場合、インデックス型はsize_t と互換性がある必要がある。ただし、配列の長さが静的に小さい整数型に収まることが分かっている場合は この限りではない。 静的配列では、配列の長さに一致する任意の整数型を使用できる。
foreach の場合、 配列の要素はインデックス0から始まり 配列の最後の要素まで順に繰り返される。foreach_reverse の場合、配列の要素は逆順で訪問される。
文字の配列を順に処理する
集約式が、char、wchar、またはdcharの静的または動的配列である場合、 値変数の型はchar 、wchar 、またはdchar のいずれかになる。 このように、UTF配列は
char[] a = "\xE2\x89\xA0".dup; // \u2260は3つのUTF-8バイトでエンコードされている foreach (dchar c; a) { writefln("a[] = %x", c); // 'a[] = 2260'と表示される } dchar[] b = "\u2260"d.dup; foreach (char c; b) { writef("%x, ", c); // 'e2, 89, a0, 'と表示される }
集約は文字列リテラルであり、char 、wchar 、またはdchar 配列としてアクセスできる。
foreach (char c; "ab") { writefln("'%s'", c); } foreach (wchar w; "xy") { writefln("'%s'", w); }
これは次のように表示される。
'a' 'b' 'x' 'y'
連想配列のforeachループ
集約式が連想配列の場合、 1つまたは2つの変数を宣言できる。1つの場合、その変数は 配列の要素に順に設定された値である。変数の型が指定されている場合、 暗黙的に配列要素の型に変換されなければならない。
// 値の型はintである int[string] userAges = ["john":30, "sue":32]; foreach (ref age; userAges) { age++; } assert(userAges == ["john":31, "sue":33]);
2つの変数が宣言されている場合、最初の変数は「インデックス」 、2番目の変数は「値」と呼ばれる。インデックスは 、連想配列の添字の型と互換性がなければならない。ref にはできず、 配列要素のインデックスとして設定される。
// indexの型は文字列、値の型はdoubleである double[string] aa = ["pi":3.14, "e":2.72]; foreach (string s, double d; aa) { writefln("aa['%s'] = %g", s, d); }
配列の要素が反復処理される順序は、foreach では未定義である。 これが、連想配列のforeach_reverse が不正である理由である。
構造体とクラスに対するforeachループopApply
集約式が構造体またはクラスオブジェクトの場合、foreach は特別な opApplyforeach_reverse の動作は特別な opApplyReverse。 これらの関数はそれぞれ、以下のシグネチャを持つ必要がある。
OpApplyDeclaration: int opApply ( scope int delegate ( OpApplyParameters ) dg ) ; OpApplyParameters: OpApplyParameter OpApplyParameter, OpApplyParameters OpApplyParameter: ForeachTypeAttributesopt BasicType Declarator
dg の各OpApplyParameterは、ForeachStatement の 各 ForeachTypeと一致しなければならない 。 一致しない場合は、ForeachStatement がエラーの原因となる。
ForeachTypeAttribute は、enum であってはならない。
struct S { int opApply(scope int delegate(ref uint n) dg); } void f(S s) { foreach (ref uint i; s) i++; }
opApply およびopApplyReverse 関数は複数存在することができ、 dg の各パラメータと ForeachStatement で宣言された各 ForeachTypeを照合して 1 つが選択される。
apply関数の本体は、 集約する要素を繰り返し処理し、各要素をdg デリゲートに順次呼び出す。デリゲートの戻り値によって、 繰り返し処理を中断するかどうかを決定する。
- 結果がゼロ以外の場合、applyは反復を中止し、 その値を返さなければならない。
- 結果が 0 の場合は、反復処理を継続する。 反復処理する要素がもうない場合は、 apply は 0 を返さなければならない。
デリゲートを呼び出した結果は、ForeachStatementの ボディが、一致するBreakStatement 、ReturnStatement、または GotoStatementを実行した場合に、一致するラベルがForeachStatementの外にあるため、0以外の値になる。
例えば、2つの要素を格納するコンテナとなるクラスを考えてみよう。
class Foo { uint[] array; int opApply(scope int delegate(ref uint) dg) { foreach (e; array) { int result = dg(e); if (result) return result; } return 0; } } void main() { import std.stdio; Foo a = new Foo(); a.array = [73, 82, 2, 9]; foreach (uint u; a) { if (u < 5) break; writeln(u); } }
これは次のように表示される。
73 82
Important: opApply が例外をキャッチした場合、それらの例外が に渡されたデリゲートから発生していないことを確認する。ユーザーは、 の本文からスローされた例外がループを終了し、 の本文の外側に伝播することを期待する。 opApply foreach foreach
テンプレートopApply
opApply テンプレート化された関数とすることもでき、 これはForeachStatementに基づいてパラメータの型を推論する。 例:
struct S { import std.traits : ParameterTypeTuple; // イントロスペクション・テンプレート import std.stdio; int opApply(Dg)(scope Dg dg) if (ParameterTypeTuple!Dg.length == 2) // 2つのパラメータを持つforeach { writeln(2); return 0; } int opApply(Dg)(scope Dg dg) if (ParameterTypeTuple!Dg.length == 3) // 3つのパラメータを持つforeach { writeln(3); return 0; } } void main() { foreach (int a, int b; S()) { } // 最初のopApply関数を呼び出す foreach (int a, int b, float c; S()) { } // 次のopApply関数を呼び出す }
範囲を持つ構造体とクラスをForeachで処理する
集約式が構造体またはクラスオブジェクトであるが、opApply forforeach 、またはopApplyReverse forforeach_reverse が存在しない場合、 その場合は範囲のプリミティブで反復処理を行うことができる。foreach の場合、以下のプロパティおよびメソッドを定義する必要がある。
Property | Purpose |
---|---|
.empty | 要素がもうない場合は true を返す |
.front | 範囲の左端の要素を返す |
Method | Purpose |
---|---|
.popFront() | 範囲の左端を 右に1つ移動する |
意味:
foreach (e; range) { ... }
翻訳すると:
for (auto __r = range; !__r.empty; __r.popFront()) { auto e = __r.front; ... }同様に、xml-ph-0000@deepl.internal については、以下のプロパティと メソッドを定義する必要がある。
同様に、foreach_reverse については、以下のプロパティと メソッドを定義する必要がある。
Property | Purpose |
---|---|
.empty | 要素がもうない場合は true を返す |
.back | 範囲の右端の要素を返す |
Method | Purpose |
---|---|
.popBack() | 範囲の右端を 左に1つ移動する |
意味:
foreach_reverse (e; range) { ... }
翻訳すると:
for (auto __r = range; !__r.empty; __r.popBack()) { auto e = __r.back; ... }
リンクされたリストの例:
struct Node { int i; Node* next; } // range struct List { Node* node; bool empty() { return node == null; } ref int front() { return node.i; } void popFront() { node = node.next; } } void main() { import std.stdio; auto l = new Node(1, new Node(2, null)); auto r = List(l); foreach (e; r) { writeln(e); } }
複数の要素値
front プロパティが、 変数の数と同じ長さの値シーケンスに展開される型を返す場合、複数のループ変数が許可される。 各変数は、 シーケンス内の対応する値に割り当てられる。
struct Tuple(Types...) // TypeSeqを受け取る { Types items; // ValueSeq alias items this; // 値列に減衰する } // タプルである繰り返し要素を持つ無限範囲 struct TupleRange { enum front = Tuple!(char, bool, int)('a', true, 2); enum bool empty = false; void popFront() {} } void main() { // タプルのデストラクチャリング foreach (a, b, c; TupleRange()) { assert(a == 'a'); assert(b == true); assert(c == 2); break; } // タプル変数 foreach (tup; TupleRange()) { assert(tup[0] == 'a'); assert(tup == TupleRange.front); break; } }
関連情報: std.typecons.Tuple。
デリゲートを繰り返し処理する
ForeachAggregateがデリゲートである場合、そのデリゲートの型署名と 動作は、opApply のものと同じである。 これにより、 同じクラスまたは構造体で、さまざまな名前のループ戦略を共存させることができる。
デリゲートは、要素をその場で生成することができる。
// 2の累乗を交互に符号を変えて反復するカスタムループの実装 // foreachループ本体はdgで渡される。 int myLoop(scope int delegate(int) dg) { for (int z = 1; z < 128; z *= -2) { auto ret = dg(z); // ループ本体にブレークが含まれる場合、retは0以外になる。 if (ret != 0) return ret; } return 0; } // 反復の各値を配列に追加する。 int[] result; foreach (x; &myLoop) { result ~= x; } assert(result == [1, -2, 4, -8, 16, -32, 64, -128]);
foreach_reverse デリゲートを指定しての生成はエラーとなる。
シーケンスのforeach
集約式がシーケンスの場合、 ループ本体は静的に各要素に対して1回ずつ展開される。これは シーケンスに対する静的foreach と同じである。
反復シンボルは1つまたは2つ宣言できる。1つの場合、そのシンボルは シーケンス内の各要素の要素エイリアスとなる。
- シーケンスがTypeSeq の場合、要素エイリアスは各型に順に設定される。
- シーケンスがValueSeq の場合、要素エイリアスは 順に各値に設定される。 要素エイリアスの型が 与えられている場合、それはシーケンスの各要素の型と互換性がなければならない。 型が与えられていない場合、要素エイリアスの型は 各シーケンス要素の型と一致するが、要素によって異なる可能性がある。
2つのシンボルが宣言されている場合、最初のシンボルはインデックス変数 であり、2番目のシンボルは要素エイリアスである。インデックスは int 、uint 、long 、またはulong 型でなければならず、 ref 型にはできず、 各シーケンス要素のインデックスに設定される。
例:
import std.meta : AliasSeq; void main() { alias Seq = AliasSeq!(int, "literal", main); foreach (int i, sym; Seq) { pragma(msg, i, ": ", sym.stringof); } }
出力:
0: int 1: "literal" 2: main()
例:
void fun() { import std.meta : AliasSeq; alias values = AliasSeq!(7.4, "hi", [2,5]); foreach (sym; values) { pragma(msg, sym, " has type ", typeof(sym)); } }
出力:
7.4 has type double hi has type string [2, 5] has type int[]
foreach Ref Parameters
ref ForeachAggregateの要素を変更するために使用することができます。 これは、左辺値要素を公開するコンテナ、および 左辺値シーケンスで動作します。
uint[2] a = [7, 8]; foreach (ref u; a) { u++; } foreach (u; a) { writeln(u); }
8 9
ref 配列のインデックス変数には適用できない。
foreach の制限事項
集約自体は、foreach が要素を繰り返し処理している間は、サイズ変更、再割り当て、解放、 再割り当て、または破棄しては ならない。
int[] a = [1, 2, 3]; auto fun = { a ~= 4; }; foreach (int v; a) { // リサイズは不特定! fun(); a ~= 4; a.length += 10; // 再割り当ては未定義である! a.reserve(10); // 再割り当ては不特定である! a = null; a = [5, 6]; } a ~= 4; // OK a = null; // OKxml-ph-0000@deepl.internal
auto aa = [1: 1, 2: 2]; foreach (v; aa) { aa[3] = 3; // 不特定のリサイズ aa.rehash; // 不特定の再割り当て aa = [4: 4]; // 不特定のreassign } aa[3] = 3; // OK aa = null; // OK
Foreach Range Statement
foreach range文は、指定された範囲を繰り返し処理する。
RangeForeach: Foreach ( ForeachType ; LwrExpression .. UprExpression ) LwrExpression: Expression UprExpression: Expression ForeachRangeStatement: RangeForeach ScopeStatement
ForeachType は、明示的な型、または LwrExpressionとUprExpression から推論された共通の型を持つ変数を宣言する。 ScopeStatement はその後n 回実行され、n は UprExpression - LwrExpression の結果である。 UprExpressionがLwrExpression 以下の場合は、 ScopeStatementは実行されない。
Foreachがforeach の場合、変数は LwrExpression に設定され、各反復の最後にインクリメントされる。 Foreachがforeach_reverse の場合、変数は UprExpression に設定され、各反復の前にデクリメントされる。 LwrExpressionとUprExpressionはそれぞれ ScopeStatementが実行される回数に関わらず、 正確に1回だけ評価される。
import std.stdio; int foo() { write("foo"); return 10; } void main() { foreach (i; 0 .. foo()) { write(i); } }
foo0123456789emailemail
switch文
switch文は、switch式の値に応じて、case文の集合のいずれか1つに移動する。SwitchStatement: switch ( IfCondition ) ScopeStatement CaseStatement: case ArgumentList : ScopeStatementListopt DefaultStatement: default : ScopeStatementListopt ScopeStatementList: StatementListNoCaseNoDefault StatementListNoCaseNoDefault: StatementNoCaseNoDefault StatementNoCaseNoDefault StatementListNoCaseNoDefault StatementNoCaseNoDefault: EmptyStatement NonEmptyStatementNoCaseNoDefault ScopeBlockStatement
IfConditionの式が評価される。 式の型が enum の場合、 (再帰的に)EnumBaseTypeに変換される。 次に、型が整数型の場合、式 は 整数型への昇格を受ける。 次に、式の型は、整数型、または char 、wchar 、またはdchar の静的または動的配列でなければならない。
識別子 = 接頭辞が提供された場合、その名前で変数が宣言され、 "式"の値と型に初期化される。そのスコープは、 初期化された時点からScopeStatementの終了までとなる。
Identifier に TypeCtorsおよび/または特定の型が指定された場合、それらは 変数宣言に使用され、"式" の値からの暗黙の変換によって初期化される 。
結果の値は、 "case"式のそれぞれと比較される。一致するものがあれば、 対応する"case"文に処理が移る。
ArgumentList内のcase式は、 カンマで区切られた式のリストである。 各式は、コンパイル時の値または配列、 または実行時に初期化された定数または整数型の不変の変数として評価されなければならない。 各式は、switch式の型に暗黙的に変換できなければならない。
コンパイル時の case の値はすべて異なるものでなければならない。定数または 不変の実行時変数はすべて異なる名前でなければならない。 2つの case 式が同じ値を共有している場合、 その値を持つ最初の case 文が制御を取得する。
ScopeStatementListは新しいスコープを導入する。
一致するbreak 文は、switch BlockStatementを終了する。
switch文には、DefaultStatementが必ず1つ必要である。 case式のいずれにも一致しない場合、制御は デフォルト文に転送される。
foreach (i; 2 .. 10) { bool prime; switch (i) { case 2, 3, 5, 7: prime = true; break; default: prime = false; } writeln(i, ": ", prime); }
switchに関連するcase文およびdefault文は、 ブロック文の中にネストすることができる。必ずしも最外ブロック内に置く必要はない。 例えば、これは許可される。
switch (i) { case 1: { case 2: } i++; break; default: }
Implementation Note: コンパイラのコード生成機能は、 case 文が使用頻度順にソートされていると想定することがあり、最も使用頻度の高い ものが最初に、最も使用頻度の低いものが最後に表示される。これは、 プログラムの正確性に関しては無関係であるが、 パフォーマンスには影響する。
ケース 範囲 文
CaseRangeStatement: case FirstExp : .. case LastExp : ScopeStatementListopt FirstExp: AssignExpression LastExp: AssignExpression
CaseRangeStatementは、 FirstExpからLastExp までの範囲のケース文を列挙する省略形である。
case 1: .. case 3:
これは次のものと同じである。
case 1, 2, 3:
暗黙のフォールスルーなし
スコープステートメントリストは、空であるか、または ContinueStatement 、BreakStatement、 ReturnStatement、GotoStatement、ThrowExpression 、またはassert(0) 式で終了しなければならない。ただし、これが最後の場合を除く。
switch (i) { case 1: message ~= "one"; // エラー: 暗黙のフォールスルー case 2: // 有効: ボディは空である default: message ~= "unknown"; }
goto case; 明示的なフォールスルーに使用できる。
string message; foreach (i; 1..5) { switch (i) { default: // 有効: 'throw'で終わる throw new Exception("unknown number"); case 3: // 有効: 'break'で終わる('switch'からだけ抜け出す) message ~= "three"; break; case 4: // 有効: 'continue'で終わる(ループを続ける) message ~= "four"; continue; // カンマを付けない case 1: // 有効: 'goto'で終わる(明示的に次のケースにフォールスルーする。) message ~= ">"; goto case; case 2: // 有効: これはswitch文の最後のケースである。 message ~= "one or two"; } message ~= ", "; } writeln(message);
goto特定のケースまたはデフォルトのケース文へのジャンプもサポートしている。
文字列スイッチ
文字列は switch 式で使用できる。 例:
string name; ... switch (name) { case "fred": case "sally": ... }
コマンドラインスイッチ処理のようなアプリケーションでは、 これにより、よりわかりやすいコードとなり、エラーが発生しにくくなる。char 、wchar 、dchar の文字列が許可される。
emailemail最終的なswitch文
FinalSwitchStatement: final switch ( IfCondition ) ScopeStatement
最終的なswitch文は、switch文とほぼ同じであるが、 ただし、次の点が異なる。
- DefaultStatementは許可されない。
- CaseRangeStatementsは許可されない。
- switch式が列挙型の場合、 すべての列挙型メンバーがCaseStatementsに表示されなければならない。
- case式は、実行時に初期化された値を評価することはできない。
Continue 文
ContinueStatement: continue Identifieropt ;
continue 最も内側の囲みループ文の現在の反復を中止 し、次の反復を開始する。 囲みループが 文である場合、 そのfor インクリメント節が実行される。
string[] words = ["OK", "just", "longer", "words", "now"]; foreach (w; words) { if (w.length < 4) continue; // writeelnをスキップする writeln(w); }
出力:
just longer words
continue の後にIdentifier が続く場合、Identifier は 囲みループ文のラベルでなければならず、 そのループの次の反復が実行される。 そのような文がない場合はエラーとなる。
outer: foreach (item; list) { // 3回試す foreach (i; 0 .. 3) { if (item.buy()) continue outer; // 次の項目にスキップする log("attempt failed"); } }
間に挟まれた finally節は実行され、 同期オブジェクトは解放されます。
Note: finally 節がfinally節の を実行した場合、 継続対象には到達しない。throw
ブレークステートメント
BreakStatement: break Identifieropt ;
break 最も内側の囲みループまたは switch その次の文から実行を再開する。
const n = 55; // nの最小の因数を見つける foreach (i; 2 .. n) { writeln("Trying: ", i); if (n % i == 0) { writeln("smallest factor is ", i); break; // 探すのをやめる } } writeln("finished");
出力:
Trying: 2 Trying: 3 Trying: 4 Trying: 5 smallest factor is 5 finished
break の後にIdentifier が続く場合、Identifierは 囲みループのラベルまたはswitch 文でなければならず、その文が終了する。そのような文がない場合はエラーとなる。
// shopが閉じるまでメッセージを繰り返し表示する outer: while (true) { foreach (msg; messages) { if (shop.isClosed()) break outer; // whileループを終了する display(msg); } } display("opens at 9am");
間に挟まれた finally節は実行され、 同期オブジェクトは解放されます。
Note: finally 節がfinally節の を実行した場合、 ブレークポイントは到達しない。throw
Return文
ReturnStatement: return Expressionopt ;現在の関数を終了し、 戻り値を返す。
return 現在の関数を終了し、その 戻り値を返す。
関数が void ではない戻り値の型を指定している場合は、"式" が必須となる。 式は暗黙的に関数の戻り値の型に変換される。
関数が void を返す型を指定している場合、void 型式は許可される。 式は評価されるが、何も返されない。これはジェネリックプログラミングで有用である。
関数が実際に返す前に、scope ストレージ期間を持つオブジェクトはすべて破棄され、finally 節がすべて実行され、scope(exit) 文がすべて実行され、scope(success) 文がすべて実行され、 そして、すべての enclosing synchronization オブジェクトが解放される。
この関数は、finally 節を囲む return、goto、またはスローによってfinally 節が終了する場合、戻りません。
out の事後条件がある場合、 その事後条件は "式"が評価された後、関数が実際に 値を返す前に実行される。
int foo(int x) { return x + 3; }
Goto Statement
GotoStatement: goto Identifier ; goto default ; goto case ; goto case Expression ;
goto 識別子とラベル付けされた文に転送される。
if (foo) goto L1; x = 3; L1: x++;
2番目の形式であるgoto default; は、それを囲むSwitchStatementの最も内側のDefaultStatementに転送される。
3番目の形式、goto case; は、 最も内側のSwitchStatementの 次のCaseStatementに 転送される。
4番目の形式、goto case Expression; は、 一致する式を持つ 最も内側のSwitchStatementの CaseStatementに 転送される。
switch (x) { case 3: goto case; case 4: goto default; case 5: goto case 4; default: x = 4; break; }
間に挟まれた finally 節はすべて実行され、 間に挟まれた同期ミュートックスが解放される。
初期化をスキップするためにGotoStatementを使用することは、 不正である。
With文
with 文は、同じオブジェクトへの繰り返し参照を簡素化する方法である。
WithStatement: with ( Expression ) ScopeStatement with ( Symbol ) ScopeStatement with ( TemplateInstance ) ScopeStatement式"が以下のいずれかに評価される場合:
- クラス参照
- 構造体インスタンス
- "列挙型"のインスタンス
- 上記いずれかのポインタ
enum E { A, B } void test(E e) { with (e) // switch文に影響を与える switch (e) { case A: // E.Aは必要ない case B: default: break; } }
以下、ident がexpression の型に属する場合は、WithStatement:
with (expression)
{
...
ident;
}
意味的には以下と同等である。(auto ref tmp) { ... tmp.ident; }(expression);
式は一度だけ評価され、コピーはされないことに注意。 with文は、this またはsuper が参照するものを変更しない。
SymbolがスコープまたはTemplateInstanceの場合、 シンボルを検索する際に、対応するスコープが検索される。 例えば:
struct Foo { alias Y = int; } ... Y y; // エラー、Yは未定義 with (Foo) { Y y; // Foo.Y yと同じ; }
with オブジェクトシンボルを使用して、ローカルシンボルを同じ識別子で覆い隠すことは 許可されていない。 これは、オブジェクト宣言に新しいメンバーが追加された際に、with文が誤って壊れてしまうリスクを低減するためである 。
xml-ph-0000@deepl.internalstruct S { float x; } void main() { int x; S s; with (s) { x++; // エラー、int xの宣言が影になる } }
ネストされたWithStatementsでは、最も内側のスコープが優先される。 最も内側のスコープでシンボルが解決できない場合、 スコープ階層を順次上に遡って解決が試みられる。
import std.stdio; struct Foo { void f() { writeln("Foo.f"); } } struct Bar { void f() { writeln("Bar.f"); } } struct Baz { // f()は実装されていない } void f() { writeln("f"); } void main() { Foo foo; Bar bar; Baz baz; f(); // "f"を表示する with(foo) { f(); // "Foo.f"を表示する with(bar) { f(); // "Bar.f"を表示する with(baz) { f(); // "Bar.f"を表示する。`Baz`は`f()`を実装していない // resolutionは`with(bar)`のスコープに転送される } } with(baz) { f(); // "Foo.f"と表示する。`Baz`は`f()`を実装していない // resolutionは`with(foo)`のスコープに転送される } } with(baz) { f(); // "f"を表示する。`Baz`は`f()`を実装していない // resolutionは`main`のスコープに転送される // `f()`は`main`のスコープに実装されていないので、 // resolutionはモジュールのスコープに転送される。 } }
同期文
synchronized文は、 複数のスレッド間のアクセスを同期させるために、mutexによるロックとアンロックで文を囲む。
SynchronizedStatement: synchronized ScopeStatement synchronized ( Expression ) ScopeStatement
式を伴わないsynchronized 文では、 一度に1つのスレッドのみが グローバル・ミューテックスを作成し、同期文ごとに1つずつ使用する。 異なる同期文には、異なるグローバル・ミューテックスが使用される。
式"がある場合、それは"オブジェクト"または"インターフェースのインスタンス"のどちらかに評価されなければならない オブジェクトまたはインターフェースのインスタンスに評価されなければならず、 その場合、 そのインターフェースを実装したオブジェクトのインスタンスにキャストされる。 使用されるミューテックスは、そのオブジェクトのインスタンスに固有のものであり、 そのインスタンスを参照するすべての同期文で共有される。 同期文に到達した時点でオブジェクトのミューテックスがすでにロックされている場合、 そのミューテックスが他のコードによってアンロックされるまで、すべてのスレッドがブロックされる。
void work(); void f(Object o) { synchronized (o) work(); } void g(Object o) { synchronized (o) work(); }
f とg が異なるスレッドから呼び出された場合でも、同じ 引数を指定すると、work の呼び出しは同時に実行することはできない。 synchronized 文の(o) 部分が、1つまたは両方の関数で削除された場合、 両方のwork 呼び出しは同時に実行できる可能性がある 。なぜなら、それらは異なるミューテックスによって保護されるからだ。
ScopeStatementが例外、goto、returnで終了した場合でも、同期は解除される。
これは標準的なクリティカルセクションを実装する。
synchronized文は再帰的ロックをサポートしている。つまり、 synchronizedで囲まれた関数は、再帰的に自身を呼び出すことが許可されており、 その動作は期待通りのものとなる。つまり、 再帰の回数だけ、ミューテックスがロックされ、ロックが解除される。
同期クラスも参照。
Try文
例外処理は、try-catch-finally文を使用して実行される。
TryStatement: try ScopeStatement Catches try ScopeStatement Catches FinallyStatement try ScopeStatement FinallyStatement Catches: Catch Catch Catches Catch: catch ( CatchParameter ) NoScopeNonEmptyStatement CatchParameter: BasicType Identifieropt FinallyStatement: finally NoScopeNonEmptyStatement
CatchParameterは型 T の変数 v を宣言する。ここで、T は Throwable または Throwable から派生した型である。T がスロー式と同じ型またはスロー式の基底クラスである場合、v はスロー式によって初期化される。 例外オブジェクトが型 T または T から派生した型である場合、catch 節が実行される
型 T が与えられ、変数 v が指定されていない場合、catch 節は 依然として実行される。
CatchParameter型T1が、その後に続くCatch型T2を隠蔽する場合はエラーとなる。 すなわち、 T1がT2と同じ型である場合、またはT2の基底クラスである場合はエラーとなる。
FinallyStatementは、try ScopeStatementがgoto、break、continue、return、例外、またはフォールスルーで終了した場合でも、 常に実行される。
FinallyStatementで例外が発生し、 元の例外がキャッチされる前にキャッチされなかった場合、 Throwableの 次のメンバーを介して前の例外に連結される。 ほとんどの他のプログラミング言語とは対照的に、新しい 例外が元の例外に置き換わるわけではないことに注意。 代わりに、後続の 例外は最初の例外によって引き起こされた「二次的な被害」とみなされる。 元の例外はキャッチされなければならず、その結果、 連鎖全体が捕捉される。 xml-ph-0000@deepl.internal
クラスから派生したスローオブジェクトは Errorクラスから派生したスローされたオブジェクトは、異なる方法で処理される。 通常の連鎖メカニズムをバイパスし、最初のError をキャッチしない限り、連鎖をキャッチできない。 Error には、その後の例外のリストに加えて、 バイパスが発生した場合に元の例外(連鎖の頭)を指すポインタも含まれている。 これにより、例外の履歴全体が保持される。
import std.stdio; int main() { try { try { throw new Exception("first"); } finally { writeln("finally"); throw new Exception("second"); } } catch (Exception e) { writefln("catch %s", e.msg); } writeln("done"); return 0; }
finally catch first done
FinallyStatementは、goto、break、continue、またはreturnでは終了しない。 また、gotoで開始することもできない。
FinallyStatementには 、Catchを含めることはできない。 この制限は、将来のバージョンで緩和される可能性がある。 xml-ph-0000@deepl.internal
スコープガード文
ScopeGuardStatement: scope ( exit ) NonEmptyOrScopeBlockStatement scope ( success ) NonEmptyOrScopeBlockStatement scope ( failure ) NonEmptyOrScopeBlockStatement
ScopeGuardStatementは、現在のスコープの終了時に、NonEmptyOrScopeBlockStatement を実行する。 ScopeGuardStatementが現れた時点ではなく、。scope(exit) は、スコープが正常に終了した場合、または例外の解消により終了した場合に、NonEmptyOrScopeBlockStatement を実行する。scope(failure) は、 例外の解消によりスコープが終了した場合に、NonEmptyOrScopeBlockStatementを実行する。scope(success) は、スコープが正常に終了した場合に、NonEmptyOrScopeBlockStatementを実行する。
スコープ内に複数のScopeGuardStatementが存在する場合、それらは 出現する語彙の逆順で実行される。 スコープの終了時に任意のスコープ・インスタンスが破棄される場合、 それらの破棄は出現する語彙の逆順でScopeGuardStatementと交互に実行される 。
write("1"); { write("2"); scope(exit) write("3"); scope(exit) write("4"); write("5"); } writeln();
12543
{ scope(exit) write("1"); scope(success) write("2"); scope(exit) write("3"); scope(success) write("4"); } writeln();
4321
struct Foo { this(string s) { write(s); } ~this() { write("1"); } } try { scope(exit) write("2"); scope(success) write("3"); Foo f = Foo("0"); scope(failure) write("4"); throw new Exception("msg"); scope(exit) write("5"); scope(success) write("6"); scope(failure) write("7"); } catch (Exception e) { } writeln();
0412scope(exit) またはscope(success) 文は、 "スロー"、"goto"、"break"、"continue"、"return"で終了してはならない。また、"goto"で開始してはならない。scope(failure)文は、"return"で終了してはならない。
import std.stdio; int foo() { scope(exit) writeln("Inside foo()"); return bar(); } int bar() { writeln("Inside bar()"); return 0; } int main() { foo(); return 0; }
Inside bar() Inside foo()
C++ クラスオブジェクトの取得
多くのプラットフォームでは、C++ クラスオブジェクトのキャッチングがサポートされている。 C++ オブジェクトと D オブジェクトのキャッチングを両方とも同じTryStatement 内で行うことはできない。 Catch から抜けた時点で、C++ オブジェクトのデストラクタが実行され、そのオブジェクトに使用されていたストレージが解放される。 C++ オブジェクトは、@safe コードではキャッチングできない。
アセンブリ言語文
インラインアセンブラはasm文でサポートされている。
AsmStatement: asm FunctionAttributesopt { AsmInstructionListopt } AsmInstructionList: AsmInstruction ; AsmInstruction ; AsmInstructionList
asm文により、アセンブリ言語命令を直接使用することが可能になる。 これにより、外部のアセンブラを使用することなく、特別なCPU機能に直接アクセスすることが容易になる。 Dコンパイラは、 関数呼び出し規約、スタックのセットアップなどを処理する。
命令のフォーマットは、もちろん、 ターゲットCPUのネイティブ命令セットに大きく依存しており、 処理系定義である。 しかし、フォーマットは以下の規約に従う
- D言語で使用されているものと同じトークンを使用しなければならない。
- コメント形式は、"D言語"のコメントと一致していなければならない。
- アセンブリ言語命令は、改行ではなくセミコロンで終了する。
例えば、Intel Pentiumの場合:
int x = 3; asm { mov EAX,x; // xをロードしてレジスタEAXに入れる }
インラインアセンブラを使用してハードウェアに直接アクセスできる。
int gethardware() { asm { mov EAX, dword ptr 0x1234; } }
DからCへのトランスレータなど、一部のD実装では、インラインアセンブラは 意味をなさないため、実装する必要はない。このことを説明するために、バージョン文を使用できる。
version (D_InlineAsm_X86) { asm { ... } } else { /* ... 何らかの回避策 ... */ }
意味的に連続するAsmStatementsの間には、 コンパイラによって他の命令(レジスタの保存や復元など)を挿入してはならない。
プラグマ文
。ミックスイン文
MixinStatement: mixin ( ArgumentList ) ;
ArgumentList内の 各AssignExpressionは コンパイル時に評価され、結果は文字列として表現可能でなければならない。 結果の文字列は結合されて1つの文字列が形成される。 文字列のテキスト内容は、有効なStatementListとしてコンパイル可能でなければならず、 そのようにコンパイルされる。
import std.stdio; void main() { int i = 0; mixin(" int x = 3; for (; i < 3; i++) writeln(x + i, i); "); // OK enum s = "int y;"; mixin(s); // OK y = 4; // OK、ミックスインがyを宣言した string t = "y = 3;"; //mixin(t); // エラー、tはコンパイル時に評価できない //mixin("y =") 4; // エラー、文字列は完全な文でなければならない mixin("y =" ~ "4;"); // OK mixin("y =", 2+2, ";"); // OK }
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku