課題2: 開発環境のセットアップと基本操作

マンダトリー要件

Part 1: インストール確認 (15点)

以下のコマンドを実行し、出力結果をスクリーンショットまたはテキストで提出してください:

# 1. Zigのバージョン確認
zig version

# 2. 利用可能なコマンドの表示
zig help

# 3. ターゲットプラットフォームの確認(出力の一部で可)
zig targets | head -20

提出物: installation_check.txt または screenshots/

Part 2: Hello World プログラム (20点)

基本的な "Hello World" プログラムを作成し、複数の方法で実行してください。

要件:

  • hello.zig を作成
  • 以下の3つの方法で実行:
- zig run hello.zig - zig build-exe hello.zig && ./hello - zig build-exe -O ReleaseFast hello.zig && ./hello

ファイル: part2/hello.zig

const std = @import("std");

pub fn main() void {
    // TODO: "Hello, Zig!" を出力
}

Part 3: コマンドライン引数処理 (25点)

コマンドライン引数を受け取り、処理するプログラムを作成してください。

要件:

  • プログラム名を表示
  • 引数の個数を表示
  • すべての引数を番号付きで表示
  • 引数がない場合は使用方法を表示

ファイル: part3/args.zig

const std = @import("std");

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

    // TODO: コマンドライン引数を取得

    // TODO: プログラム名を表示

    // TODO: 引数がない場合の処理
    if (args.len < 2) {
        std.debug.print("Usage: {} <arg1> <arg2> ...\n", .{args[0]});
        return;
    }

    // TODO: すべての引数を表示
}

実行例:

$ zig run args.zig -- hello world test
Program: args
Arguments: 3
1: hello
2: world
3: test

Part 4: ファイル入出力 (40点)

テキストファイルの読み書きを行うプログラムを実装してください。

要件:

  • ファイルに文字列を書き込む
  • ファイルから内容を読み込む
  • ファイルの内容を表示する
  • エラーハンドリングを実装(ファイルが存在しない場合など)

ファイル: part4/fileio.zig

const std = @import("std");

pub fn writeFile(filename: []const u8, content: []const u8) !void {
    // TODO: ファイルに書き込み
}

pub fn readFile(allocator: std.mem.Allocator, filename: []const u8) ![]u8 {
    // TODO: ファイルを読み込み
}

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

    // テストデータ
    const test_content = "Hello from Zig!\nThis is a test file.\n";

    // 書き込み
    try writeFile("test_output.txt", test_content);
    std.debug.print("File written successfully.\n", .{});

    // 読み込み
    const content = try readFile(allocator, "test_output.txt");
    defer allocator.free(content);

    std.debug.print("File content:\n{s}", .{content});
}

ボーナス課題

Bonus 1: build.zig の作成 (15点)

完全なプロジェクト構造を作成し、build.zig を実装してください。

プロジェクト構造:

bonus1/
├── build.zig
├── src/
│   ├── main.zig
│   └── utils.zig
└── test/
    └── utils_test.zig

src/main.zig:

const std = @import("std");
const utils = @import("utils.zig");

pub fn main() !void {
    const result = utils.add(10, 20);
    std.debug.print("10 + 20 = {}\n", .{result});
}

src/utils.zig:

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

pub fn multiply(a: i32, b: i32) i32 {
    return a * b;
}

test "add function" {
    const std = @import("std");
    try std.testing.expectEqual(@as(i32, 30), add(10, 20));
}

test "multiply function" {
    const std = @import("std");
    try std.testing.expectEqual(@as(i32, 200), multiply(10, 20));
}

build.zig:

const std = @import("std");

pub fn build(b: *std.Build) void {
    // TODO: ターゲットと最適化オプションの設定

    // TODO: 実行ファイルの定義

    // TODO: `zig build run` の実装

    // TODO: テストの実装
}

実行テスト:

cd bonus1
zig build
zig build run
zig build test

Bonus 2: クロスコンパイル (15点)

同じプログラムを複数のプラットフォーム向けにビルドしてください。

課題: シンプルな計算機プログラムを作成し、以下のターゲットでビルド:

  • x86_64-linux
  • x86_64-windows
  • aarch64-linux (ARM64)

ファイル: bonus2/calculator.zig

const std = @import("std");

pub fn main() !void {
    const allocator = std.heap.page_allocator;
    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);

    if (args.len != 4) {
        std.debug.print("Usage: {} <num1> <op> <num2>\n", .{args[0]});
        std.debug.print("Operations: + - * /\n", .{});
        return;
    }

    const a = try std.fmt.parseInt(i32, args[1], 10);
    const op = args[2][0];
    const b = try std.fmt.parseInt(i32, args[3], 10);

    const result = switch (op) {
        '+' => a + b,
        '-' => a - b,
        '*' => a * b,
        '/' => @divTrunc(a, b),
        else => {
            std.debug.print("Unknown operator: {c}\n", .{op});
            return error.InvalidOperator;
        },
    };

    std.debug.print("{} {} {} = {}\n", .{a, op, b, result});
}

ビルドスクリプト: bonus2/build_all.sh

#!/bin/bash

# TODO: 各ターゲット向けにビルド
# zig build-exe -target x86_64-linux calculator.zig -femit-bin=calculator-linux
# ...

