課題3: 基本構文と変数の理解
マンダトリー要件
Part 1: 型の理解 (25点)
さまざまなプリミティブ型を使用するプログラムを作成してください。
ファイル: part1/types.zig
const std = @import("std");
pub fn main() void {
// TODO: 以下の型の変数を宣言し、値を出力
// 1. 符号付き整数(i8, i16, i32, i64)
// 各型の最小値と最大値を表示
// 2. 符号なし整数(u8, u16, u32, u64)
// 各型の最大値を表示
// 3. 浮動小数点(f32, f64)
// 円周率を各型で表現して表示
// 4. ブール型
// 論理演算の結果を表示
// 5. 文字型
// ASCII文字とUnicode文字を表示
}
期待される出力例:
i8: -128 to 127
i16: -32768 to 32767
...
u8: 0 to 255
...
pi (f32): 3.14
pi (f64): 3.14159265
...
Part 2: 型変換 (25点)
型変換を実装し、正しく動作することを確認してください。
ファイル: part2/conversion.zig
const std = @import("std");
// TODO: 整数から浮動小数点への変換関数
fn intToFloat(value: i32) f64 {
// 実装
}
// TODO: 浮動小数点から整数への変換関数(切り捨て)
fn floatToInt(value: f64) i32 {
// 実装
}
// TODO: 小さい整数型から大きい整数型への変換
fn widentInt(value: i16) i32 {
// 実装
}
// TODO: 大きい整数型から小さい整数型への変換(安全性チェック付き)
fn narrowInt(value: i32) !i16 {
// 実装
// ヒント: 範囲外の場合はエラーを返す
}
pub fn main() !void {
std.debug.print("=== Type Conversion Tests ===\n", .{});
// テスト1: intToFloat
const int_val: i32 = 42;
const float_result = intToFloat(int_val);
std.debug.print("intToFloat({}) = {d:.1}\n", .{int_val, float_result});
// テスト2: floatToInt
const float_val: f64 = 3.14159;
const int_result = floatToInt(float_val);
std.debug.print("floatToInt({d:.5}) = {}\n", .{float_val, int_result});
// テスト3: widenInt
const small_int: i16 = 100;
const large_int = widenInt(small_int);
std.debug.print("widenInt({}) = {}\n", .{small_int, large_int});
// テスト4: narrowInt(成功ケース)
const valid_val: i32 = 100;
const narrow_result = try narrowInt(valid_val);
std.debug.print("narrowInt({}) = {}\n", .{valid_val, narrow_result});
// テスト5: narrowInt(エラーケース)
const invalid_val: i32 = 40000;
narrowInt(invalid_val) catch |err| {
std.debug.print("narrowInt({}) failed: {}\n", .{invalid_val, err});
};
}
Part 3: 演算子の活用 (25点)
各種演算子を使用したプログラムを実装してください。
ファイル: part3/operators.zig
const std = @import("std");
// TODO: 基本的な算術演算を行う関数
fn arithmetic(a: i32, b: i32) void {
// 加算、減算、乗算、除算、剰余を計算して表示
}
// TODO: ビット演算を行う関数
fn bitwise(a: u8, b: u8) void {
// AND, OR, XOR, NOT, シフト演算を実行して表示
}
// TODO: 比較演算を行う関数
fn comparison(a: i32, b: i32) void {
// すべての比較演算の結果を表示
}
// TODO: オーバーフロー対策の演算
fn overflowSafe() void {
var x: u8 = 255;
// 通常の加算(デバッグビルドではパニック)
// x += 1;
// ラップアラウンド加算
x +%= 1;
std.debug.print("Wrapping: 255 +%%= 1 = {}\n", .{x});
// 飽和加算
var y: u8 = 255;
y +|= 1;
std.debug.print("Saturating: 255 +|= 1 = {}\n", .{y});
}
pub fn main() void {
std.debug.print("=== Arithmetic Operations ===\n", .{});
arithmetic(10, 3);
std.debug.print("\n=== Bitwise Operations ===\n", .{});
bitwise(0b1100, 0b1010);
std.debug.print("\n=== Comparison Operations ===\n", .{});
comparison(10, 20);
std.debug.print("\n=== Overflow-Safe Operations ===\n", .{});
overflowSafe();
}
Part 4: 実践プログラム (25点)
温度変換プログラムを実装してください。
ファイル: part4/temperature.zig
const std = @import("std");
// TODO: 摂氏から華氏への変換
fn celsiusToFahrenheit(celsius: f64) f64 {
// 実装
}
// TODO: 華氏から摂氏への変換
fn fahrenheitToCelsius(fahrenheit: f64) f64 {
// 実装
}
// TODO: 摂氏からケルビンへの変換
fn celsiusToKelvin(celsius: f64) f64 {
// 実装
}
// TODO: ケルビンから摂氏への変換
fn kelvinToCelsius(kelvin: f64) f64 {
// 実装
}
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 != 3) {
std.debug.print("Usage: {} <temperature> <unit>\n", .{args[0]});
std.debug.print("Units: C (Celsius), F (Fahrenheit), K (Kelvin)\n", .{});
return;
}
const temp = try std.fmt.parseFloat(f64, args[1]);
const unit = args[2][0];
switch (unit) {
'C', 'c' => {
const f = celsiusToFahrenheit(temp);
const k = celsiusToKelvin(temp);
std.debug.print("{d:.2}°C = {d:.2}°F = {d:.2}K\n", .{temp, f, k});
},
'F', 'f' => {
const c = fahrenheitToCelsius(temp);
const k = celsiusToKelvin(c);
std.debug.print("{d:.2}°F = {d:.2}°C = {d:.2}K\n", .{temp, c, k});
},
'K', 'k' => {
const c = kelvinToCelsius(temp);
const f = celsiusToFahrenheit(c);
std.debug.print("{d:.2}K = {d:.2}°C = {d:.2}°F\n", .{temp, c, f});
},
else => {
std.debug.print("Unknown unit: {c}\n", .{unit});
return error.InvalidUnit;
},
}
}
実行例:
$ zig run temperature.zig -- 100 C
100.00°C = 212.00°F = 373.15K
$ zig run temperature.zig -- 32 F
32.00°F = 0.00°C = 273.15K
ボーナス課題
Bonus 1: 任意ビット幅整数 (15点)
Zigの任意ビット幅整数を活用したプログラムを作成してください。
ファイル: bonus1/arbitrary_int.zig
const std = @import("std");
pub fn main() void {
// TODO: さまざまなビット幅の整数を使用
// 3ビット符号付き整数(-4 to 3)
const i3_max: i3 = 3;
const i3_min: i3 = -4;
// 5ビット符号なし整数(0 to 31)
const u5_max: u5 = 31;
// 7ビット符号なし整数
const u7_max: u7 = 127;
// TODO: 各型の範囲を表示
// TODO: ビットフラグとして使用する例
const flags: u4 = 0b1010;
// ビットごとに意味を持たせる
}
Bonus 2: comptime型推論 (15点)
コンパイル時型推論を活用したプログラムを作成してください。
ファイル: bonus2/comptime_type.zig
const std = @import("std");
// TODO: 任意の数値型の最大値を返すコンパイル時関数
fn maxValue(comptime T: type) T {
const type_info = @typeInfo(T);
return switch (type_info) {
.Int => |int_info| blk: {
if (int_info.signedness == .unsigned) {
// 符号なし整数の最大値
break :blk (1 << int_info.bits) - 1;
} else {
// 符号付き整数の最大値
break :blk (1 << (int_info.bits - 1)) - 1;
}
},
else => @compileError("Unsupported type"),
};
}
// TODO: 任意の数値型の最小値を返すコンパイル時関数
fn minValue(comptime T: type) T {
// 実装
}
pub fn main() void {
// テスト
const u8_max = maxValue(u8);
const i8_max = maxValue(i8);
const i8_min = minValue(i8);
std.debug.print("u8 max: {}\n", .{u8_max});
std.debug.print("i8 range: {} to {}\n", .{i8_min, i8_max});
// さまざまな型でテスト
const u16_max = maxValue(u16);
const i32_min = minValue(i32);
std.debug.print("u16 max: {}\n", .{u16_max});
std.debug.print("i32 min: {}\n", .{i32_min});
}
Bonus 3: ビット操作ユーティリティ (20点)
ビット操作を行うユーティリティ関数を実装してください。
ファイル: bonus3/bitutils.zig
const std = @import("std");
// TODO: nビット目を設定
fn setBit(value: u32, bit: u5) u32 {
// 実装
}
// TODO: nビット目をクリア
fn clearBit(value: u32, bit: u5) u32 {
// 実装
}
// TODO: nビット目をトグル
fn toggleBit(value: u32, bit: u5) u32 {
// 実装
}
// TODO: nビット目をチェック
fn checkBit(value: u32, bit: u5) bool {
// 実装
}
// TODO: 立っているビットの数を数える
fn countBits(value: u32) u6 {
// 実装
}
// TODO: バイトオーダーを反転(エンディアン変換)
fn reverseBytes(value: u32) u32 {
// 実装
}
pub fn main() void {
var x: u32 = 0;
// ビット操作のテスト
x = setBit(x, 0); // ビット0を設定
x = setBit(x, 3); // ビット3を設定
std.debug.print("After setting bits 0 and 3: 0b{b:08}\n", .{x});
x = toggleBit(x, 1); // ビット1をトグル
std.debug.print("After toggling bit 1: 0b{b:08}\n", .{x});
const is_set = checkBit(x, 3);
std.debug.print("Bit 3 is set: {}\n", .{is_set});
const num_bits = countBits(x);
std.debug.print("Number of set bits: {}\n", .{num_bits});
// エンディアン変換
const original: u32 = 0x12345678;
const reversed = reverseBytes(original);
std.debug.print("Original: 0x{x:08}, Reversed: 0x{x:08}\n", .{original, reversed});
}
Bonus 4: 数値フォーマッター (20点)
数値をさまざまな形式で表示するフォーマッターを実装してください。
ファイル: bonus4/formatter.zig
const std = @import("std");
// TODO: 数値を2進数文字列に変換
fn toBinaryString(allocator: std.mem.Allocator, value: u32) ![]u8 {
// 実装
}
// TODO: 数値を16進数文字列に変換
fn toHexString(allocator: std.mem.Allocator, value: u32) ![]u8 {
// 実装
}
// TODO: 数値をカンマ区切りに変換(例: 1000000 -> "1,000,000")
fn toCommaString(allocator: std.mem.Allocator, value: u64) ![]u8 {
// 実装
}
// TODO: バイトサイズを人間が読みやすい形式に変換(例: 1024 -> "1 KB")
fn toHumanSize(allocator: std.mem.Allocator, bytes: u64) ![]u8 {
// 実装(B, KB, MB, GB, TBまで対応)
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
// テスト
const value: u32 = 255;
const binary = try toBinaryString(allocator, value);
defer allocator.free(binary);
std.debug.print("Binary: {s}\n", .{binary});
const hex = try toHexString(allocator, value);
defer allocator.free(hex);
std.debug.print("Hex: {s}\n", .{hex});
const large_num: u64 = 1234567890;
const comma = try toCommaString(allocator, large_num);
defer allocator.free(comma);
std.debug.print("Comma: {s}\n", .{comma});
const size: u64 = 1024 * 1024 * 512; // 512 MB
const human = try toHumanSize(allocator, size);
defer allocator.free(human);
std.debug.print("Size: {s}\n", .{human});
}
評価基準
| 項目 | 配点 |
|---|---|
| Part 1: 型の理解 | 25点 |
| Part 2: 型変換 | 25点 |
| Part 3: 演算子の活用 | 25点 |
| Part 4: 実践プログラム | 25点 |
| **マンダトリー合計** | **100点** |
| Bonus 1: 任意ビット幅整数 | 15点 |
| Bonus 2: comptime型推論 | 15点 |
| Bonus 3: ビット操作ユーティリティ | 20点 |
| Bonus 4: 数値フォーマッター | 20点 |
| **ボーナス合計** | **70点** |
合格基準
- マンダトリー: 80点以上で合格
- ボーナス: 追加評価(最終成績の加算)
提出方法
exercise03/
├── part1/
│ └── types.zig
├── part2/
│ └── conversion.zig
├── part3/
│ └── operators.zig
├── part4/
│ └── temperature.zig
├── bonus1/
│ └── arbitrary_int.zig
├── bonus2/
│ └── comptime_type.zig
├── bonus3/
│ └── bitutils.zig
└── bonus4/
└── formatter.zig
ヒント
const std = @import("std");
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
const u8_max = maxInt(u8); // 255
const i8_min = minInt(i8); // -128
- 型情報の取得:
const type_info = @typeInfo(u8);
// type_info.Int.bits -> 8
// type_info.Int.signedness -> .unsigned
- ビット演算のテクニック:
// nビット目を設定: value | (1 << n)
// nビット目をクリア: value & ~(1 << n)
// nビット目をトグル: value ^ (1 << n)
// nビット目をチェック: (value & (1 << n)) != 0
- 文字列フォーマット:
const std = @import("std");
const result = try std.fmt.allocPrint(allocator, "Value: {}", .{42});
defer allocator.free(result);
参考資料
- Zig Language Reference - Types: https://ziglang.org/documentation/master/#Types
- Zig Standard Library - Math: https://ziglang.org/documentation/master/std/#std.math
- Zig Standard Library - Fmt: https://ziglang.org/documentation/master/std/#std.fmt