課題11: コンパイル時計算の実践

マンダトリー要件 (80点)

Part 1: コンパイル時ルックアップテーブル (20点)

コンパイル時に計算されるルックアップテーブルを実装してください。

ファイル: part1/lookup_tables.zig

const std = @import("std");

// TODO: コンパイル時にフィボナッチ数列を生成
fn generateFibonacci(comptime n: usize) [n]u64 {
    // 実装
}

// TODO: コンパイル時に階乗のテーブルを生成
fn generateFactorials(comptime n: usize) [n]u64 {
    // 実装
}

// TODO: コンパイル時に素数のリストを生成
fn generatePrimes(comptime max: u32) []const u32 {
    comptime {
        var primes: [max]u32 = undefined;
        var count: usize = 0;
        // 実装
        return primes[0..count];
    }
}

// TODO: コンパイル時にべき乗のテーブルを生成
fn generatePowerTable(comptime base: u32, comptime count: usize) [count]u64 {
    // 実装: base^0, base^1, base^2, ...
}

pub fn main() void {
    std.debug.print("=== Lookup Tables ===\n\n", .{});

    // フィボナッチ数列
    comptime var fibs = generateFibonacci(15);
    std.debug.print("Fibonacci numbers:\n", .{});
    for (fibs, 0..) |fib, i| {
        std.debug.print("  F({}) = {}\n", .{i, fib});
    }

    // 階乗
    comptime var facts = generateFactorials(10);
    std.debug.print("\nFactorials:\n", .{});
    for (facts, 0..) |fact, i| {
        std.debug.print("  {}! = {}\n", .{i, fact});
    }

    // 素数
    comptime var primes = generatePrimes(50);
    std.debug.print("\nPrimes up to 50:\n  ", .{});
    for (primes) |prime| {
        std.debug.print("{} ", .{prime});
    }
    std.debug.print("\n", .{});

    // べき乗テーブル
    comptime var powers = generatePowerTable(2, 16);
    std.debug.print("\nPowers of 2:\n", .{});
    for (powers, 0..) |power, i| {
        std.debug.print("  2^{} = {}\n", .{i, power});
    }
}

Part 2: ジェネリック型生成 (20点)

comptime型パラメータを使って汎用的なデータ構造を実装してください。

ファイル: part2/generic_types.zig

const std = @import("std");

// TODO: ジェネリックなPair型
fn Pair(comptime T: type, comptime U: type) type {
    return struct {
        first: T,
        second: U,

        const Self = @This();

        pub fn init(first: T, second: U) Self {
            // 実装
        }

        pub fn swap(self: Self) Pair(U, T) {
            // 実装
        }

        pub fn map(
            self: Self,
            comptime R: type,
            comptime S: type,
            f: fn(T) R,
            g: fn(U) S,
        ) Pair(R, S) {
            // 実装
        }
    };
}

// TODO: ジェネリックなTuple型(可変長)
fn Tuple(comptime types: []const type) type {
    return struct {
        // 実装のヒント: @Typeを使って動的にフィールドを生成
    };
}

// TODO: ジェネリックなArray型(固定長)
fn Array(comptime T: type, comptime size: usize) type {
    return struct {
        data: [size]T,

        const Self = @This();

        pub fn init(default_value: T) Self {
            // 実装
        }

        pub fn get(self: Self, index: usize) ?T {
            // 実装
        }

        pub fn set(self: *Self, index: usize, value: T) bool {
            // 実装
        }

        pub fn map(self: Self, comptime R: type, func: fn(T) R) Array(R, size) {
            // 実装
        }

        pub fn filter(self: Self, predicate: fn(T) bool) []const T {
            // 実装のヒント: comptime配列を使用
        }
    };
}

// TODO: ジェネリックなOption型
fn Option(comptime T: type) type {
    return union(enum) {
        Some: T,
        None,

        const Self = @This();

        pub fn isSome(self: Self) bool {
            // 実装
        }

        pub fn isNone(self: Self) bool {
            // 実装
        }

        pub fn unwrap(self: Self) T {
            // 実装
        }

        pub fn unwrapOr(self: Self, default: T) T {
            // 実装
        }

        pub fn map(self: Self, comptime R: type, func: fn(T) R) Option(R) {
            // 実装
        }

        pub fn andThen(self: Self, comptime R: type, func: fn(T) Option(R)) Option(R) {
            // 実装
        }
    };
}

