ページへ戻る

− Links

 印刷 

Articles​/D言語入門講座​/第13章 - その他の文法1 :: D言語友の会

wiki:Articles/D言語入門講座/第13章 - その他の文法1


ページ内コンテンツ
  • #01 - with文
    • 今回は…
    • 今回のミソ
    • サンプルコード
    • 実行結果
    • まとめ
  • #02 - switch文
    • 今回は…
    • 今回のミソ
    • サンプルコード
    • 実行結果
    • まとめ
  • #03 - foreach文
    • 今回は…
    • 今回のミソ
    • サンプルコード
    • 実行結果
    • まとめ
  • #04 - goto文
    • 今回は…
    • 今回のミソ
    • サンプルコード
    • 実行結果
    • まとめ
  • #summary - まとめ
    • 第13章のミソ
    • 宿題
    • コメント
  • 投票とコメント

#01 - with文 anchor.png[4]

Page Top

今回は… anchor.png[5]

以前構造体を使用しましたね。
今回は、その構造体のメンバを一気にまとめて変更したい場合なんかに有効な文法 with 文を紹介します。
この文法はC言語には存在しません。
このキーワードが存在する言語としては、JavascriptをはじめとするECMAScript系の言語なんかが有名ですね。

Page Top

今回のミソ anchor.png[6]

  • 使い方は以下の通り
    with(構造体の変数)
    {
        ...処理...
    }
  • 上の...処理...のところでは、構造体の変数の名前を使用しなくてもその構造体の中身にアクセスできる
Page Top

サンプルコード anchor.png[7]

filesample1301p.d[8]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
 
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
 
 
-
-
!
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
|
!
import std.stdio;
 
// 構造体 MyStruct を定義
struct MyStruct
{
    // int型のメンバ変数aを定義
    int a;
    // int型のメンバ変数bを定義
    int b;
    // int型のメンバ変数cを定義
    int c;
    // int型のメンバ変数xを定義
    int x;
    // int型のメンバ変数yを定義
    int y;
    // int型のメンバ変数zを定義
    int z;
}
 
int main(char[][] args)
{
    // 構造体MyStruct型の変数 mystruct を宣言
    MyStruct mystruct;
    // mystructを初期化します
    with (mystruct)
    {
        // mystruct.a = 10; とおなじ
        a = 10;
        // mystruct.b = 20; とおなじ
        b = 20;
        // mystruct.c = 30; とおなじ
        c = 30;
        // mystruct.x = mystruct.a*4; とおなじ
        x = a*4;
        // mystruct.y = mystruct.b*5; とおなじ
        y = b*5;
        // mystruct.z = mystruct.c*6; とおなじ
        z = c*6;
    }
    // with文は初期化以外にも利用できます
    with (mystruct)
    {
        // writeln("a = %d", mystruct.a); とおなじ
        writefln("a = %d", a);
        // writeln("b = %d", mystruct.b); とおなじ
        writefln("b = %d", b);
        // writeln("c = %d", mystruct.c); とおなじ
        writefln("c = %d", c);
        // writeln("x = %d", mystruct.x); とおなじ
        writefln("x = %d", x);
        // writeln("y = %d", mystruct.y); とおなじ
        writefln("y = %d", y);
        // writeln("z = %d", mystruct.z); とおなじ
        writefln("z = %d", z);
    }
    return 0;
}
+  Tango用はこちら
filesample1301t.d[9]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
 
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
 
 
-
-
!
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
-
!
-
-
!
-
!
-
!
-
!
-
!
-
!
!
|
!
import tango.io.Stdout;
 
// 構造体 MyStruct を定義
struct MyStruct
{
    // int型のメンバ変数aを定義
    int a;
    // int型のメンバ変数bを定義
    int b;
    // int型のメンバ変数cを定義
    int c;
    // int型のメンバ変数xを定義
    int x;
    // int型のメンバ変数yを定義
    int y;
    // int型のメンバ変数zを定義
    int z;
}
 
