オフィスアワーがそろそろ始まるよ!()

分岐

実行フローを制御するifについて説明します。

if

最も単純なif分岐の構文は、if (条件式) { 条件式がtrueの時の処理 }です。条件式の部分にはブール値、オプション型、エラー共用体、のいずれかが記述できます。{}で囲われた部分をブロックと呼びます。

条件式にブール値を使用するコードは次の通りです。条件式がtrueの場合のみ、ブロックが実行されます。

examples/ch04-basic-syntax/src/if.zen:4:10

test "the simplest if" {
    var x: u32 = 1;
    if (x == 1) {
        // `x == 1`の場合のみ実行される
        ok(true);
    }
}

さらに、ifの条件式がfalseの場合、else if (条件式) { ... }で次の候補の条件を指定することが可能です。全ての条件式がfalseの場合に、その他全ての場合の処理をelse { ... }で記述することもできます。

examples/ch04-basic-syntax/src/if.zen:12:24

test "else if" {
    var x: u32 = 2;
    if (x == 1) {
        // `x == 1`の場合のみ実行される
        ok(false);
    } else if (x == 2) {
        // `x == 2`の場合のみ実行される
        ok(true);
    } else {
        // `x == 1`でも`x == 2`でもない場合のみ実行される
        ok(false);
    }
}

ブロック内の文が1文だけの場合、中括弧 ({}) を省略することが可能です。コーディングスタイルとしては、このような場合も中括弧を書くことをお勧めします。

examples/ch04-basic-syntax/src/if.zen:26:29

test "if without {}" {
    if (true)
        ok(true);
}

条件式にオプション型の値を使用することができます。この場合、オプション型の値がnullなければifのブロックが実行されます。オプション型の値を条件式に使用する場合、アンラップした値をキャプチャしなければなりません。そのため、基本構文はif (オプション型の値) |アンラップした値| { ... }となります。

examples/ch04-basic-syntax/src/if.zen:31:43

test "if optional" {
    var x: ?u32 = 42;
    if (x) |value| {
        // このブロックは実行される
        ok(value == 42);
    }

    var y: ?u32 = null;
    if (y) |value| {
        // このブロックは実行されない
        ok(false);
    }
}

条件式がオプション型の値の場合、elseで値がnullの場合の処理を記述することができます。

examples/ch04-basic-syntax/src/if.zen:45:53

test "else optional" {
    var x: ?u32 = null;
    if (x) |value| {
        ok(false);
    } else {
        // `value` == `null`の場合
        ok(true);
    }
}

アンラップした値は、ifブロック内でのみ使用できます。

examples/ch04-basic-syntax/src/if.zen:55:67

test "scope of unwrapped value" {
    var x: ?u32 = 42;
    if (x) |value| {
        // `value`はこのブロック内でのみ有効
        ok(value == 42);
    } else {
        // Compile error
        // ok(value == 42);
    }

    // Compile error
    // ok(value == 42);
}

条件式にエラー共用体の値を使用することができます。この場合、エラー共用体の値がエラー型でなければifのブロックが実行されます。エラー共用体の値を条件式に使用する場合、アンラップした値をキャプチャしなければなりません。加えて、エラー型の値をキャプチャして処理するelseを記述する必要があります。そのため、基本構文はif (エラー共用体の値) |アンラップした値| { ... } else |エラー型の値| |err| { ... }となります。

examples/ch04-basic-syntax/src/if.zen:69:80

test "if error union" {
    var x: anyerror!u32 = 42;
    if (x) |value| {
        // `x`がエラーでない場合の処理
        // アンラップした値を`value`としてキャプチャ
        ok(value == 42);
    } else |err| {
        // `x`がエラーの場合の処理
        // エラー型の値を`err`としてキャプチャ
        ok(false);
    }
}

if (条件式) 代入式のように、ブロックの代わりに代入式をifの本体に書くことができます。

examples/ch04-basic-syntax/src/if_expression.zen:41:46

test "if assign expression" {
    var x: u32 = 42;
    if (x == 42) x += 1;

    ok(x == 43);
}

if式

Zenではifです。すなわち、値を返すことが可能です。次のコードは、実用上意味を持ちませんが、ifが式であることを表しています。

examples/ch04-basic-syntax/src/if_expression.zen:4:8

test "concept of if expression" {
    // `x`に`if`の結果を代入する
    var x = if (true) { };
    ok(@typeOf(x) == void);
}

if (true) { }xに代入しています。ブロック{ ... }が返す値は何もないことを表すvoid型の値です。

もう少し有用な例を見てみましょう。

examples/ch04-basic-syntax/src/if_expression.zen:10:15

test "if expression" {
    var x: u32 = 41;
    var y = if (x == 42) u32(1) else u32(0);

    ok(y == 0);
}

上のコードでは、xの値が42であればy1が、それ以外であればy0が代入されます。

if式の値を利用する場合、ifブロックの値とelse if / elseブロックの値とが、全て同じ型でなければなりません。特に、elseを省略した場合に、void型の値を返すelse {}があるかのように振る舞います。

test "if expression without else" {
    var x: u32 = 42;
    var y: u32 = if (x == 42) u32(1);
}

上のコードは、次のコンパイルエラーになります。

error[E02047]: unable to convert 'void' to 'u32'
    var y: u32 = if (x == 42) u32(1);
                 ~

このエラーは、ifブロックがu32を返しているにも関わらず、elseブロックがvoidを返していることを意味しています。u32の値を返すelseブロックを書くと、このエラーは解消できます。

{}を使ったブロックで複数行の処理を書き、その結果をifの値にすることもできます。そのような場合には、ラベル付きブロックを使います。

ラベル付きブロック

通常ブロック ({}) の値はvoidになりますが、ラベルを付けて、値をbreakすることで、その値を返すことができます。

examples/ch04-basic-syntax/src/if_expression.zen:17:29

test "concept of labeled block" {
    // `result: {}`はラベル付きブロック
    var x: u32 = result: {
        // 複数行の文を記述する
        const y = 40;
        const z = 2;
        // ラベルを指定して式を`break`する
        break: result (y + z);
    };

    // `break`された式の値が格納されている
    ok(x == 42);
}

ラベル付きブロックを使ったif式

ラベル付きブロックを利用することで、複雑な計算が必要な値をif式の値として返すことができます。

examples/ch04-basic-syntax/src/if_expression.zen:31:39

test "labeled if" {
    var x: u32 = 42;
    var y = if (x == 42) if_block: {
        var z = x * 2;
        break :if_block z;
    } else u32(0);

    ok(y == 84);
}

☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2019 connectFree Corporation. All rights reserved.
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.