課題1: Zigの設計思想を理解する
マンダトリー要件
Part 1: 基礎知識 (20点)
以下の質問に答えてください:
- 歴史
- 設計哲学
- 未定義動作
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形式)
- マンダトリー: 80点以上で合格
- ボーナス: 追加評価(最終成績の加算)
- すべてのコードを
exercise01/ディレクトリに配置 - 以下のファイル構成にすること:
評価基準
| 項目 | 配点 |
|---|---|
| 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点** |
合格基準
提出方法
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/