int main(char[][] args)
{
    // 構造体MyStruct型の変数 mystruct を宣言
    MyStruct mystruct;
    // mystructを初期化します
    with (mystruct)
    {
        // mystruct.a = 10; とおなじ
        a = 10;
        // mystruct.b = 20; とおなじ
        b = 20;
        // mystruct.c = 30; とおなじ
        c = 30;
        // mystruct.x = mystruct.a*4; とおなじ
        x = a*4;
        // mystruct.y = mystruct.b*5; とおなじ
        y = b*5;
        // mystruct.z = mystruct.c*6; とおなじ
        z = c*6;
    }
    // with文は初期化以外にも利用できます
    with (mystruct)
    {
        // Stdout.formatln("a = {}", mystruct.a); とおなじ
        Stdout.formatln("a = {}", a);
        // Stdout.formatln("b = {}", mystruct.b); とおなじ
        Stdout.formatln("b = {}", b);
        // Stdout.formatln("c = {}", mystruct.c); とおなじ
        Stdout.formatln("c = {}", c);
        // Stdout.formatln("x = {}", mystruct.x); とおなじ
        Stdout.formatln("x = {}", x);
        // Stdout.formatln("y = {}", mystruct.y); とおなじ
        Stdout.formatln("y = {}", y);
        // Stdout.formatln("z = {}", mystruct.z); とおなじ
        Stdout.formatln("z = {}", z);
    }
    return 0;
}
Page Top

実行結果 anchor.png[10]

1
2
3
4
5
6
7
$ dmd -run sample1301p.d
a = 10
b = 20
c = 30
x = 40
y = 100
z = 180
Page Top

まとめ anchor.png[11]

というわけで、with文。使いどころが難しいかもしれませんが、使えるところでは使えますよ。
具体的には、もっと先でやるclassのメソッドやプロパティと呼ばれるものにもアクセスできたりするので、オブジェクト指向向きの文法と言えます。
今のところは構造体をもっと使いやすくする方法という感じで覚えておくといいかもしれませんね。

Page Top

#02 - switch文 anchor.png[12]

Page Top

今回は… anchor.png[13]

switch文を説明します。これは条件分岐の一種の文法です。なので、ifelseを使用しても同じ挙動が期待できる場合が多いです。
ただ、switch文は、C言語にも存在する割とベーシックな文法です。いろいろなところで見るかと思いますので、一応触れておきます。
if文で十分に代用可能な文法ですが、もしかしたら最適化が計れて速度的に有利になることがあるかもしれません。あまり期待はできないけど。
特に、Windowsプログラミングにおけるウィンドウプロシージャの内部処理には頻繁に登場する文法です。
筆者的にはswtich文の文法が若干気味悪く感じるのでifを使うことのほうが多いですが…

Page Top

今回のミソ anchor.png[14]

  • 多条件分岐、switch文を使うには以下のようにする。
    switch (式)
    {
    case(値1):
        式が値1だったときの処理;
        break;
    case(値2):
        式が値2だったときの処理;
        break;
    default:
        デフォルトの処理;
    }
  • 分岐には case文とdefault文を使う
  • 式 が case 値: の値になった場合、そこへ飛ぶ。
  • 式 が複数ある case 値: のどの値にも当てはまらなかった場合、 default: へ飛ぶ。
  • 終わる場合は break を使う。
  • breakしないcase文はその次のcase文もつづけて実行する。
Page Top

サンプルコード anchor.png[15]

filesample1302p.d[16]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
-
!
-
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
!
!
 
 
-
-
!
-
!
-
|
!
-
!
-
!
|
!
import std.stdio;
 
// x の値によって処理の内容を振り分ける関数 Switching の定義
void Switching(int x)
{
    switch(x)
    {
    case(0):
        writeln("零の場合");
        break;
    case 1: // ()でくくらなくても大丈夫だけど…
        writeln("一の場合");
        break;
    case(2):
        writeln("二の場合。breakがないので次のも実行。");
    case(3):
        writeln("三の場合");
        break;
    default: // デフォルトの挙動はできる限りあったほうが良い。
        writeln("デフォルトの挙動");
    }
}
 
int main(char[][] args)
{
    // 零の場合
    Switching(0);
    // 一の場合
    Switching(1);
    // 二の場合。breakがないので次のも実行。
    // 三の場合
    Switching(2);
    // 三の場合
    Switching(3);
    // デフォルトの挙動
    Switching(4);
    return 0;
}
+  Tango用はこちら
filesample1302t.d[17]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
-
!
-
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
!
!
 
 
-
-
!
-
!
-
|
!
-
!
-
!
|
!
import tango.io.Stdout;
 
