課題1: Zigの設計思想を理解する

マンダトリー要件

Part 1: 基礎知識 (20点)

以下の質問に答えてください:

  • 歴史
- Zigが開発された年はいつですか? - Zigの開発者は誰ですか? - Zigの最初の公開リリースはいつですか?

  • 設計哲学
- Zigの設計哲学の4つの核となる原則を挙げてください - それぞれの原則がどのようにコードに反映されているか、具体例を挙げて説明してください

  • 未定義動作
- C言語の未定義動作とは何ですか? - Zigはどのようにして未定義動作を排除していますか?

Part 2: 言語比較 (30点)

以下のシナリオで、どの言語(C, Zig, Rust, Go)が最適か、理由と共に答えてください:

  • 既存のLinuxカーネルモジュールを拡張する
  • 新しいWebサーバーを一から作成する
  • 組み込みシステム向けのファームウェアを開発する
  • メモリ安全性が最重要な金融取引システムを構築する
  • リアルタイム画像処理システムを開発する

Part 3: コード理解 (30点)

以下のZigコードを読み、各コードがどの設計原則を体現しているか説明してください:

コード1: 明示性

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;

    const file = try std.fs.cwd().openFile("data.txt", .{});
    defer file.close();

    const contents = try file.readToEndAlloc(allocator, 1024 * 1024);
    defer allocator.free(contents);
}

コード2: 整数演算

pub fn example1() void {
    var x: u8 = 200;
    x +%= 100; // 結果は?
}

pub fn example2() void {
    var y: u8 = 200;
    y +|= 100; // 結果は?
}

コード3: comptime

