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

変数と定数

変数とは、値を一時的に記録するためのメモリ領域に名前を付けたものです。一方、定数とは、変更できない値に対して、名前を与えたものです。変数や定数に値を割り当てる操作を、代入と呼びます。

Zenでは、変数はvarを使用して、定数はconstを使用して、それぞれ定義します。変数を定義する構文は次の通りです。

const / var 変数名 :型名 バイトアライメント リンクセクション = 式;

:型名バイトアライメントリンクセクションは省略可能です。

var

varで定義された変数は、実行時ミュータブル (変更可能) であることを意味します。varで定義された変数は、次の例のように、再代入による値の更新が可能です。

examples/ch02-primitives/src/values.zen:1:8

const std = @import("std");
const ok = std.testing.ok;

test "var" {
    var i: u32 = 42;
    i  = 52;
    ok(i == 52);
}

const

constイミュータブルな (変更できない) 値を定義する際に使用します。一度constで定義した値は、不変値として利用することができます。

examples/ch02-primitives/src/values.zen:10:13

test "const" {
    const very_important_number: u32 = 42;
    ok(very_important_number == 42);
}

Zenのconstには2つの意味があります。

  1. イミュータブルな値である
  2. コンパイル時計算可能な可能性がある

全てのconstで定義された値は、1.の特性を持ちます。すなわち、一度初期化すると、その値を変更することができません。次のコードはコンパイルエラーになります。

test "assign to constant" {
    const i: u32 = 42;
    i = 52;
}
error: cannot assign to constant
    i = 52;
      ^

コンパイル時計算可能なconstは、コンパイル時にその値を利用することができます。

test "const in compile time" {
    const added: u32 = 1 + 2;
    comptime {
        ok(added == 3);
    }
}

ノート: comptimeブロックは、コンパイル時に実行するブロックです。詳しくは、12章 comptimeで説明します。

varは本質的に実行時の値であるため、このような使い方はできません。

test "var in compile time" {
    var added: u32 = 1 + 2;
    comptime {
        ok(added == 3);
    }
}

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

error[E04000]: unable to evaluate constant expression
        ok(added == 3);
                 ~

varとconstの使い分け

可能な場合は常にvarよりconstを使用することがベストプラクティスです。

constが適切に利用されることにより、まず、プログラムの可読性が向上します。constで定義された値はイミュータブルであるため、プログラマはその値が書き換わらないことを前提にプログラムを読むことができます。

constで定義された値が誤って再代入されると、コンパイラはエラーを出力し、コンパイルを中断します。constで値を定義することは、プログラマの誤りを防ぐシートベルトでもあります。

また、constで定義された値は、コンパイル時により多くの最適化ができる可能性があります。constには、コンパイル時に計算可能という意味が含まれる場合があることを説明しました。constがコンパイル時に計算可能である場合、コンパイラはその計算を可能な限りコンパイル時に完了させ、実行時の計算を減らしてくれるでしょう。

変数の初期化

Zen言語では変数を定義する時に初期化を省略することができません。

test "Incorrect Initialization" {
    var x: i32;
    x = 42;
}

上のコードは次のコンパイルエラーを出します。

$ zen test test.zen
test.zen:2:5: error: variables must be initialized
    var x: i32;

変数は任意の式で定義できます。

examples/ch02-primitives/src/values.zen:22:24

test "Correct Initialization" {
    var x: i32 = 42;
}

ここで式とは、値、変数、演算、関数の組み合わせであるため、次のように変数を初期化することも可能です。

examples/ch02-primitives/src/values.zen:26:41

test "correct initializations" {
    var a: i32 = 1;
    var b: i32 = a;
    var c: i32 = a + b;
    var d: i32 = add(a, c);
    var e: i32 = add(a, b) + c;

    ok(b == 1);
    ok(c == 2);
    ok(d == 3);
    ok(e == 4);
}

fn add(a: i32, b: i32) i32 {
    return a + b;
}

undefined

変数を定義する時、初期値が決定していない場合があります。Zen言語では変数の初期化は省略できませんため、そのような場合にはundefinedを利用します。

    var a: u32 = undefined;
    a = 42;

undefinedは配列の初期化を後回しにしたい時、特に便利です。

    var array: [100]u32 = undefined;
    // ループを回すなどして`array`を初期化する

注意: undefined は値が不定値になることに注意して下さい。ただし、デバッグの利便性のため、デバッグビルド時は、各バイトを0xAAで初期化します。また、一度undefinedで初期化すると、undefinedで初期化されているかどうかは分かりません

型推論

ここまでのサンプルコードでは、初期化時に全ての変数の型を明示的に記述しました。しかし、Zenには型推論の機能が備わっており、Zenコンパイラが推論可能な場所では、型の記述を省略できます。

次の例では、初期化時にbの型を明示していません。

examples/ch02-primitives/src/values.zen:43:48

test "type inference" {
    var a: i32 = 1;
    // `b`の型は`a`の型から`i32`に推論される
    var b = a;
    ok(@typeOf(b) == i32);
}

しかし、bの型は、aの型から推論可能であるため、bの型はi32になります。@typeOfは引数として与えた値の型を返す組込み関数です。

☰ 人の生きた証は永遠に残るよう ☰
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.