// x の値によって処理の内容を振り分ける関数 Switching の定義
void Switching(int x)
{
    switch(x)
    {
    case(0):
        Stdout("零の場合").newline;
        break;
    case 1: // ()でくくらなくても大丈夫だけど…
        Stdout("一の場合").newline;
        break;
    case(2):
        Stdout("二の場合。breakがないので次のも実行。").newline;
    case(3):
        Stdout("三の場合").newline;
        break;
    default: // デフォルトの挙動はできる限りあったほうが良い。
        Stdout("デフォルトの挙動").newline;
    }
}
 
int main(char[][] args)
{
    // 零の場合
    Switching(0);
    // 一の場合
    Switching(1);
    // 二の場合。breakがないので次のも実行。
    // 三の場合
    Switching(2);
    // 三の場合
    Switching(3);
    // デフォルトの挙動
    Switching(4);
    return 0;
}
Page Top

実行結果 anchor.png[18]

1
2
3
4
5
6
7
$ dmd -run sample1302p.d
零の場合
一の場合
二の場合。breakがないので次のも実行。
三の場合
三の場合
デフォルトの挙動
Page Top

まとめ anchor.png[19]

switch文は使いどころが限られます。正直ifのほうが使いやすかったりもします。 ただ、何度も

if (x == 1)
{
    ... 
}
else if (x == 2)
{
    ... 
}
else if (x == 3)
{
    ... 
}
else
{
    ... 
}

といったように x == ... のような似たような条件を繰り返して使いたくない場合に使うといいと思います。 ………ほかにswtichの有用性について知っている方がいたら教えてください

Page Top

#03 - foreach文 anchor.png[20]

Page Top

今回は… anchor.png[21]

foreach文です。foreach文は繰り返し構文の一種です。
foreach文はC言語では使用できません。利用できる言語は[[Wikipedia:Foreach文]]を見るといいでしょう
foreach文は配列などに関して、すべての要素に対して処理を行う場合に非常に便利な文です。
配列など、というのは他にも使えるものがあって、ある特定の条件を満たした構造体、クラス、連想配列にも使うことができるます。これらはのちの講座で解説します。

Page Top

今回のミソ anchor.png[22]

  • foreach文を使うには次のようにします
    foreach (val; list)
    {
        ...
    }
  • vallistの中のデータが一つずつ入る
  • foreach文を使うと、breakするかすべての要素をめぐるまでループする
  • valは型名などを使ってもよいが、使わなくても勝手に推測してくれる
  • リストの要素の変更をする場合は次のよう ref を使います。
    foreach (ref val; list)
    {
        ...
    }
Page Top

サンプルコード anchor.png[23]

filesample1303p.d[24]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
 
 
-
-
!
|
|
|
|
|
-
!
-
|
!
-
!
-
-
!
!
-
!
-
|
!
-
|
!
-
|
!
|
!
import std.stdio;
 
int main(char[][] args)
{
    // 配列の変数 list を定義して初期化しておく
    int[5] list;
    list[0] = 4;
    list[1] = 3;
    list[2] = 2;
    list[3] = 5;
    list[4] = 7;
    // すべての要素を表示します
    foreach (val; list)
    {
        writeln(val);
    }
    // ref をつけると要素の変更ができます。
    foreach (ref val; list)
    {
        // すべての要素を8にする
        val = 8;
    }
    // i には添え字が入ります。
    foreach (i, ref val; list)
    {
        val = i + val + list[ (i+4) % list.length ];
    }
    // すべての要素を表示します
    // 変更された内容を確認してください。
    foreach (i, val; list)
    {
        writefln("val[%d] = %d", i, val);
    }
    return 0;
}
+  Tango用はこちら
filesample1303t.d[25]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
 
 
-
-
!
|
|
|
|
|
-
!
-
|
!
-
!
-
-
!
!
-
!
-
|
!
-
|
!
-
|
!
|
!
import tango.io.Stdout;
 