pub fn main() void {
    std.debug.print("=== Generic Types ===\n\n", .{});

    // Pair
    const pair = Pair(i32, []const u8).init(42, "answer");
    std.debug.print("Pair: ({}, {s})\n", .{pair.first, pair.second});

    // Array
    var arr = Array(i32, 5).init(0);
    _ = arr.set(0, 10);
    _ = arr.set(1, 20);
    _ = arr.set(2, 30);
    std.debug.print("\nArray[1]: {?}\n", .{arr.get(1)});

    // Option
    const some = Option(i32){ .Some = 42 };
    const none = Option(i32).None;
    std.debug.print("\nOption some: {}\n", .{some.unwrapOr(0)});
    std.debug.print("Option none: {}\n", .{none.unwrapOr(0)});
}

Part 3: コンパイル時リフレクション (20点)

型情報を使ったメタプログラミングを実装してください。

ファイル: part3/reflection.zig

const std = @import("std");

const Person = struct {
    name: []const u8,
    age: u32,
    email: []const u8,
    active: bool,
};

// TODO: 構造体のフィールド数を取得
fn fieldCount(comptime T: type) usize {
    // 実装
}

// TODO: 構造体のフィールド名を配列で取得
fn fieldNames(comptime T: type) []const []const u8 {
    comptime {
        const fields = std.meta.fields(T);
        var names: [fields.len][]const u8 = undefined;
        // 実装
        return &names;
    }
}

// TODO: 構造体のフィールド型を配列で取得
fn fieldTypes(comptime T: type) []const type {
    comptime {
        const fields = std.meta.fields(T);
        var types: [fields.len]type = undefined;
        // 実装
        return &types;
    }
}

// TODO: 特定のフィールドが存在するか確認
fn hasField(comptime T: type, comptime field_name: []const u8) bool {
    // 実装
}

// TODO: 特定の型のフィールドを持つか確認
fn hasFieldOfType(comptime T: type, comptime FieldType: type) bool {
    // 実装
}

// TODO: 構造体の情報を表示
fn printStructInfo(comptime T: type) void {
    std.debug.print("Struct: {}\n", .{T});
    std.debug.print("Size: {} bytes\n", .{@sizeOf(T)});
    std.debug.print("Alignment: {} bytes\n", .{@alignOf(T)});
    std.debug.print("Fields: {}\n", .{fieldCount(T)});

    const fields = std.meta.fields(T);
    inline for (fields) |field| {
        std.debug.print("  - {s}: {} ({} bytes)\n",
            .{field.name, field.type, @sizeOf(field.type)});
    }
}

// TODO: 列挙型の値を配列で取得
fn enumValues(comptime T: type) []const T {
    comptime {
        const info = @typeInfo(T);
        if (info != .Enum) {
            @compileError("enumValues requires enum type");
        }
        var values: [info.Enum.fields.len]T = undefined;
        // 実装
        return &values;
    }
}

pub fn main() void {
    std.debug.print("=== Reflection ===\n\n", .{});

    printStructInfo(Person);

    std.debug.print("\nField count: {}\n", .{fieldCount(Person)});
    std.debug.print("Has 'name' field: {}\n", .{hasField(Person, "name")});
    std.debug.print("Has 'address' field: {}\n", .{hasField(Person, "address")});
    std.debug.print("Has u32 field: {}\n", .{hasFieldOfType(Person, u32)});
}

Part 4: コンパイル時最適化 (20点)

comptimeを使ってパフォーマンスを最適化する実装をしてください。

ファイル: part4/optimization.zig

const std = @import("std");

// TODO: コンパイル時にCRC32テーブルを生成
fn generateCRC32Table() [256]u32 {
    comptime {
        var table: [256]u32 = undefined;
        var i: usize = 0;
        while (i < 256) : (i += 1) {
            var crc: u32 = @intCast(i);
            var j: usize = 0;
            while (j < 8) : (j += 1) {
                if (crc & 1 == 1) {
                    crc = (crc >> 1) ^ 0xEDB88320;
                } else {
                    crc >>= 1;
                }
            }
            table[i] = crc;
        }
        return table;
    }
}