# バイナリサイズを比較
ls -lh calculator-*

Bonus 3: ZLS統合とエディタ設定 (10点)

ZLS (Zig Language Server) をインストールし、お好みのエディタと統合してください。

提出物:

  • ZLSのインストール確認 (zls --version の出力)
  • エディタの設定ファイル(VS Code の settings.json、Neovim の init.lua など)
  • スクリーンショット(コード補完やエラー表示が動作している様子)

設定例:

VS Code (settings.json):

{
  "zig.path": "/usr/local/bin/zig",
  "zig.zls.path": "/usr/local/bin/zls",
  "zig.initialSetupDone": true,
  "zig.checkForUpdate": false
}

Neovim (init.lua):

require'lspconfig'.zls.setup{
  cmd = {'/usr/local/bin/zls'},
  filetypes = {'zig'},
}

Bonus 4: パフォーマンス比較 (20点)

同じアルゴリズムを異なる最適化レベルでビルドし、パフォーマンスを測定してください。

課題: フィボナッチ数列を計算するプログラムを作成し、最適化レベル別に実行時間を測定。

ファイル: bonus4/fibonacci.zig

const std = @import("std");

fn fibonacci(n: u64) u64 {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

pub fn main() !void {
    const args = try std.process.argsAlloc(std.heap.page_allocator);
    defer std.process.argsFree(std.heap.page_allocator, args);

    const n = if (args.len > 1)
        try std.fmt.parseInt(u64, args[1], 10)
    else
        40;

    const start = std.time.nanoTimestamp();
    const result = fibonacci(n);
    const end = std.time.nanoTimestamp();

    const duration = @as(f64, @floatFromInt(end - start)) / 1_000_000.0;

    std.debug.print("fibonacci({}) = {}\n", .{n, result});
    std.debug.print("Time: {d:.2} ms\n", .{duration});
}

ベンチマークスクリプト: bonus4/benchmark.sh

#!/bin/bash

echo "Building with different optimization levels..."

# Debug
zig build-exe -O Debug fibonacci.zig -femit-bin=fib-debug
echo "Debug build:"
time ./fib-debug 40

# ReleaseSafe
zig build-exe -O ReleaseSafe fibonacci.zig -femit-bin=fib-safe
echo "ReleaseSafe build:"
time ./fib-safe 40

# ReleaseFast
zig build-exe -O ReleaseFast fibonacci.zig -femit-bin=fib-fast
echo "ReleaseFast build:"
time ./fib-fast 40

# ReleaseSmall
zig build-exe -O ReleaseSmall fibonacci.zig -femit-bin=fib-small
echo "ReleaseSmall build:"
time ./fib-small 40

echo "Binary sizes:"
ls -lh fib-*

レポート作成: bonus4/benchmark_report.md

以下の情報を含むレポートを作成:

  • 各最適化レベルでの実行時間
  • バイナリサイズの比較
  • パフォーマンスとサイズのトレードオフの分析
  • 評価基準

    項目 配点
    Part 1: インストール確認 15点
    Part 2: Hello World 20点
    Part 3: コマンドライン引数 25点
    Part 4: ファイル入出力 40点
    **マンダトリー合計** **100点**
    Bonus 1: build.zig 15点
    Bonus 2: クロスコンパイル 15点
    Bonus 3: ZLS統合 10点
    Bonus 4: パフォーマンス比較 20点
    **ボーナス合計** **60点**

    合格基準

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

    exercise02/
    ├── installation_check.txt
    ├── part2/
    │   └── hello.zig
    ├── part3/
    │   └── args.zig
    ├── part4/
    │   └── fileio.zig
    ├── bonus1/
    │   ├── build.zig
    │   ├── src/
    │   │   ├── main.zig
    │   │   └── utils.zig
    │   └── test/
    │       └── utils_test.zig
    ├── bonus2/
    │   ├── calculator.zig
    │   └── build_all.sh
    ├── bonus3/
    │   ├── zls_version.txt
    │   ├── editor_config/
    │   └── screenshots/
    └── bonus4/
        ├── fibonacci.zig
        ├── benchmark.sh
        └── benchmark_report.md
    

    ヒント

  • エラー処理:
// ファイルオープンのエラー処理
const file = std.fs.cwd().openFile("test.txt", .{}) catch |err| {
    std.debug.print("Failed to open file: {}\n", .{err});
    return err;
};
defer file.close();

  • メモリ管理:
// アロケータを使った動的メモリ確保
const allocator = std.heap.page_allocator;
const buffer = try allocator.alloc(u8, 1024);
defer allocator.free(buffer);

  • フォーマット指定子:
std.debug.print("{s}\n", .{"文字列"});  // 文字列
std.debug.print("{}\n", .{42});         // 整数
std.debug.print("{d:.2}\n", .{3.14});   // 小数(2桁)

  • ビルド時間の測定:
time zig build-exe main.zig

参考資料

  • Zig Standard Library: https://ziglang.org/documentation/master/std/
  • Build System: https://ziglang.org/documentation/master/#Build-System
  • Cross-compilation: https://ziglang.org/documentation/master/#Cross-compiling