int main(char[][] args)
{
    // 配列の変数 list を定義して初期化しておく
    int[5] list;
    list[0] = 4;
    list[1] = 3;
    list[2] = 2;
    list[3] = 5;
    list[4] = 7;
    // すべての要素を表示します
    foreach (val; list)
    {
        Stdout(val).newline;
    }
    // ref をつけると要素の変更ができます。
    foreach (ref val; list)
    {
        // すべての要素を8にする
        val = 8;
    }
    // i には添え字が入ります。
    foreach (i,ref val; list)
    {
        val = i + val + list[ (i+4) % list.length ];
    }
    // すべての要素を表示します
    // 変更された内容を確認してください。
    foreach (i, val; list)
    {
        Stdout.formatln("val[{}] = {}", i, val);
    }
    return 0;
}
Page Top

実行結果 anchor.png[26]

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ dmd -run sample1303p.d
4
3
2
5
7
val[0] = 16
val[1] = 25
val[2] = 35
val[3] = 46
val[4] = 58
Page Top

まとめ anchor.png[27]

さてforeachもswitch同様forやwhile文で同じものが記述できます。
では、いったい何がいいのか。
まず、その一つとして、このような配列をすべて廻るようなものでは添え字が配列に対して大きすぎる値を入力することが少なくなります。バグの原因を減らせます。
ほかにも、簡単な記述が可能ということや、限定された使い方であるため最大の効率化が期待できたりもします。*1
D言語では、人によってはfor文以上に使うことが多くなるかもしれませんね。

Page Top

#04 - goto文 anchor.png[28]

Page Top

今回は… anchor.png[29]

goto文についてお話します。
この構文はまずほとんど使うことはないと思います。
なので軽めに。

goto文は古くはアセンブリの時代から非常に愛されてきた文です。
forwhileなどの繰り返し文にも代用として使えますし、switch文の代用にもすることができる、非常に強力な文です(笑)。

ちなみに、なんでほとんど使わないかというと、プログラムが煩雑になりやすいからです。
あちこちにジャンプして、流れがつかみにくいプログラム、それを、スパゲッティプログラムと言います。
そんなコードを書かないように注意しましょう。

Page Top

今回のミソ anchor.png[30]

  • LABEL: としてラベルを定義
  • ラベルの定義は";"(セミコロン)じゃなく":"(コロン)なので注意。
  • goto LABEL; としてラベルにジャンプする
  • gotoを使うとプログラムコードが煩雑になりやすいため、使いどころを考えなければならない
Page Top

サンプルコード anchor.png[31]

filesample1304p.d[32]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
-
|
|
|
|
|
!
 
-
|
-
!
|
-
!
|
|
-
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
import std.stdio;
 
// 注意!この goto の使用例は非常に悪い使用例です。
// 流れが無駄に複雑になっています。
// このように、goto文は下手に使うとすぐに、
// ごちゃごちゃした読みにくい、保守のしにくい、汚い、臭い、しょっぱい
// コードになってしまいます。
// 用法と容量を正しく守った上、ご使用ください。
 
int main(char[][] args)
{
    
    // この後出てくるラベル5にジャンプ
    goto L_LABEL5;
    
    // ラベルを定義
L_LABEL1:
    writeln("L_LABEL1");
    
    // ラベル3にジャンプ
    goto L_LABEL3;
    
L_LABEL2:
    writeln("L_LABEL2");
    goto L_LABEL1;
L_LABEL3:
    writeln("L_LABEL3");
    goto L_END;
L_LABEL4:
    writeln("L_LABEL4");
    goto L_LABEL2;
L_LABEL5:
    writeln("L_LABEL5");
    goto L_LABEL4;
L_END:
    writeln("L_END");
    return 0;
}
+  Tango用はこちら
filesample1304t.d[33]
Everything is expanded.Everything is shortened.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 
 
-
|
|
|
|
|
!
 
-
|
-
!
|
-
!
|
|
-
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
import tango.io.Stdout;
 
// 注意!この goto の使用例は非常に悪い使用例です。
// 流れが無駄に複雑になっています。
// このように、goto文は下手に使うとすぐに、
// ごちゃごちゃした読みにくい、保守のしにくい、汚い、臭い、しょっぱい
// コードになってしまいます。
// 用法と容量を正しく守った上、ご使用ください。
 