const crc32_table = generateCRC32Table();

// TODO: CRC32を計算
fn crc32(data: []const u8) u32 {
    var crc: u32 = 0xFFFFFFFF;
    for (data) |byte| {
        const index = @as(u8, @truncate((crc ^ byte) & 0xFF));
        crc = (crc >> 8) ^ crc32_table[index];
    }
    return ~crc;
}

// TODO: コンパイル時にビット反転テーブルを生成
fn generateBitReverseTable() [256]u8 {
    comptime {
        var table: [256]u8 = undefined;
        // 実装: 各バイトのビットを反転
        return table;
    }
}

// TODO: ループ展開を使った最適化
fn sumArrayUnrolled(comptime size: usize, array: [size]i32) i32 {
    var sum: i32 = 0;
    // 実装のヒント: inline forを使用
    return sum;
}

// TODO: コンパイル時分岐による最適化
fn optimizedMultiply(comptime factor: i32, value: i32) i32 {
    // 実装のヒント: factorが2のべき乗ならシフト演算を使用
    if (comptime std.math.isPowerOfTwo(factor)) {
        const shift = comptime std.math.log2_int(u32, @abs(factor));
        return value << shift;
    } else {
        return value * factor;
    }
}

pub fn main() void {
    std.debug.print("=== Compile-time Optimization ===\n\n", .{});

    // CRC32
    const data = "Hello, World!";
    const checksum = crc32(data);
    std.debug.print("CRC32 of '{s}': 0x{x:0>8}\n\n", .{data, checksum});

    // ループ展開
    const numbers = [_]i32{ 1, 2, 3, 4, 5, 6, 7, 8 };
    const sum = sumArrayUnrolled(8, numbers);
    std.debug.print("Sum (unrolled): {}\n\n", .{sum});

    // 最適化された乗算
    const result1 = optimizedMultiply(8, 10);
    const result2 = optimizedMultiply(7, 10);
    std.debug.print("10 * 8 (optimized): {}\n", .{result1});
    std.debug.print("10 * 7 (normal): {}\n", .{result2});
}

ボーナス課題 (20点)

Bonus 1: 型安全なビルダーパターン (10点)

comptimeを使った型安全なビルダーパターンを実装してください。

ファイル: bonus1/builder.zig

const std = @import("std");

// TODO: 型安全なビルダーパターン
fn Builder(comptime T: type) type {
    return struct {
        const Self = @This();

        value: T,

        pub fn init() Self {
            return Self{ .value = undefined };
        }

        pub fn set(self: Self, comptime field_name: []const u8, field_value: anytype) Self {
            var new_self = self;
            @field(new_self.value, field_name) = field_value;
            return new_self;
        }

        pub fn build(self: Self) T {
            // TODO: すべてのフィールドが設定されているか検証
            return self.value;
        }

        // TODO: 必須フィールドが設定されているか確認
        fn validateRequired(self: Self) bool {
            _ = self;
            // 実装
            return true;
        }
    };
}

const ServerConfig = struct {
    host: []const u8,
    port: u16,
    timeout: u32,
    max_connections: u32,
    ssl_enabled: bool,
};

pub fn main() void {
    std.debug.print("=== Type-Safe Builder ===\n\n", .{});

    const config = Builder(ServerConfig).init()
        .set("host", "0.0.0.0")
        .set("port", 8080)
        .set("timeout", 30000)
        .set("max_connections", 100)
        .set("ssl_enabled", false)
        .build();

    std.debug.print("Server Config:\n", .{});
    std.debug.print("  Host: {s}\n", .{config.host});
    std.debug.print("  Port: {}\n", .{config.port});
    std.debug.print("  Timeout: {}ms\n", .{config.timeout});
    std.debug.print("  Max Connections: {}\n", .{config.max_connections});
    std.debug.print("  SSL: {}\n", .{config.ssl_enabled});
}

