分岐とループの実践
分岐とループの実践
これまでに多くの理論を学びました——今こそ実際のプロジェクトを構築する時です。これは実践的なレッスンです。if 文、for ループ、while ループ、break、continue など、学んだ概念を使って、4 つの完全なプログラムを段階的に構築します。
各プロジェクトでは、要件を分析し、コードを書き、改善点について議論します。
プロジェクト 1:数当てゲーム(拡張版)
レッスン 08 で基本的な数当てゲームを書きました。今回は難易度選択、試行回数制限、「もう一度プレイ」機能を追加してアップグレードします。
要件
1. プレイヤーが難易度を選択:Easy(1~50、無制限)、Normal(1~100、10 回)、Hard(1~200、7 回)
2. システムがランダムにターゲット数を生成
3. 推測ごとに「大きい」または「小さい」のヒントと残りの試行回数を表示
4. 正解するか試行回数を使い切ったら、「もう一度プレイ?」と尋ねる
5. q を入力して終了
完全なコード(⭐⭐⭐)
例:数当てゲーム
import random
print("=" * 30)
print("Number Guessing Game (Enhanced)")
print("=" * 30)
while True: # 外側のループ:「もう一度プレイ」を制御
# 難易度を選択
print("\nSelect difficulty:")
print("1. Easy (1~50, unlimited)")
print("2. Normal (1~100, 10 tries)")
print("3. Hard (1~200, 7 tries)")
choice = input("Enter 1/2/3: ")
if choice == "1":
max_num = 50
max_attempts = float("inf") # 無制限
elif choice == "2":
max_num = 100
max_attempts = 10
elif choice == "3":
max_num = 200
max_attempts = 7
else:
print("Invalid choice, defaulting to Normal")
max_num = 100
max_attempts = 10
# ターゲット数を生成
target = random.randint(1, max_num)
attempts = 0
print(f"\nNumber generated between 1~{max_num}. Start guessing!")
# 内側のループ:ゲームのメインロジック
while True:
# 試行回数を使い切ったか確認
if attempts >= max_attempts:
print(f"\nOut of attempts! The answer was {target}")
break
# プレイヤーの入力を取得
guess_str = input(f"{max_attempts - attempts} tries left, enter your guess: ")
if guess_str.lower() == "q":
print("Quitting game.")
max_attempts = -1 # 外側のループに通知するフラグ
break
# 入力を検証
if not guess_str.isdigit():
print("Please enter a valid number!")
continue
guess = int(guess_str)
attempts += 1
# 推測をチェック
if guess < target:
print("Too low, try higher!")
elif guess > target:
print("Too high, try lower!")
else:
print(f"\nCongratulations! The answer was {target}!")
print(f"You got it in {attempts} tries.")
break
# 完全に終了すべきか確認
if max_attempts == -1: # プレイヤーが q を入力
break
# もう 1 回プレイするか尋ねる
again = input("\nPlay again? (y/n): ")
if again.lower() != "y":
print("Thanks for playing, goodbye!")
break
設計のポイント:外側のループは「もう一度プレイ」を制御し、内側のループは「推測プロセス」を制御します。
max_attempts = -1フラグは、プレイヤーが q を入力したときに両方のループから抜ける問題を解決します。よりエレガントな方法は、ゲームロジックを関数にカプセル化し、returnで一度に終了することです。
プロジェクト 2:九九の表(複数のスタイル)
レッスン 08 では標準的な九九の表を出力しました。優れたプログラマーはループ構造を柔軟に制御して任意のスタイルを出力できるべきです。
スタイル 1:標準的な下三角(⭐⭐)
例:標準的な下三角
print("=== Standard Lower Triangle ===")
for i in range(1, 10):
for j in range(1, i + 1):
print(f"{j}x{i}={i*j}", end="\t")
print()
スタイル 2:完全な長方形(⭐⭐)
例:完全な長方形
print("=== Full Rectangle ===")
for i in range(1, 10):
for j in range(1, 10):
print(f"{j}x{i}={i*j}", end="\t")
print()
スタイル 3:上三角(⭐⭐⭐)
例:上三角
print("=== Upper Triangle ===")
for i in range(1, 10):
# 位置揃えのための先頭スペースを出力
for k in range(1, i):
print(end="\t\t")
# 九九の式を出力
for j in range(i, 10):
print(f"{i}x{j}={i*j}", end="\t")
print()
出力:
=== Upper Triangle ===
1x1=1 1x2=2 1x3=3 ... 1x9=9
2x2=4 2x3=6 ... 2x9=18
3x3=9 ... 3x9=27
...
9x9=81
重要な洞察:上三角の課題は「スペースの配置」です——各行に
(i-1) * 2個のタブ文字のインデントが必要です。2 つの内側ループを使います:最初のループはスペースを出力し、2 番目のループは式を出力します。
プロジェクト 3:簡易電卓(拡張版)
レッスン 08 の練習問題で基本的な電卓を書きました。今回は履歴付きの連続演算をサポートする拡張版を作りましょう。
要件
1. 現在の結果を 0 として開始
2. ユーザーが「演算子 数値」の形式で入力、例:「+ 5」
3. サポートする演算子:+ - * / ** // % clear exit
4. clear は結果を 0 にリセット
5. exit はプログラムを終了
6. 各操作の後に、現在の結果と履歴を表示
完全なコード(⭐⭐⭐)
例:拡張電卓
print("=" * 40)
print("Enhanced Calculator")
print("Format: operator number, e.g., + 5")
print("Supported: + - * / ** // %")
print("Special commands: clear reset exit quit")
print("=" * 40)
result = 0.0
history = [] # 操作履歴を保存
while True:
# 現在の結果を表示
print(f"\nCurrent result: {result}")
# ユーザー入力を取得
cmd = input(">>> ").strip()
# 特殊コマンドを処理
if cmd.lower() in ("exit", "quit"):
print("Thanks for using!")
break
if cmd.lower() == "clear":
result = 0.0
history.clear()
print("Reset to 0")
continue
# 通常の操作コマンドを解析
parts = cmd.split()
if len(parts) != 2:
print("Invalid format! Enter: operator number")
continue
op, num_str = parts
# 数値を検証
if not num_str.replace(".", "").isdigit() or num_str.count(".") > 1:
print("Invalid number!")
continue
num = float(num_str)
# 操作を実行
old_result = result
if op == "+":
result += num
elif op == "-":
result -= num
elif op == "*":
result *= num
elif op == "/":
if num == 0:
print("Error: Cannot divide by zero!")
continue
result /= num
elif op == "**":
result **= num
elif op == "//":
if num == 0:
print("Error: Cannot divide by zero!")
continue
result //= num
elif op == "%":
if num == 0:
print("Error: Cannot divide by zero!")
continue
result %= num
else:
print(f"Invalid operator: {op}")
continue
# 履歴を記録
history.append(f"{old_result} {op} {num} = {result}")
# 結果を表示
print(f"= {result}")
# 最新の履歴 3 件を表示
if len(history) >= 1:
print("--- Recent History ---")
for item in history[-3:]:
print(f" {item}")
プロジェクト 4:素数発見プログラム
ユーザーが範囲を入力し、その範囲内のすべての素数を出力してカウントします。
例:素数発見プログラム
print("Prime Number Finder")
print("Enter a range, and I'll find all primes within it.")
# 範囲を取得
start_str = input("Start value (>=2): ")
end_str = input("End value: ")
# 入力を検証
if not (start_str.isdigit() and end_str.isdigit()):
print("Please enter valid positive integers!")
else:
start = int(start_str)
end = int(end_str)
if start < 2:
start = 2
print("Start value adjusted to 2 (primes start from 2)")
if start > end:
print("Start value cannot be greater than end value!")
else:
primes = [] # 見つかった素数を保存
# 範囲内の各数値をチェック
for num in range(start, end + 1):
# num が素数かどうかを確認
is_prime = True
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
is_prime = False
break
if is_prime:
primes.append(num)
# 結果を出力
print(f"\nFound {len(primes)} primes between {start} and {end}:")
for i, prime in enumerate(primes, 1):
print(f"{prime:5d}", end="")
if i % 10 == 0: # 10 個ごとに改行
print()
print() # 最後の改行
最適化のヒント:数値が素数かどうかをチェックするとき、
num-1まで割る必要はなく、sqrt(num)までで十分です。numにsqrt(num)より大きい因数がある場合、それとペアになるsqrt(num)より小さい因数が必ず存在します。この最適化は大きな範囲(100,000 以上)で非常に効果的です。
プロジェクト 5:BMI 健康指数+パスワード強度チェッカー
このプロジェクトでは、算術演算子、比較演算子、論理演算子、条件分岐、文字列メソッドを組み合わせて、2 つの実用的な問題を解決します。
パート 1:BMI 健康指数(⭐⭐)
BMI(Body Mass Index)は国際的に使用される体脂肪の測定基準です。計算式は簡単です:
BMI = 体重(kg) / 身長(m)^2
例:BMI 計算機
# BMI 健康指数計算機
# データ入力
weight = 68
height = 1.75
# BMI を計算
bmi = weight / (height ** 2)
print("=== BMI Health Index ===")
print(f"Weight: {weight} kg")
print(f"Height: {height} m")
print(f"BMI: {bmi:.1f}")
# 連鎖比較と if-elif でカテゴリを判定
if bmi < 18.5:
print("Category: Underweight")
elif 18.5 <= bmi < 24:
print("Category: Normal")
elif 24 <= bmi < 28:
print("Category: Overweight")
else:
print("Category: Obese")
パート 2:パスワード強度チェッカー(⭐⭐⭐)
ユーザーのパスワードが安全かどうかを判定するプログラムを書きます。パスワードの強度は、その長さと含まれる文字種によって決まります。
例:パスワード強度チェッカー
# パスワード強度チェッカー
password = "Py3#thon"
# パスワードの特性を分析
length = len(password) # 長さ
has_digit = any(c.isdigit() for c in password) # 数字を含む
has_letter = any(c.isalpha() for c in password) # 文字を含む
has_special = not password.isalnum() # 特殊文字を含む
print("=== Password Strength Checker ===")
print(f"Password: {password}")
print(f"Length: {length}")
print(f"Contains digit: {has_digit}")
print(f"Contains letter: {has_letter}")
print(f"Contains special char: {has_special}")
# 論理演算子で条件を組み合わせて強度を判定
if length < 6:
strength = "Weak"
elif length >= 10 and has_digit and has_letter and has_special:
strength = "Strong"
elif length >= 8 and has_digit and has_letter:
strength = "Medium"
else:
strength = "Weak"
print(f"Password strength: {strength}")
よくある分岐/ループのミス
コードを書くとき、特にループ初心者は誰でもミスをします。最も典型的な落とし穴を紹介します。
ミス 1:無限ループ——条件変数の更新忘れ
# 間違った方法
n = 1
while n <= 10:
print(n)
# n += 1 を忘れた — 永遠に終わらない
# 正しい方法
n = 1
while n <= 10:
print(n)
n += 1
ミス 2:= と == の混同
# 間違った方法
if score = 100: # これは代入であり、比較ではない!
print("Perfect!")
# 正しい方法
if score == 100:
print("Perfect!")
ミス 3:反復中にリストを変更する
# 間違った方法 — 反復中に削除するとスキップが発生
numbers = [1, 2, 3, 4, 5]
for num in numbers:
if num % 2 == 0:
numbers.remove(num)
print(numbers) # 期待通りにならない
# 正しい方法 — 新しいリストを作成
numbers = [1, 2, 3, 4, 5]
odd_numbers = [num for num in numbers if num % 2 != 0]
print(odd_numbers)
ミス 4:range() の終了値の誤解
# 間違い:range(5) が 1 2 3 4 5 を出力すると期待
for i in range(5):
print(i) # 実際の出力:0 1 2 3 4
# 正しい方法
for i in range(1, 6):
print(i) # 出力:1 2 3 4 5
よくあるユースケース
| シナリオ | ループ/分岐テクニック | 説明 |
|---|---|---|
| データ処理(フィルタリング、クリーニング) | for + continue |
無効なデータをスキップ |
| ユーザー対話メニュー | while True + break |
終了するまでメニューを繰り返し表示 |
| ページネーション読み込み | while + 条件 |
データがなくなるまで |
| 検索/照合 | for + break + else |
見つかったら停止。見つからなければ else |
| バッチファイル処理 | for + ネストロジック |
ディレクトリ内のすべてのファイルを反復 |
| ゲームメインループ | while True + イベント処理 |
継続的なレンダリングと入力応答 |
❓ よくある質問
False になりますか?2)ループ本体は条件変数を変更していますか?3)while True に対応する break がありますか?重要な変数の値を出力する print() を追加することが、無限ループのデバッグに最も効果的です。range() とリストの反復、どちらが効率的ですか?range() は遅延シーケンスを返し、すべての数値を一度に生成しません。range(1000000) はほとんどメモリを使用しませんが、list(range(1000000)) は大量に使用します。大量の反復には、メモリ効率の点で range() を直接使用する方が優れています。📖 まとめ
- 拡張数当てゲーム:外側ループで「もう一度プレイ」、内側ループで「推測」、
while True+breakでマルチレベルループ終了管理 - 九九の表のスタイル:内側ループの
range()境界を制御して、下三角、上三角、長方形などを出力 - 拡張電卓:
continueで無効な入力をスキップ、リストで履歴を保存、split()でユーザー入力を解析 - 素数発見プログラム:
sqrt(num)までのチェックに最適化。enumerate(primes, 1)で番号付き出力 - デバッグのヒント:無限ループチェックの 3 要素、
=と==の区別、反復中のリスト変更禁止
📝 練習問題
⭐:ナルシシスト数
「ナルシシスト数」は、各桁の 3 乗の和がその数自身と等しい 3 桁の数です。例:153 = 1^3 + 5^3 + 3^3。
for ループを使ってすべてのナルシシスト数(4 つあります)を見つけてください。
ヒント:range(100, 1000) ですべての 3 桁の数を反復。// と % で百の位、十の位、一の位を抽出。
⭐⭐:ひし形の出力
ユーザーから奇数 n を受け取り、* で作られたひし形をコンソールに出力します。
例(n=5):
*
***
*
***
*
ヒント:ひし形には上半分と下半分があります。上半分は i を 1 から n//2 + 1 までループ、スペースを減らしアスタリスクを増やします。
⭐⭐⭐:学生スコア管理システム
以下の機能を実装するプログラムを書いてください:
1. 学生のスコアを入力(q を入力で停止)
2. 合計、平均、最高、最低スコアを計算
3. 各範囲の学生数をカウント:90-100(優秀)、80-89(良)、70-79(普通)、60-69(合格)、<60(不合格)
4. スコアを高い順にソートして出力
要件:
while Trueループでスコアを収集- リストでスコアを保存
forループでスコア範囲をカウントsorted(..., reverse=True)でソート