fn factorial(comptime n: u32) u32 {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

const fact_5 = factorial(5);

質問:

  • コード1のどこに「明示性」の原則が表れていますか?
  • コード2の2つの演算の違いは何ですか?
  • コード3の fact_5 はいつ計算されますか? その利点は何ですか?

Part 4: 実践演習 (20点)

以下の要件を満たすZigプログラムを作成してください:

要件:

  • 2つの整数を受け取り、その最大値を返す関数 max を実装
  • エラー処理を含むファイル読み込み関数を実装
  • コンパイル時計算を使った関数を1つ実装

ファイル名: mandatory.zig

const std = @import("std");

// TODO: max関数を実装
fn max(a: i32, b: i32) i32 {
    // ここに実装
}

// TODO: ファイル読み込み関数を実装
fn readFileContent(allocator: std.mem.Allocator, filename: []const u8) ![]u8 {
    // ここに実装
}

// TODO: コンパイル時計算関数を実装
fn power(comptime base: u32, comptime exp: u32) u32 {
    // ここに実装
}

pub fn main() !void {
    // テストコード
    const result1 = max(10, 20);
    std.debug.print("max(10, 20) = {}\n", .{result1});

    const allocator = std.heap.page_allocator;
    const content = try readFileContent(allocator, "test.txt");
    defer allocator.free(content);
    std.debug.print("File content length: {}\n", .{content.len});

    const pow_result = power(2, 10);
    std.debug.print("2^10 = {}\n", .{pow_result});
}

ボーナス課題

> ボーナス: これらはオプションです。マンダトリー要件を完了してから取り組んでください。

Bonus 1: ジェネリック関数 (10点)

任意の型に対応する max 関数を comptime を使って実装してください:

// TODO: ジェネリック版のmax関数を実装
fn maxGeneric(comptime T: type, a: T, b: T) T {
    // ここに実装
}

pub fn bonusTest1() void {
    const x = maxGeneric(i32, 10, 20);
    const y = maxGeneric(f64, 3.14, 2.71);
    const z = maxGeneric(u8, 100, 200);

    std.debug.print("i32: {}, f64: {d:.2}, u8: {}\n", .{x, y, z});
}

Bonus 2: 型の最大値取得 (10点)

任意の整数型の最大値を返すコンパイル時関数を実装してください:

const std = @import("std");

// TODO: 型の最大値を返す関数を実装
fn maxValue(comptime T: type) T {
    // ヒント: @typeInfo() を使用します
    // @typeInfo(T) で型情報を取得し、.Int.bits でビット数を取得できます
}

pub fn bonusTest2() void {
    const max_u8 = maxValue(u8);   // 255
    const max_i16 = maxValue(i16); // 32767
    const max_u32 = maxValue(u32); // 4294967295

    std.debug.print("max u8: {}\n", .{max_u8});
    std.debug.print("max i16: {}\n", .{max_i16});
    std.debug.print("max u32: {}\n", .{max_u32});
}

Bonus 3: カスタムエラーセット (10点)

複数のエラー型を持つシステムを実装してください:

const std = @import("std");

const FileError = error{
    FileNotFound,
    PermissionDenied,
    InvalidPath,
};

const ParseError = error{
    InvalidFormat,
    UnexpectedToken,
    OutOfBounds,
};

// TODO: 両方のエラーを扱える関数を実装
fn processFile(filename: []const u8) (FileError || ParseError)!void {
    // ここに実装
    // 1. ファイル存在チェック(FileError)
    // 2. ファイル内容の解析(ParseError)
}

pub fn bonusTest3() void {
    processFile("test.txt") catch |err| {
        std.debug.print("Error occurred: {}\n", .{err});
    };
}

Bonus 4: ベンチマーク比較 (10点)

Zigとその他の言語(C、Python、Rustなど)で同じアルゴリズムを実装し、パフォーマンスを比較してください。

課題:

  • フィボナッチ数列の計算(n=40)
  • 大きな配列のソート(100万要素)
  • ファイルI/O(1GBのファイル読み込み)

提出物:

  • 各言語での実装コード
  • 実行時間の測定結果
  • パフォーマンス比較レポート(Markdown形式)
  • 評価基準

    項目 配点
    Part 1: 基礎知識 20点
    Part 2: 言語比較 30点
    Part 3: コード理解 30点
    Part 4: 実践演習 20点
    **マンダトリー合計** **100点**
    Bonus 1: ジェネリック関数 10点
    Bonus 2: 型の最大値取得 10点
    Bonus 3: カスタムエラーセット 10点
    Bonus 4: ベンチマーク比較 10点
    **ボーナス合計** **40点**

    合格基準

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

  • すべてのコードを exercise01/ ディレクトリに配置
  • 以下のファイル構成にすること:
exercise01/
├── mandatory.zig      # マンダトリー要件
├── answers.md         # 質問への回答
├── bonus1.zig         # ボーナス1
├── bonus2.zig         # ボーナス2
├── bonus3.zig         # ボーナス3
└── bonus4/            # ボーナス4
    ├── benchmark.md
    ├── fibonacci.zig
    ├── sort.zig
    └── fileio.zig

  • テスト実行:
# マンダトリー
zig build-exe mandatory.zig
./mandatory

# ボーナス
zig build-exe bonus1.zig && ./bonus1
zig build-exe bonus2.zig && ./bonus2
zig build-exe bonus3.zig && ./bonus3

ヒント

  • comptime の理解:
- comptime キーワードは、その値がコンパイル時に決定されることを意味します - コンパイル時計算は実行時のパフォーマンスに影響しません

  • エラー処理:
- try は エラーを上位に伝播します - catch はエラーをキャッチして処理します - defer はスコープ終了時に実行されます

  • アロケータ:
- std.heap.page_allocator: シンプルな汎用アロケータ - std.heap.GeneralPurposeAllocator: デバッグ機能付き - メモリリークを防ぐため、必ず defer allocator.free() を使用

  • 型情報の取得:
const type_info = @typeInfo(u8);
// type_info.Int.bits で型のビット数を取得
// type_info.Int.signedness で符号の有無を確認

参考資料

  • Zig Language Reference: https://ziglang.org/documentation/master/
  • Zig Standard Library: https://ziglang.org/documentation/master/std/
  • Zig Learn: https://ziglearn.org/