Bonus 2: コンパイル時JSON パーサー (10点)

comptimeを使った簡易的なコンパイル時JSONパーサーを実装してください。

ファイル: bonus2/comptime_json.zig

const std = @import("std");

// TODO: コンパイル時JSONパーサー(簡易版)
fn parseJsonInt(comptime json: []const u8) i64 {
    comptime {
        var result: i64 = 0;
        var negative = false;
        var i: usize = 0;

        // 空白をスキップ
        while (i < json.len and json[i] == ' ') : (i += 1) {}

        // 符号をチェック
        if (i < json.len and json[i] == '-') {
            negative = true;
            i += 1;
        }

        // 数字を解析
        while (i < json.len and json[i] >= '0' and json[i] <= '9') : (i += 1) {
            result = result * 10 + (json[i] - '0');
        }

        return if (negative) -result else result;
    }
}

// TODO: コンパイル時文字列パーサー
fn parseJsonString(comptime json: []const u8) []const u8 {
    comptime {
        // 実装: "..." から文字列を抽出
        var start: usize = 0;
        var end: usize = 0;

        // 最初の"を見つける
        while (start < json.len and json[start] != '"') : (start += 1) {}
        start += 1;

        // 次の"を見つける
        end = start;
        while (end < json.len and json[end] != '"') : (end += 1) {}

        return json[start..end];
    }
}

// TODO: コンパイル時配列パーサー
fn parseJsonArray(comptime json: []const u8) []const i64 {
    comptime {
        // 実装: [...] から配列を抽出
        _ = json;
        const result: [3]i64 = [_]i64{1, 2, 3};
        return &result;
    }
}

pub fn main() void {
    std.debug.print("=== Compile-time JSON Parser ===\n\n", .{});

    comptime var int_value = parseJsonInt("  -42  ");
    std.debug.print("Parsed int: {}\n", .{int_value});

    comptime var str_value = parseJsonString("  \"Hello, World!\"  ");
    std.debug.print("Parsed string: {s}\n", .{str_value});

    comptime var arr_value = parseJsonArray("[1, 2, 3]");
    std.debug.print("Parsed array: ", .{});
    for (arr_value) |item| {
        std.debug.print("{} ", .{item});
    }
    std.debug.print("\n", .{});
}

評価基準

項目 配点
Part 1: コンパイル時ルックアップテーブル 20点
Part 2: ジェネリック型生成 20点
Part 3: コンパイル時リフレクション 20点
Part 4: コンパイル時最適化 20点
**マンダトリー合計** **80点**
Bonus 1: 型安全なビルダーパターン 10点
Bonus 2: コンパイル時JSONパーサー 10点
**ボーナス合計** **20点**

合格基準

  • マンダトリー: 64点以上で合格(80点満点の80%)
  • ボーナス: 追加評価(最終成績の加算)
  • 提出方法

    exercise11/
    ├── part1/
    │   └── lookup_tables.zig
    ├── part2/
    │   └── generic_types.zig
    ├── part3/
    │   └── reflection.zig
    ├── part4/
    │   └── optimization.zig
    ├── bonus1/
    │   └── builder.zig
    └── bonus2/
        └── comptime_json.zig
    

    テスト実行

    # マンダトリー
    zig run part1/lookup_tables.zig
    zig run part2/generic_types.zig
    zig run part3/reflection.zig
    zig run part4/optimization.zig
    
    # ボーナス
    zig run bonus1/builder.zig
    zig run bonus2/comptime_json.zig
    

    ヒント

  • comptime変数:
comptime var x = 10;

  • comptime関数:
fn fibonacci(n: u32) u32 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

  • 型情報の取得:
const fields = std.meta.fields(T);
inline for (fields) |field| {
    // フィールドを処理
}

  • inline for:
inline for (0..10) |i| {
    // ループが展開される
}

参考資料

  • Zig Language Reference - Comptime: https://ziglang.org/documentation/master/#comptime
  • Ziglearn - Comptime: https://ziglearn.org/chapter-2/#comptime
  • Zig Standard Library - Meta: https://ziglang.org/documentation/master/std/#std.meta