int main(char[][] args)
{
    
    // この後出てくるラベル5にジャンプ
    goto L_LABEL5;
    
    // ラベルを定義
L_LABEL1:
    Stdout("L_LABEL1").newline;
    
    // ラベル3にジャンプ
    goto L_LABEL3;
    
L_LABEL2:
    Stdout("L_LABEL2").newline;
    goto L_LABEL1;
L_LABEL3:
    Stdout("L_LABEL3").newline;
    goto L_END;
L_LABEL4:
    Stdout("L_LABEL4").newline;
    goto L_LABEL2;
L_LABEL5:
    Stdout("L_LABEL5").newline;
    goto L_LABEL4;
L_END:
    Stdout("L_END").newline;
    return 0;
}
Page Top

実行結果 anchor.png[34]

1
2
3
4
5
6
7
$ dmd -run sample1304p.d
L_LABEL5
L_LABEL4
L_LABEL2
L_LABEL1
L_LABEL3
L_END
Page Top

まとめ anchor.png[35]

サンプルコードではgoto単体として使っていましたが、ふつうはif文などと組み合わせて使用します。
もし値が1ならLABEL1にジャンプする…と。このように使うのですが、これはswitchでできますし、関数なんかにまとめたほうがよっぽど読みやすいコードになります。
できる限りgoto文をなくすようにしましょう。

Page Top

#summary - まとめ anchor.png[36]

Page Top

第13章のミソ anchor.png[37]

  • with文について
    • with文の使い方は以下の通り
      with(構造体の変数)
      {
          ...処理...
      }
    • 上の...処理...のところでは、構造体の変数の名前を使用しなくてもその構造体の中身にアクセスできる
  • switch分について
    • 多条件分岐、switch文を使うには以下のようにする。
      switch (式)
      {
      case(値1):
          式が値1だったときの処理;
          break;
      case(値2):
          式が値2だったときの処理;
          break;
      default:
          デフォルトの処理;
      }
    • 分岐には case文とdefault文を使う
    • 式 が case 値: の値になった場合、そこへ飛ぶ。
    • 式 が複数ある case 値: のどの値にも当てはまらなかった場合、 default: へ飛ぶ。
    • 終わる場合は break を使う。
    • breakしないcase文はその次のcase文もつづけて実行する。
  • foreach文について
    • foreach文を使うには次のようにします
      foreach (val; list)
      {
          ...
      }
    • vallistの中のデータが一つずつ入る
    • foreach文を使うと、breakするかすべての要素をめぐるまでループする
    • valは型名などを使ってもよいが、使わなくても勝手に推測してくれる
    • リストの要素の変更をする場合は次のよう ref を使います。
      foreach (ref val; list)
      {
          ...
      }
  • goto文について
    • LABEL: としてラベルを定義
    • ラベルの定義は";"(セミコロン)じゃなく":"(コロン)なので注意。
    • goto LABEL; としてラベルにジャンプする
    • gotoを使うとプログラムコードが煩雑になりやすいため、使いどころを考えなければならない
Page Top

宿題 anchor.png[38]

この章で紹介したサンプルコードをすべてwith,switch,foreach,for,while文を使わずに期待される動作を全く同じになるように記述してみてください。

Page Top

コメント anchor.png[39]

今回の宿題はつまり、繰返しや分岐を使わないで、どのようにプログラムが流れているかを確認するためのものです。
goto文を使って再現してみるのもいいかもしれません。あまり役には立たないと思うけど。 そんなわけで、宿題できたら、あるいは予想ついたら次にいきましょう~


Page Top

投票とコメント anchor.png[40]

選択肢 投票
大変参考になった2  
参考になった1  
あまり参考にならなかった0  
まったく参考にならなかった0  

最新の10件を表示しています。 コメントページを参照[41]

  • D2.0ではどうなのか知らないのですが、D1.0ではswitch文のcaseを括弧でくくると、case(1,2,3)というふうに複数の条件を指定したときに上手く動かないので、危ない習慣だと思います。 -- ゲスト編集[42] 2012-03-19 (月) 22:37:40
  • とりあえずD2では意図したとおりに動作しますね。ちなみにcase(1,2,3)とかくのは、case(3)と書くのと同じです。とはいえ、カッコつきで書くのはあまり一般的ではありませんし、記事を書いた当初からswitch文も事情が変わってきたので、そのうち書き直します。 -- SHOO編集[43] 2012-03-22 (木) 18:22:30
お名前:

*1 これもやはりあまり期待できませんが。

Last-modified: 2016-01-04 (月) 09:51:03 (JST) (445d) by ゲスト