評価3: 基本構文と変数の理解 - 評価基準
評価の目的
この評価では、学生がZigの基本的な構文、型システム、演算子を理解し、実用的なプログラムを作成できるかを確認します。型の正確な理解は、Zigプログラミングの基礎となる重要なスキルです。
学習目標
- Zigのプリミティブ型(整数、浮動小数点、ブール、文字)を理解し使用できる
- 型変換を安全に実装できる
- 各種演算子(算術、ビット、比較、オーバーフロー対策)を適切に使用できる
- 実用的なプログラム(温度変換など)を実装できる
- 任意ビット幅整数やcomptime型推論などの高度な機能を理解できる
評価項目
1. 型の理解(25点)
| 項目 | 配点 | 評価基準 |
|---|---|---|
| **整数型の使用** | 10点 | 10点:符号付き/符号なし整数の各サイズ(i8,i16,i32,i64,u8,u16,u32,u64)を正しく使用し、最大値・最小値を表示 7点:主要な型は使用できるが、一部不正確 4点:基本的な型のみ使用 0点:理解が不十分 |
| **浮動小数点型** | 5点 | 5点:f32とf64の違いを理解し、精度の違いを示せる 3点:使用できるが精度の違いの理解が浅い 0点:適切に使用できない |
| **ブール・文字型** | 5点 | 5点:ブール値と論理演算、ASCII/Unicode文字を正しく扱える 3点:基本的な使用のみ 0点:使用できない |
| **出力フォーマット** | 5点 | 5点:適切なフォーマット指定子を使用し、読みやすい出力 3点:出力はされるがフォーマットが不適切 0点:フォーマットの理解なし |
実装チェックポイント:
// 整数型の最大値・最小値
const i8_min = std.math.minInt(i8); // -128
const i8_max = std.math.maxInt(i8); // 127
const u8_max = std.math.maxInt(u8); // 255
// 浮動小数点の精度
const pi_f32: f32 = 3.14159265;
const pi_f64: f64 = 3.14159265358979;
// フォーマット指定子
std.debug.print("{}\n", .{整数}); // 整数
std.debug.print("{d:.2}\n", .{浮動}); // 小数点2桁
std.debug.print("{s}\n", .{文字列}); // 文字列
2. 型変換(25点)
| 項目 | 配点 | 評価基準 |
|---|---|---|
| **基本的な型変換** | 10点 | 10点:intToFloat、floatToIntが正しく動作し、適切な型キャストを使用 7点:動作するが型キャストの理解が不十分 3点:部分的に動作 0点:動作しない |
| **拡張・縮小変換** | 10点 | 10点:widenIntとnarrowIntの両方を正しく実装し、範囲チェックを含む 7点:基本的な変換はできるが、エラー処理が不十分 3点:拡張のみまたは縮小のみ 0点:動作しない |
| **エラー処理** | 5点 | 5点:narrowIntで範囲外の値を適切にエラーとして処理 3点:エラー処理はあるが不完全 0点:エラー処理なし |
評価ポイント:
// 正しい型変換の例
fn intToFloat(value: i32) f64 {
return @floatFromInt(value);
}
fn floatToInt(value: f64) i32 {
return @intFromFloat(value);
}
fn narrowInt(value: i32) !i16 {
if (value < std.math.minInt(i16) or value > std.math.maxInt(i16)) {
return error.Overflow;
}
return @intCast(value);
}
3. 演算子の活用(25点)
| 項目 | 配点 | 評価基準 | |
|---|---|---|---|
| **算術演算** | 7点 | 7点:加減乗除余が正しく実装され、適切に表示される 5点:基本演算はできるが、一部不正確 2点:限定的な実装 0点:動作しない |
|
| **ビット演算** | 8点 | 8点:AND、OR、XOR、NOT、シフト演算すべてを正しく実装 6点:主要なビット演算を実装 3点:一部のビット演算のみ 0点:理解が不十分 |
|
| **比較演算** | 5点 | 5点:すべての比較演算子(<, >, <=, >=, ==, !=)を使用 3点:基本的な比較のみ 0点:不適切 |
|
| **オーバーフロー対策** | 5点 | 5点:ラップアラウンド(+%=)と飽和演算(+\ | =)の違いを理解し実装 3点:使用できるが説明が不十分 0点:理解していない |
実装例:
// オーバーフロー対策の演算
var x: u8 = 255;
x +%= 1; // ラップアラウンド: 255 + 1 = 0
std.debug.print("Wrapping: {}\n", .{x});
var y: u8 = 255;
y +|= 1; // 飽和演算: 255 + 1 = 255 (最大値で飽和)
std.debug.print("Saturating: {}\n", .{y});
4. 実践プログラム(25点)
| 項目 | 配点 | 評価基準 |
|---|---|---|
| **変換関数の実装** | 12点 | 12点:4つの変換関数すべてが正しい計算式で実装されている 9点:3つの関数が正しい 6点:2つの関数が正しい 0点:1つ以下 |
| **CLI処理** | 8点 | 8点:引数解析、単位判定、適切なエラーメッセージを実装 6点:基本的な処理はできるが、エラー処理が不十分 3点:限定的な機能 0点:動作しない |
| **出力品質** | 5点 | 5点:適切な小数点精度と単位記号を含む読みやすい出力 3点:出力はあるが品質が低い 0点:不適切 |
温度変換の正しい式:
// 摂氏から華氏: F = C × 9/5 + 32
fn celsiusToFahrenheit(celsius: f64) f64 {
return celsius * 9.0 / 5.0 + 32.0;
}
// 華氏から摂氏: C = (F - 32) × 5/9
fn fahrenheitToCelsius(fahrenheit: f64) f64 {
return (fahrenheit - 32.0) * 5.0 / 9.0;
}
// 摂氏からケルビン: K = C + 273.15
fn celsiusToKelvin(celsius: f64) f64 {
return celsius + 273.15;
}
// ケルビンから摂氏: C = K - 273.15
fn kelvinToCelsius(kelvin: f64) f64 {
return kelvin - 273.15;
}
ボーナス課題の評価
Bonus 1: 任意ビット幅整数(15点)
| 配点 | 評価基準 |
|---|---|
| 15点 | さまざまなビット幅(i3,u5,u7など)を正しく使用し、範囲と用途を説明 |
| 10点 | 任意ビット幅整数を使用できるが、説明が不十分 |
| 5点 | 限定的な使用のみ |
| 0点 | 理解が不十分 |
評価ポイント:
- i3: -4 から 3 までの4ビット符号付き整数
- u5: 0 から 31 までの5ビット符号なし整数
- ビットフラグとしての活用例
- メモリ効率の理解
Bonus 2: comptime型推論(15点)
| 配点 | 評価基準 |
|---|---|
| 15点 | `@typeInfo`を使用し、符号付き/符号なし両方の最大値・最小値を計算 |
| 10点 | 基本的な型情報取得はできるが、計算が不完全 |
| 5点 | `@typeInfo`の使用のみ |
| 0点 | 動作しない |
実装例:
fn maxValue(comptime T: type) T {
const info = @typeInfo(T);
return switch (info) {
.Int => |int_info| blk: {
if (int_info.signedness == .unsigned) {
break :blk (@as(T, 1) << int_info.bits) - 1;
} else {
break :blk (@as(T, 1) << (int_info.bits - 1)) - 1;
}
},
else => @compileError("Unsupported type"),
};
}
Bonus 3: ビット操作ユーティリティ(20点)
| 配点 | 評価基準 |
|---|---|
| 20点 | 6つの関数すべてが正しく動作し、適切なビット操作を実装 |
| 15点 | 4-5個の関数が正しく動作 |
| 10点 | 3個の関数が正しく動作 |
| 5点 | 1-2個のみ |
| 0点 | ほとんど動作しない |
実装チェックポイント:
fn setBit(value: u32, bit: u5) u32 {
return value | (@as(u32, 1) << bit);
}
fn clearBit(value: u32, bit: u5) u32 {
return value & ~(@as(u32, 1) << bit);
}
fn toggleBit(value: u32, bit: u5) u32 {
return value ^ (@as(u32, 1) << bit);
}
fn checkBit(value: u32, bit: u5) bool {
return (value & (@as(u32, 1) << bit)) != 0;
}
fn countBits(value: u32) u6 {
return @popCount(value);
}
fn reverseBytes(value: u32) u32 {
return @byteSwap(value);
}
Bonus 4: 数値フォーマッター(20点)
| 配点 | 評価基準 |
|---|---|
| 20点 | 4つのフォーマット関数すべてが正しく動作し、適切な文字列を生成 |
| 15点 | 3個の関数が正しく動作 |
| 10点 | 2個の関数が正しく動作 |
| 5点 | 1個のみ |
| 0点 | ほとんど動作しない |
実装ヒント:
std.fmt.allocPrintを使用した文字列生成- カンマ区切り: 1000000 → "1,000,000"
- 人間が読みやすいサイズ: 1024 → "1 KB", 1048576 → "1 MB"
評価チェックリスト
マンダトリー要件
- [ ] Part 1で全ての型を正しく使用している
- [ ] Part 1で最大値・最小値を正確に表示している
- [ ] Part 2の4つの型変換関数すべてが動作する
- [ ] Part 2のnarrowIntで範囲チェックが実装されている
- [ ] Part 3の算術、ビット、比較演算すべてが実装されている
- [ ] Part 3のオーバーフロー対策演算が正しく動作する
- [ ] Part 4の4つの温度変換関数が正しい計算式で実装されている
- [ ] Part 4のCLIで引数解析とエラー処理が適切
- [ ] すべてのコードがコンパイルエラーなく動作する
- [ ] 提出物のディレクトリ構造が要件を満たす
コード品質
- [ ] 変数名が意味を持っている
- [ ] 適切なインデントとフォーマット
- [ ] 型が明示的に指定されている
- [ ] フォーマット指定子が適切
- [ ] エラーメッセージがわかりやすい
ボーナス要件
- [ ] Bonus 1: 複数の任意ビット幅整数を使用
- [ ] Bonus 2: @typeInfoを使用した型情報取得
- [ ] Bonus 2: maxValueとminValueの両方を実装
- [ ] Bonus 3: 6つのビット操作関数すべてが動作
- [ ] Bonus 4: 4つのフォーマット関数すべてが動作
- [ ] Bonus 4: メモリリークがない(allocator.free使用)
- 型システムの理解
合格基準
| レベル | 点数 | 説明 |
|---|---|---|
| **優秀** | 90-100点 | すべての型を正確に理解し、複雑な型変換やビット操作を実装できる。ボーナス課題で高度な機能を活用している。 |
| **良好** | 80-89点 | 基本的な型システムを理解し、型変換と演算子を適切に使用できる。一部のボーナス課題に取り組んでいる。 |
| **合格** | 70-79点 | 型の基礎を理解し、基本的なプログラムを作成できる。いくつかの改善点はあるが、次へ進める水準。 |
| **要再提出** | 0-69点 | 型システムの理解が不十分、または実装に重大な問題。基礎の復習と再提出が必要。 |
学習の手引き
効果的な学習方法
- 型変換の練習
- 演算子の習得
- 実践的なプログラム作成
よくある間違いと対策
間違い1: 型の不一致
// ❌ 間違い
const x: i32 = 10;
const y: f64 = x; // コンパイルエラー
// ✅ 正しい
const x: i32 = 10;
const y: f64 = @floatFromInt(x);
間違い2: オーバーフロー
// ❌ 間違い(デバッグビルドでパニック)
var x: u8 = 255;
x += 1;
// ✅ 正しい(意図を明示)
var x: u8 = 255;
x +%= 1; // ラップアラウンド
// または
x +|= 1; // 飽和演算
間違い3: 型キャストの乱用
// ❌ 問題あり
fn narrowInt(value: i32) i16 {
return @intCast(value); // 範囲チェックなし
}
// ✅ 正しい
fn narrowInt(value: i32) !i16 {
if (value < std.math.minInt(i16) or value > std.math.maxInt(i16)) {
return error.Overflow;
}
return @intCast(value);
}
間違い4: ビット演算の優先順位
// ❌ 間違い(優先順位に注意)
const result = value & 1 << 3;
// ✅ 正しい
const result = value & (@as(u32, 1) << 3);
デバッグのヒント
- 型エラーのデバッグ
- 計算結果の確認
{b:08}
- 16進数表示: {x:08}- 範囲チェック
std.math.minIntとstd.math.maxIntで範囲確認
- テストケースに境界値を含める
- エッジケースを意識する学習リソース
- 公式ドキュメント
- 標準ライブラリ
- コミュニティリソース
時間配分の目安
| パート | 推奨時間 | 内容 |
|---|---|---|
| Part 1 | 2-3時間 | 型の理解と実装 |
| Part 2 | 2-3時間 | 型変換関数の実装 |
| Part 3 | 2-3時間 | 演算子の実装とテスト |
| Part 4 | 2-3時間 | 温度変換プログラム |
| Bonus 1 | 1-2時間 | 任意ビット幅整数 |
| Bonus 2 | 2-3時間 | comptime型推論 |
| Bonus 3 | 3-4時間 | ビット操作 |
| Bonus 4 | 3-4時間 | フォーマッター |
合計: マンダトリーで8-12時間、ボーナス込みで17-25時間
評価後のフィードバック例
優秀な提出物の場合
型システムの理解が非常に優れています。特に、任意ビット幅整数の
活用とcomptime型推論の実装は、Zigの高度な機能を深く理解して
いることを示しています。
ビット操作ユーティリティの実装は効率的で、エッジケースも適切に
処理されています。フォーマッターの実装も読みやすく、メモリ管理も
完璧です。
次のステップ: ジェネリックプログラミングやメタプログラミングに
進むことをお勧めします。
改善が必要な場合
基本的な型の理解はできていますが、以下の点で改善が必要です:
1. 型変換でエラー処理が不十分です。narrowInt関数で範囲チェックを
追加してください。
2. オーバーフロー対策演算の使い分けが理解できていません。
ラップアラウンド(+%=)と飽和演算(+|=)の違いを確認してください。
3. ビット演算で括弧が不足し、意図しない結果になっています。
改善のヒント:
- エラー型を返す関数には必ず!を付ける
- ビット演算では常に括弧で優先順位を明示
- std.mathの関数を活用する
参考: Zig Language Reference の Types と Operators を復習してください。
参考資料
- Zig Language Reference - Types: https://ziglang.org/documentation/master/#Types
- Zig Language Reference - Operators: https://ziglang.org/documentation/master/#Operators
- Zig Standard Library - math: https://ziglang.org/documentation/master/std/#std.math
- Zig Standard Library - fmt: https://ziglang.org/documentation/master/std/#std.fmt
- Zig Learn - Types: https://ziglearn.org/chapter-1/#types