Skilore

分岐とループ

制御フローはプログラムの「実行順序を変える」仕組み。分岐とループの設計は言語の哲学を反映する。

87 分で読めます43,448 文字

分岐とループ

制御フローはプログラムの「実行順序を変える」仕組み。分岐とループの設計は言語の哲学を反映する。

この章で学ぶこと

  • 各言語の分岐構文の違いと設計思想を理解する
  • ループの種類と使い分けを把握する
  • 式ベースの制御フローを理解する
  • 早期リターンとガード節を適切に使いこなせる
  • ループ最適化のテクニックを把握する
  • 制御フローの設計判断を言語横断的に比較できる

前提知識

このガイドを読む前に、以下の知識があると理解が深まります:

  • 基本的なプログラミングの知識
  • 関連する基礎概念の理解

1. 分岐(Branching)

1.1 if 文 vs if 式

プログラミング言語における if の設計は、「文(statement)」として扱うか「式(expression)」として扱うかで大きく異なる。文は副作用のために実行され、式は値を返す。

# Python: if は文(statement)
if score >= 90:
    grade = "A"
elif score >= 80:
    grade = "B"
elif score >= 70:
    grade = "C"
elif score >= 60:
    grade = "D"
else:
    grade = "F"
 
# 三項演算子(式)— 条件式
grade = "A" if score >= 90 else "B" if score >= 80 else "C"
 
# 三項演算子の実務的な使い方
status = "active" if user.is_verified else "pending"
display_name = user.nickname if user.nickname else user.email
max_val = a if a > b else b
 
# 複雑な条件は三項演算子を避け、通常の if 文を使う
# ❌ 読みにくい
result = "A" if x > 90 else "B" if x > 80 else "C" if x > 70 else "D" if x > 60 else "F"
 
# ✅ 読みやすい
if x > 90:
    result = "A"
elif x > 80:
    result = "B"
elif x > 70:
    result = "C"
elif x > 60:
    result = "D"
else:
    result = "F"
// Rust: if は式(expression)→ 値を返す
let grade = if score >= 90 {
    "A"
} else if score >= 80 {
    "B"
} else if score >= 70 {
    "C"
} else if score >= 60 {
    "D"
} else {
    "F"
};  // セミコロンで束縛
 
// 1行で使える(短い条件の場合)
let abs_val = if x >= 0 { x } else { -x };
let min_val = if a < b { a } else { b };
 
// ブロック内で複数の文を含む場合、最後の式が値になる
let description = if score >= 90 {
    let prefix = "Excellent";
    let emoji = "🌟";
    format!("{} {}", prefix, emoji)  // これが返る値
} else {
    "Keep trying".to_string()
};
 
// if let — Option や Result のパターンマッチ簡略版
let config_value = if let Some(val) = config.get("timeout") {
    val.parse::<u64>().unwrap_or(30)
} else {
    30  // デフォルト値
};
// Kotlin: if も when も式
val grade = if (score >= 90) "A" else if (score >= 80) "B" else "C"
 
// 複数行の場合、最後の式が値になる
val result = if (score >= 90) {
    println("Great job!")
    "A"  // この値が返る
} else {
    println("Keep going")
    "B"
}
 
// when 式(Kotlin 独自の強力な分岐)
val result = when {
    score >= 90 -> "A"
    score >= 80 -> "B"
    score >= 70 -> "C"
    score >= 60 -> "D"
    else -> "F"
}
 
// when を値ベースで使用
val typeDescription = when (val day = getDayOfWeek()) {
    "Mon", "Tue", "Wed", "Thu", "Fri" -> "Weekday"
    "Sat", "Sun" -> "Weekend"
    else -> "Unknown"
}
 
// when で型チェック
fun describe(obj: Any): String = when (obj) {
    is Int -> "Integer: $obj"
    is String -> "String of length ${obj.length}"
    is List<*> -> "List of size ${obj.size}"
    else -> "Unknown type"
}
 
// when で範囲チェック
val category = when (age) {
    in 0..12 -> "Child"
    in 13..17 -> "Teen"
    in 18..64 -> "Adult"
    in 65..Int.MAX_VALUE -> "Senior"
    else -> "Invalid"
}
// Swift: if は文だが、if let / guard let が強力
let score = 85
 
// if let(Optional バインディング)
if let username = optionalUsername {
    print("Hello, \(username)")
} else {
    print("No username")
}
 
// guard let(早期リターン向け)
func processUser(_ user: User?) -> String {
    guard let user = user else {
        return "No user"
    }
    guard user.isActive else {
        return "Inactive user"
    }
    return "Processing \(user.name)"
}
 
// Swift 5.9+: if 式として使用可能
let grade = if score >= 90 { "A" } else if score >= 80 { "B" } else { "C" }
// Scala: if は式(常に値を返す)
val grade = if (score >= 90) "A"
            else if (score >= 80) "B"
            else if (score >= 70) "C"
            else "F"
 
// ブロック式
val result = if (condition) {
  val computed = heavyComputation()
  computed * 2  // 最後の式が返る値
} else {
  defaultValue
}
-- Haskell: if は式(else が必須)
grade = if score >= 90 then "A"
        else if score >= 80 then "B"
        else if score >= 70 then "C"
        else "F"
 
-- ガード(関数定義での条件分岐)
bmiCategory bmi
  | bmi < 18.5 = "Underweight"
  | bmi < 25.0 = "Normal"
  | bmi < 30.0 = "Overweight"
  | otherwise   = "Obese"

1.2 文 vs 式の設計哲学比較

文ベース(Statement-based)の言語:
  C, Java, Python, JavaScript, Go
  → if は値を返さない。副作用(代入など)で結果を伝える
  → 三項演算子(? :)が式としての分岐を補完

式ベース(Expression-based)の言語:
  Rust, Kotlin, Scala, Haskell, OCaml, F#, Elixir
  → if は値を返す。変数束縛と自然に組み合わせられる
  → 「全ての構文が値を返す」一貫性

式ベースの利点:
  1. 変数の不変性を保ちやすい(let x = if ... で一度だけ代入)
  2. 初期化忘れがない(else がないとコンパイルエラー)
  3. 関数型スタイルとの親和性が高い
  4. 型推論が効きやすい

文ベースの利点:
  1. 馴染みやすい(C から続く伝統)
  2. 副作用を明示的に分離できる
  3. void(値なし)の分岐が自然

1.3 switch / match

// JavaScript: switch文(fall-through に注意)
switch (day) {
    case "Mon": case "Tue": case "Wed":
    case "Thu": case "Fri":
        type = "Weekday";
        break;      // break 忘れ → fall-through(バグの温床)
    case "Sat": case "Sun":
        type = "Weekend";
        break;
    default:
        type = "Unknown";
}
 
// switch の実務的な使用例: HTTPステータスコードの分類
function categorizeStatus(code) {
    switch (true) {
        case code >= 200 && code < 300:
            return "Success";
        case code >= 300 && code < 400:
            return "Redirect";
        case code >= 400 && code < 500:
            return "Client Error";
        case code >= 500:
            return "Server Error";
        default:
            return "Informational";
    }
}
 
// switch の代替: オブジェクトマップ(推奨パターン)
const statusMessages = {
    200: "OK",
    201: "Created",
    204: "No Content",
    400: "Bad Request",
    401: "Unauthorized",
    403: "Forbidden",
    404: "Not Found",
    500: "Internal Server Error",
};
const message = statusMessages[code] ?? "Unknown Status";
// TypeScript: switch で型の絞り込み(Narrowing)
type Shape =
    | { kind: "circle"; radius: number }
    | { kind: "rectangle"; width: number; height: number }
    | { kind: "triangle"; base: number; height: number };
 
function area(shape: Shape): number {
    switch (shape.kind) {
        case "circle":
            // ここでは shape は { kind: "circle"; radius: number }
            return Math.PI * shape.radius ** 2;
        case "rectangle":
            // ここでは shape は { kind: "rectangle"; width: number; height: number }
            return shape.width * shape.height;
        case "triangle":
            return (shape.base * shape.height) / 2;
        default:
            // 網羅性チェック(never 型)
            const _exhaustive: never = shape;
            return _exhaustive;
    }
}
// Rust: match式(網羅性チェック + パターンマッチ)
let type_str = match day {
    "Mon" | "Tue" | "Wed" | "Thu" | "Fri" => "Weekday",
    "Sat" | "Sun" => "Weekend",
    _ => "Unknown",
};
// 全パターンを網羅しないとコンパイルエラー
// fall-through なし(安全)
 
// match で複雑な条件分岐
let message = match status_code {
    200 => "OK",
    201 => "Created",
    204 => "No Content",
    301 | 302 => "Redirect",
    400 => "Bad Request",
    401 => "Unauthorized",
    403 => "Forbidden",
    404 => "Not Found",
    405 => "Method Not Allowed",
    500 => "Internal Server Error",
    502 | 503 => "Service Unavailable",
    code @ 100..=199 => "Informational",
    code @ 200..=299 => "Success",
    code @ 300..=399 => "Redirection",
    code @ 400..=499 => "Client Error",
    code @ 500..=599 => "Server Error",
    _ => "Unknown",
};
// Go: switch(break不要、fall-through は明示的)
switch day {
case "Mon", "Tue", "Wed", "Thu", "Fri":
    typeStr = "Weekday"
case "Sat", "Sun":
    typeStr = "Weekend"
default:
    typeStr = "Unknown"
}
// break 不要(自動的に抜ける)
// fallthrough キーワードで明示的に fall-through
 
// 条件式なしの switch(if-else チェーンの代替)
switch {
case score >= 90:
    grade = "A"
case score >= 80:
    grade = "B"
case score >= 70:
    grade = "C"
case score >= 60:
    grade = "D"
default:
    grade = "F"
}
 
// 型 switch(Go のインターフェース型アサーション)
func describe(i interface{}) string {
    switch v := i.(type) {
    case int:
        return fmt.Sprintf("Integer: %d", v)
    case string:
        return fmt.Sprintf("String: %s", v)
    case bool:
        return fmt.Sprintf("Boolean: %t", v)
    case []int:
        return fmt.Sprintf("Int slice of length %d", len(v))
    default:
        return fmt.Sprintf("Unknown type: %T", v)
    }
}
// C: switch文(整数型のみ、fall-through がデフォルト)
switch (command) {
    case CMD_START:
        initialize();
        break;
    case CMD_STOP:
        cleanup();
        break;
    case CMD_PAUSE:
    case CMD_SUSPEND:  // fall-through(意図的)
        pause();
        break;
    default:
        fprintf(stderr, "Unknown command: %d\n", command);
        break;
}
 
// C の switch の制限
// - 整数型(int, char, enum)のみ
// - 文字列の比較はできない
// - 範囲指定はできない(GCC拡張を除く)
// Java: switch 式(Java 14+)
// 従来の switch 文
String typeStr;
switch (day) {
    case "Mon": case "Tue": case "Wed":
    case "Thu": case "Fri":
        typeStr = "Weekday";
        break;
    case "Sat": case "Sun":
        typeStr = "Weekend";
        break;
    default:
        typeStr = "Unknown";
}
 
// Java 14+: switch 式(アロー構文)
String typeStr = switch (day) {
    case "Mon", "Tue", "Wed", "Thu", "Fri" -> "Weekday";
    case "Sat", "Sun" -> "Weekend";
    default -> "Unknown";
};
 
// Java 14+: switch 式でブロック使用(yield で値を返す)
String result = switch (statusCode) {
    case 200 -> "OK";
    case 404 -> "Not Found";
    default -> {
        logger.warn("Unexpected status: " + statusCode);
        yield "Unknown";
    }
};
 
// Java 21+: パターンマッチング switch
String describe(Object obj) {
    return switch (obj) {
        case Integer i when i > 0 -> "Positive integer: " + i;
        case Integer i -> "Non-positive integer: " + i;
        case String s -> "String: " + s;
        case null -> "null";
        default -> "Unknown: " + obj;
    };
}

1.4 条件演算子と条件式

# Python: 条件式(三項演算子相当)
result = value_if_true if condition else value_if_false
 
# 実務例
display = f"{count} item{'s' if count != 1 else ''}"
log_level = "DEBUG" if is_development else "INFO"
timeout = custom_timeout if custom_timeout is not None else default_timeout
 
# Python: Walrus演算子(:=)— Python 3.8+
# 代入と条件判定を同時に行う
if (n := len(data)) > 10:
    print(f"Data too long: {n}")
 
# while ループでの活用
while (line := file.readline()) != "":
    process(line)
 
# リスト内包表記での活用
results = [y for x in data if (y := expensive_computation(x)) is not None]
// C / C++ / JavaScript / Java: 三項演算子
int abs_val = (x >= 0) ? x : -x;
const char* msg = (err == 0) ? "Success" : "Error";
 
// ネストした三項演算子(非推奨 — 読みにくい)
const char* grade = (score >= 90) ? "A"
                  : (score >= 80) ? "B"
                  : (score >= 70) ? "C"
                  : "F";
# Ruby: 多彩な条件式
# if 修飾子(後置 if)
puts "Adult" if age >= 18
log.warn("Low memory") if memory_usage > 0.9
 
# unless(否定条件)
raise "Not found" unless record
send_notification unless user.opted_out?
 
# 三項演算子
status = active? ? "Active" : "Inactive"
 
# case-when(パターンマッチ風)
result = case score
         when 90..100 then "A"
         when 80..89  then "B"
         when 70..79  then "C"
         when 60..69  then "D"
         else              "F"
         end
 
# case-in(Ruby 3.0+ パターンマッチ)
case user
in { name: String => name, age: (18..) => age }
  puts "#{name} is an adult (#{age})"
in { name: String => name, age: }
  puts "#{name} is a minor (#{age})"
end

2. ループ

2.1 for ループの進化

// C: 古典的な for ループ
for (int i = 0; i < 10; i++) {
    printf("%d\n", i);
}
 
// C: 配列の走査(インデックスベース)
int arr[] = {10, 20, 30, 40, 50};
int len = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < len; i++) {
    printf("arr[%d] = %d\n", i, arr[i]);
}
 
// C: 逆順走査
for (int i = len - 1; i >= 0; i--) {
    printf("arr[%d] = %d\n", i, arr[i]);
}
 
// C: 2重ループ(行列処理)
for (int i = 0; i < rows; i++) {
    for (int j = 0; j < cols; j++) {
        matrix[i][j] = i * cols + j;
    }
}
# Python: for-in(イテレータベース)
for i in range(10):
    print(i)
 
for item in collection:
    print(item)
 
# enumerate(インデックス付き)
for i, item in enumerate(collection):
    print(f"{i}: {item}")
 
# enumerate の開始インデックス指定
for i, line in enumerate(lines, start=1):
    print(f"Line {i}: {line}")
 
# zip(並行イテレーション)
for name, age in zip(names, ages):
    print(f"{name}: {age}")
 
# zip_longest(長さが異なるイテラブルの結合)
from itertools import zip_longest
for a, b in zip_longest([1, 2, 3], [10, 20], fillvalue=0):
    print(f"{a}, {b}")  # (1,10), (2,20), (3,0)
 
# reversed(逆順)
for item in reversed(collection):
    print(item)
 
# sorted(ソート順)
for item in sorted(collection, key=lambda x: x.name):
    print(item)
 
# itertools の活用
from itertools import product, combinations, permutations, chain
 
# デカルト積(全ての組み合わせ)
for x, y in product(range(3), range(3)):
    print(f"({x}, {y})")
 
# 組み合わせ
for a, b in combinations([1, 2, 3, 4], 2):
    print(f"{a}, {b}")  # (1,2), (1,3), (1,4), (2,3), (2,4), (3,4)
 
# 順列
for a, b in permutations([1, 2, 3], 2):
    print(f"{a}, {b}")  # (1,2), (1,3), (2,1), (2,3), (3,1), (3,2)
 
# チェーン(複数のイテラブルを連結)
for item in chain([1, 2], [3, 4], [5, 6]):
    print(item)  # 1, 2, 3, 4, 5, 6
 
# 辞書の走査
for key, value in config.items():
    print(f"{key} = {value}")
 
# 辞書内包表記(ループ + 変換)
squared = {x: x**2 for x in range(10)}
filtered = {k: v for k, v in data.items() if v > threshold}
// Rust: for-in(所有権を意識)
for item in &collection {     // 不変借用(コレクション保持)
    println!("{}", item);
}
 
for item in &mut collection { // 可変借用(要素を変更)
    *item += 1;
}
 
for item in collection {      // ムーブ(コレクション消費)
    println!("{}", item);
}
// collection はもう使えない
 
// レンジ
for i in 0..10 {        // 0〜9
    println!("{}", i);
}
for i in 0..=10 {       // 0〜10(inclusive)
    println!("{}", i);
}
 
// ステップ(step_by)
for i in (0..100).step_by(5) {
    println!("{}", i);  // 0, 5, 10, ..., 95
}
 
// 逆順
for i in (0..10).rev() {
    println!("{}", i);  // 9, 8, 7, ..., 0
}
 
// enumerate(インデックス付き)
for (i, item) in collection.iter().enumerate() {
    println!("{}: {}", i, item);
}
 
// zip(並行イテレーション)
for (name, age) in names.iter().zip(ages.iter()) {
    println!("{}: {}", name, age);
}
 
// windows と chunks
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
 
// スライディングウィンドウ
for window in data.windows(3) {
    println!("{:?}", window);  // [1,2,3], [2,3,4], [3,4,5], ...
}
 
// チャンク分割
for chunk in data.chunks(3) {
    println!("{:?}", chunk);  // [1,2,3], [4,5,6], [7,8]
}
// Go: for だけ(while も loop も for で表現)
for i := 0; i < 10; i++ {  // C風 for
    fmt.Println(i)
}
 
for condition {              // while 相当
    // ...
}
 
for {                        // 無限ループ
    // ...
}
 
for i, v := range slice {   // for-range(スライス)
    fmt.Println(i, v)
}
 
// for-range(マップ)
for key, value := range myMap {
    fmt.Printf("%s: %v\n", key, value)
}
 
// for-range(文字列)— rune(Unicodeコードポイント)単位
for i, r := range "Hello, 世界" {
    fmt.Printf("index=%d, rune=%c\n", i, r)
}
 
// for-range(チャネル)
for msg := range ch {
    fmt.Println("Received:", msg)
}
 
// インデックスのみ(値を捨てる)
for i := range slice {
    fmt.Println(i)
}
 
// 値のみ(インデックスを捨てる)
for _, v := range slice {
    fmt.Println(v)
}
// JavaScript: 4種類の for ループ
 
// 1. 古典的 for
for (let i = 0; i < 10; i++) {
    console.log(i);
}
 
// 2. for...in(オブジェクトのキーを走査 — 配列には非推奨)
for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(`${key}: ${obj[key]}`);
    }
}
 
// 3. for...of(イテラブルの値を走査 — ES6+)
for (const item of array) {
    console.log(item);
}
 
// 4. forEach メソッド(配列専用)
array.forEach((item, index) => {
    console.log(`${index}: ${item}`);
});
 
// for...of の応用
// Map の走査
for (const [key, value] of map) {
    console.log(`${key}: ${value}`);
}
 
// Set の走査
for (const item of set) {
    console.log(item);
}
 
// entries()(インデックス付き)
for (const [i, item] of array.entries()) {
    console.log(`${i}: ${item}`);
}
 
// 注意: for...in vs for...of
// for...in: プロトタイプチェーンのプロパティも列挙(危険)
// for...of: Symbol.iterator プロトコルに従う(安全)

2.2 while と loop

// Rust: loop(無限ループ、値を返せる)
let result = loop {
    let input = get_input();
    if input.is_valid() {
        break input.value();  // break で値を返す
    }
    println!("Invalid input, try again");
};
 
// ラベル付きループ(ネストしたループの制御)
'outer: for i in 0..10 {
    for j in 0..10 {
        if i + j > 15 {
            break 'outer;  // 外側のループを脱出
        }
        if j % 2 == 0 {
            continue;  // 内側のループの次の反復へ
        }
        println!("{}, {}", i, j);
    }
}
 
// while let(パターンマッチ付き)
while let Some(item) = iterator.next() {
    println!("{}", item);
}
 
// while let チェーン(Rust 1.64+)
while let Some(item) = stack.pop() {
    match item {
        Item::Value(v) => results.push(v),
        Item::SubList(list) => {
            for sub_item in list.into_iter().rev() {
                stack.push(sub_item);
            }
        }
    }
}
 
// loop + match パターン(状態機械の実装)
enum State { Init, Running, Paused, Done }
 
let mut state = State::Init;
loop {
    state = match state {
        State::Init => {
            initialize();
            State::Running
        }
        State::Running => {
            if should_pause() {
                State::Paused
            } else if is_complete() {
                State::Done
            } else {
                process_next();
                State::Running
            }
        }
        State::Paused => {
            wait_for_resume();
            State::Running
        }
        State::Done => break,
    };
}
# Python: while ループ
 
# 基本的な while
count = 0
while count < 10:
    print(count)
    count += 1
 
# while + else(ループが正常終了した場合に else が実行される)
def find_item(items, target):
    i = 0
    while i < len(items):
        if items[i] == target:
            print(f"Found at index {i}")
            break
        i += 1
    else:
        # break で抜けなかった場合に実行
        print("Not found")
 
# do-while 相当(Python には do-while がない)
while True:
    user_input = input("Enter a number (0 to quit): ")
    if user_input == "0":
        break
    process(user_input)
 
# イテレータを while で消費
it = iter(data)
while (chunk := list(islice(it, 100))):
    process_batch(chunk)
// Go: for だけで全てのループを表現
 
// while 相当
for condition {
    // ...
}
 
// do-while 相当
for {
    doSomething()
    if !condition {
        break
    }
}
 
// ラベル付きループ
OuterLoop:
    for i := 0; i < 10; i++ {
        for j := 0; j < 10; j++ {
            if i+j > 15 {
                break OuterLoop
            }
        }
    }
// Java: do-while(少なくとも1回実行)
Scanner scanner = new Scanner(System.in);
String input;
do {
    System.out.print("Enter command: ");
    input = scanner.nextLine();
    processCommand(input);
} while (!input.equals("quit"));
 
// Java: 拡張 for ループ(for-each)
for (String item : collection) {
    System.out.println(item);
}
 
// Java: ラベル付き break/continue
outer:
for (int i = 0; i < matrix.length; i++) {
    for (int j = 0; j < matrix[i].length; j++) {
        if (matrix[i][j] == target) {
            System.out.printf("Found at (%d, %d)%n", i, j);
            break outer;
        }
    }
}

2.3 イテレータメソッド(関数型スタイル)

// TypeScript: メソッドチェーン
const result = numbers
    .filter(n => n > 0)
    .map(n => n * 2)
    .reduce((sum, n) => sum + n, 0);
 
// vs 命令型
let result = 0;
for (const n of numbers) {
    if (n > 0) {
        result += n * 2;
    }
}
 
// 実務的なメソッドチェーンの例
interface User {
    name: string;
    age: number;
    department: string;
    isActive: boolean;
}
 
// 部門ごとのアクティブユーザー数を集計
const departmentCounts = users
    .filter(user => user.isActive)
    .reduce((acc, user) => {
        acc[user.department] = (acc[user.department] || 0) + 1;
        return acc;
    }, {} as Record<string, number>);
 
// グループ化(Object.groupBy — ES2024)
const grouped = Object.groupBy(users, user => user.department);
 
// flatMap の活用
const allTags = articles
    .flatMap(article => article.tags)
    .filter((tag, i, arr) => arr.indexOf(tag) === i);  // 重複除去
 
// find と findIndex
const firstAdmin = users.find(u => u.role === "admin");
const adminIndex = users.findIndex(u => u.role === "admin");
 
// some と every
const hasAdmin = users.some(u => u.role === "admin");
const allActive = users.every(u => u.isActive);
// Rust: イテレータ(ゼロコスト抽象化)
let result: i32 = numbers.iter()
    .filter(|&&n| n > 0)
    .map(|&n| n * 2)
    .sum();
// コンパイル後は手書きのループと同等の性能
 
// 実務的なイテレータの例
struct Employee {
    name: String,
    department: String,
    salary: u64,
}
 
// 部門ごとの平均給与
let dept_averages: HashMap<String, f64> = employees.iter()
    .fold(HashMap::new(), |mut acc, emp| {
        let entry = acc.entry(emp.department.clone())
            .or_insert((0u64, 0u64));
        entry.0 += emp.salary;
        entry.1 += 1;
        acc
    })
    .into_iter()
    .map(|(dept, (total, count))| {
        (dept, total as f64 / count as f64)
    })
    .collect();
 
// partition: 条件で2つに分割
let (evens, odds): (Vec<i32>, Vec<i32>) = numbers.iter()
    .partition(|&&n| n % 2 == 0);
 
// unzip: ペアのイテレータを2つのコレクションに分割
let (names, ages): (Vec<&str>, Vec<u32>) = people.iter()
    .map(|p| (p.name.as_str(), p.age))
    .unzip();
 
// チェーンの遅延評価を活用した効率的な検索
let first_match = huge_dataset.iter()
    .filter(|item| expensive_check(item))
    .map(|item| transform(item))
    .next();  // 最初の1つだけ計算(残りは評価されない)
# Python: 内包表記(Pythonic なループ)
 
# リスト内包表記
squares = [x**2 for x in range(10)]
evens = [x for x in range(20) if x % 2 == 0]
 
# ネストした内包表記
pairs = [(x, y) for x in range(3) for y in range(3) if x != y]
 
# 辞書内包表記
word_lengths = {word: len(word) for word in words}
 
# 集合内包表記
unique_lengths = {len(word) for word in words}
 
# ジェネレータ式(メモリ効率が良い)
total = sum(x**2 for x in range(1000000))
 
# map, filter, reduce(関数型スタイル)
from functools import reduce
 
squared = list(map(lambda x: x**2, numbers))
positive = list(filter(lambda x: x > 0, numbers))
total = reduce(lambda acc, x: acc + x, numbers, 0)
 
# 実務では内包表記の方が Pythonic
# map/filter よりリスト内包表記が推奨される
squared = [x**2 for x in numbers]              # map 相当
positive = [x for x in numbers if x > 0]       # filter 相当

3. 早期リターンとガード節

3.1 ガード節パターン

// ガード節パターン(ネストを減らす)
// ❌ ネストが深い
fn process(input: Option<&str>) -> Result<String, Error> {
    if let Some(s) = input {
        if !s.is_empty() {
            if s.len() < 100 {
                Ok(s.to_uppercase())
            } else {
                Err(Error::TooLong)
            }
        } else {
            Err(Error::Empty)
        }
    } else {
        Err(Error::Missing)
    }
}
 
// ✅ ガード節で早期リターン
fn process(input: Option<&str>) -> Result<String, Error> {
    let s = input.ok_or(Error::Missing)?;
    if s.is_empty() { return Err(Error::Empty); }
    if s.len() >= 100 { return Err(Error::TooLong); }
    Ok(s.to_uppercase())
}
# Python: ガード節
 
# ❌ ネストが深い
def process_order(order):
    if order is not None:
        if order.is_valid():
            if order.has_items():
                if order.payment_verified():
                    return ship_order(order)
                else:
                    return {"error": "Payment not verified"}
            else:
                return {"error": "No items"}
        else:
            return {"error": "Invalid order"}
    else:
        return {"error": "No order"}
 
# ✅ ガード節で早期リターン
def process_order(order):
    if order is None:
        return {"error": "No order"}
    if not order.is_valid():
        return {"error": "Invalid order"}
    if not order.has_items():
        return {"error": "No items"}
    if not order.payment_verified():
        return {"error": "Payment not verified"}
    return ship_order(order)
// Go: ガード節が標準スタイル
func processUser(userID string) (*User, error) {
    if userID == "" {
        return nil, fmt.Errorf("empty user ID")
    }
 
    user, err := db.FindUser(userID)
    if err != nil {
        return nil, fmt.Errorf("find user %s: %w", userID, err)
    }
 
    if !user.IsActive {
        return nil, fmt.Errorf("user %s is not active", userID)
    }
 
    if user.IsLocked {
        return nil, fmt.Errorf("user %s is locked", userID)
    }
 
    return user, nil
}
// TypeScript: ガード節 + 型の絞り込み
function processInput(input: unknown): string {
    if (input === null || input === undefined) {
        return "No input";
    }
    if (typeof input !== "string") {
        return "Not a string";
    }
    // ここで input は string 型に絞り込まれている
    if (input.length === 0) {
        return "Empty string";
    }
    if (input.length > 100) {
        return "Too long";
    }
    return input.toUpperCase();
}

3.2 ループ内の continue と break

# continue: 現在の反復をスキップ
for item in items:
    if not item.is_valid():
        continue  # 無効なアイテムをスキップ
    if item.is_deleted():
        continue  # 削除済みをスキップ
    process(item)
 
# break: ループを脱出
for item in items:
    if item.matches(target):
        result = item
        break
else:
    # break で抜けなかった場合(見つからなかった場合)
    result = None
// Rust: ラベル付き break/continue
'search: for row in &matrix {
    for &cell in row {
        if cell == target {
            println!("Found: {}", cell);
            break 'search;  // 外側のループも脱出
        }
    }
}
 
// break で値を返す(loop の場合)
let found = 'outer: loop {
    for item in &collection {
        if item.matches(&criteria) {
            break 'outer Some(item);  // 外側の loop から値を返す
        }
    }
    break None;  // 見つからなかった場合
};

4. 制御フローの高度なパターン

4.1 テーブル駆動ディスパッチ

# if-elif チェーンの代替: 辞書ディスパッチ
def handle_command(command: str, args: list[str]) -> str:
    handlers = {
        "help": lambda args: show_help(),
        "list": lambda args: list_items(),
        "add": lambda args: add_item(args[0]) if args else "Missing argument",
        "remove": lambda args: remove_item(args[0]) if args else "Missing argument",
        "search": lambda args: search_items(" ".join(args)),
    }
 
    handler = handlers.get(command)
    if handler is None:
        return f"Unknown command: {command}"
    return handler(args)
// TypeScript: ディスパッチマップ
type Handler = (req: Request) => Response;
 
const routes: Record<string, Handler> = {
    "/api/users": handleUsers,
    "/api/posts": handlePosts,
    "/api/comments": handleComments,
};
 
function router(req: Request): Response {
    const handler = routes[req.path];
    if (!handler) {
        return new Response("Not Found", { status: 404 });
    }
    return handler(req);
}
// Go: ディスパッチテーブル
type CommandHandler func(args []string) error
 
var commands = map[string]CommandHandler{
    "start":   handleStart,
    "stop":    handleStop,
    "status":  handleStatus,
    "restart": handleRestart,
}
 
func dispatch(name string, args []string) error {
    handler, ok := commands[name]
    if !ok {
        return fmt.Errorf("unknown command: %s", name)
    }
    return handler(args)
}

4.2 状態機械パターン

// Rust: 列挙型による状態機械
enum ConnectionState {
    Disconnected,
    Connecting { attempt: u32, max_attempts: u32 },
    Connected { session_id: String },
    Disconnecting,
}
 
fn handle_event(state: ConnectionState, event: Event) -> ConnectionState {
    match (state, event) {
        (ConnectionState::Disconnected, Event::Connect) => {
            ConnectionState::Connecting { attempt: 1, max_attempts: 5 }
        }
        (ConnectionState::Connecting { attempt, max_attempts }, Event::Success(session)) => {
            ConnectionState::Connected { session_id: session }
        }
        (ConnectionState::Connecting { attempt, max_attempts }, Event::Failure) => {
            if attempt < max_attempts {
                ConnectionState::Connecting { attempt: attempt + 1, max_attempts }
            } else {
                ConnectionState::Disconnected
            }
        }
        (ConnectionState::Connected { .. }, Event::Disconnect) => {
            ConnectionState::Disconnecting
        }
        (ConnectionState::Disconnecting, Event::Done) => {
            ConnectionState::Disconnected
        }
        (state, _) => state,  // 無関係なイベントは無視
    }
}
# Python: 状態機械をクラスで実装
from enum import Enum, auto
 
class State(Enum):
    IDLE = auto()
    PROCESSING = auto()
    WAITING = auto()
    ERROR = auto()
    DONE = auto()
 
class StateMachine:
    def __init__(self):
        self.state = State.IDLE
        self._transitions = {
            State.IDLE: {
                "start": self._start_processing,
            },
            State.PROCESSING: {
                "complete": self._complete,
                "error": self._handle_error,
                "wait": self._wait,
            },
            State.WAITING: {
                "resume": self._resume,
                "timeout": self._handle_error,
            },
            State.ERROR: {
                "retry": self._start_processing,
                "abort": self._abort,
            },
        }
 
    def handle_event(self, event: str):
        transitions = self._transitions.get(self.state, {})
        handler = transitions.get(event)
        if handler is None:
            raise ValueError(
                f"Invalid event '{event}' in state {self.state}"
            )
        handler()
 
    def _start_processing(self):
        print("Starting...")
        self.state = State.PROCESSING
 
    def _complete(self):
        print("Done!")
        self.state = State.DONE
 
    def _handle_error(self):
        print("Error occurred")
        self.state = State.ERROR
 
    def _wait(self):
        print("Waiting...")
        self.state = State.WAITING
 
    def _resume(self):
        print("Resuming...")
        self.state = State.PROCESSING
 
    def _abort(self):
        print("Aborted")
        self.state = State.DONE

4.3 再帰 vs ループ

# 再帰による木構造の走査
def tree_sum(node):
    if node is None:
        return 0
    return node.value + tree_sum(node.left) + tree_sum(node.right)
 
# ループ(スタックを使った明示的な走査)
def tree_sum_iterative(root):
    if root is None:
        return 0
    total = 0
    stack = [root]
    while stack:
        node = stack.pop()
        total += node.value
        if node.right:
            stack.append(node.right)
        if node.left:
            stack.append(node.left)
    return total
// Rust: 末尾再帰の最適化(手動)
// 再帰版
fn factorial(n: u64) -> u64 {
    if n <= 1 { 1 } else { n * factorial(n - 1) }
}
 
// ループ版(スタックオーバーフローの心配なし)
fn factorial_iter(n: u64) -> u64 {
    (1..=n).product()
}
 
// アキュムレータパターン(末尾再帰風)
fn factorial_acc(n: u64, acc: u64) -> u64 {
    if n <= 1 { acc } else { factorial_acc(n - 1, n * acc) }
}
-- Haskell: 末尾再帰最適化(TCO)
-- 非末尾再帰(スタックを消費)
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
 
-- 末尾再帰(アキュムレータ)
factorial' :: Integer -> Integer
factorial' n = go n 1
  where
    go 0 acc = acc
    go n acc = go (n - 1) (n * acc)
 
-- fold で表現(最も簡潔)
factorial'' :: Integer -> Integer
factorial'' n = foldl' (*) 1 [1..n]

5. ループ最適化テクニック

5.1 ループ不変式の外出し

# ❌ ループ内で毎回計算
for i in range(len(data)):
    normalized = data[i] / sum(data)  # sum(data) が毎回計算される
    results.append(normalized)
 
# ✅ ループ外で一度だけ計算
total = sum(data)
for i in range(len(data)):
    normalized = data[i] / total
    results.append(normalized)
 
# さらに Pythonic に
total = sum(data)
results = [x / total for x in data]

5.2 ループの展開とバッチ処理

# バッチ処理(データベース操作など)
# ❌ 1件ずつ INSERT(遅い)
for item in items:
    db.execute("INSERT INTO table VALUES (?)", (item,))
 
# ✅ バッチ INSERT(高速)
BATCH_SIZE = 1000
for i in range(0, len(items), BATCH_SIZE):
    batch = items[i:i + BATCH_SIZE]
    db.executemany("INSERT INTO table VALUES (?)", [(item,) for item in batch])
// Rust: chunks によるバッチ処理
let data: Vec<Record> = load_records();
 
for chunk in data.chunks(100) {
    db.bulk_insert(chunk)?;
}
 
// par_chunks で並列バッチ処理(rayon)
use rayon::prelude::*;
data.par_chunks(100)
    .for_each(|chunk| {
        process_batch(chunk);
    });

5.3 短絡評価の活用

# 短絡評価: 条件が確定した時点で評価を停止
# any() — 最初の True で停止
has_error = any(item.is_error() for item in items)
 
# all() — 最初の False で停止
all_valid = all(item.is_valid() for item in items)
 
# 大量データの場合、ジェネレータ式で遅延評価
has_match = any(
    expensive_check(item)
    for item in huge_dataset
)  # 最初のマッチで停止、全件チェックしない

6. 言語間の制御フロー設計比較

6.1 例外的制御フロー

# Python: for-else(ループが break せずに終了した場合に else が実行)
def find_prime_factor(n):
    for i in range(2, int(n**0.5) + 1):
        if n % i == 0:
            return i
    return n  # 素数の場合
 
# Python: try-except を制御フローに使う(Pythonic)
# EAFP: Easier to Ask Forgiveness than Permission
try:
    value = dictionary[key]
except KeyError:
    value = default_value
 
# vs LBYL: Look Before You Leap
if key in dictionary:
    value = dictionary[key]
else:
    value = default_value
 
# 推奨: dict.get() を使う
value = dictionary.get(key, default_value)
// Go: defer(関数終了時に実行)— 制御フローの一種
func processFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close()  // 関数終了時に確実にクローズ
 
    // 複数の defer は LIFO(後入れ先出し)で実行
    defer fmt.Println("Step 3")
    defer fmt.Println("Step 2")
    defer fmt.Println("Step 1")
    // 出力: Step 1, Step 2, Step 3
 
    return processData(f)
}
 
// Go: panic/recover(例外的な状況のみ)
func safeDivide(a, b float64) (result float64, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("panic: %v", r)
        }
    }()
 
    if b == 0 {
        panic("division by zero")
    }
    return a / b, nil
}

6.2 コルーチンと制御フロー

# Python: async/await による制御フロー
import asyncio
 
async def fetch_all(urls):
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_one(session, url) for url in urls]
        results = await asyncio.gather(*tasks)
    return results
 
async def fetch_one(session, url):
    async with session.get(url) as response:
        return await response.json()
 
# 非同期イテレーション
async def process_stream(stream):
    async for chunk in stream:
        await process_chunk(chunk)
// Rust: async/await
async fn fetch_all(urls: Vec<String>) -> Vec<Result<String, Error>> {
    let futures: Vec<_> = urls.iter()
        .map(|url| fetch_one(url))
        .collect();
 
    futures::future::join_all(futures).await
}
 
async fn fetch_one(url: &str) -> Result<String, Error> {
    let response = reqwest::get(url).await?;
    let body = response.text().await?;
    Ok(body)
}

7. アンチパターンとベストプラクティス

7.1 よくあるアンチパターン

# ❌ アンチパターン1: フラグ変数の乱用
found = False
for item in items:
    if item == target:
        found = True
        break
if found:
    process(item)
 
# ✅ 改善: 早期リターンまたは組み込み関数
if target in items:
    process(target)
 
# または
try:
    index = items.index(target)
    process(items[index])
except ValueError:
    pass
# ❌ アンチパターン2: 深いネスト
def validate_and_process(data):
    if data is not None:
        if isinstance(data, dict):
            if "type" in data:
                if data["type"] in VALID_TYPES:
                    if "payload" in data:
                        return process(data["payload"])
                    else:
                        return Error("Missing payload")
                else:
                    return Error("Invalid type")
            else:
                return Error("Missing type")
        else:
            return Error("Not a dict")
    else:
        return Error("No data")
 
# ✅ 改善: ガード節
def validate_and_process(data):
    if data is None:
        return Error("No data")
    if not isinstance(data, dict):
        return Error("Not a dict")
    if "type" not in data:
        return Error("Missing type")
    if data["type"] not in VALID_TYPES:
        return Error("Invalid type")
    if "payload" not in data:
        return Error("Missing payload")
    return process(data["payload"])
// ❌ アンチパターン3: callback hell
getUser(userId, (err, user) => {
    if (err) return handleError(err);
    getOrders(user.id, (err, orders) => {
        if (err) return handleError(err);
        getOrderDetails(orders[0].id, (err, details) => {
            if (err) return handleError(err);
            processDetails(details);
        });
    });
});
 
// ✅ 改善: async/await
async function processUserOrder(userId) {
    try {
        const user = await getUser(userId);
        const orders = await getOrders(user.id);
        const details = await getOrderDetails(orders[0].id);
        return processDetails(details);
    } catch (err) {
        handleError(err);
    }
}
# ❌ アンチパターン4: ループ内の不必要な再計算
for i in range(len(items)):
    for j in range(len(items)):
        distance = compute_distance(items[i], items[j])
        if distance < threshold:
            pairs.append((i, j))
 
# ✅ 改善: 対称性を活用して計算量を半減
for i in range(len(items)):
    for j in range(i + 1, len(items)):  # j > i のみ計算
        distance = compute_distance(items[i], items[j])
        if distance < threshold:
            pairs.append((i, j))
            pairs.append((j, i))  # 対称なペアも追加(必要な場合)

7.2 ベストプラクティスまとめ

1. ネストを浅く保つ
   → ガード節(早期リターン)を活用
   → 最大3段階のネストを目安に

2. 式ベースの分岐を活用する
   → Rust/Kotlin: if 式、match 式
   → 変数の不変性を保つ

3. 適切なループ構文を選ぶ
   → イテレータベース(for-in)が現代の主流
   → 関数型メソッドチェーン(map/filter/reduce)を活用
   → インデックスベースの for は最後の手段

4. 短絡評価を意識する
   → any/all、&&/|| の短絡評価を活用
   → 重い計算は遅延評価で

5. テーブル駆動にする
   → 長い if-elif/switch チェーンはディスパッチテーブルに
   → 保守性と拡張性が向上

6. 網羅性を保証する
   → match/switch で全ケースを明示的に処理
   → ワイルドカードの安易な使用を避ける
   → TypeScript: never 型で網羅性チェック

7. ループ最適化を意識する
   → ループ不変式の外出し
   → バッチ処理の活用
   → 不必要な再計算の排除

実践演習

演習1: 基本的な実装

以下の要件を満たすコードを実装してください。

要件:

  • 入力データの検証を行うこと
  • エラーハンドリングを適切に実装すること
  • テストコードも作成すること
# 演習1: 基本実装のテンプレート
class Exercise1:
    """基本的な実装パターンの演習"""
 
    def __init__(self):
        self.data = []
 
    def validate_input(self, value):
        """入力値の検証"""
        if value is None:
            raise ValueError("入力値がNoneです")
        return True
 
    def process(self, value):
        """データ処理のメインロジック"""
        self.validate_input(value)
        self.data.append(value)
        return self.data
 
    def get_results(self):
        """処理結果の取得"""
        return {
            'count': len(self.data),
            'data': self.data
        }
 
# テスト
def test_exercise1():
    ex = Exercise1()
    assert ex.process(1) == [1]
    assert ex.process(2) == [1, 2]
    assert ex.get_results()['count'] == 2
 
    try:
        ex.process(None)
        assert False, "例外が発生するべき"
    except ValueError:
        pass
 
    print("全テスト合格!")
 
test_exercise1()

演習2: 応用パターン

基本実装を拡張して、以下の機能を追加してください。

# 演習2: 応用パターン
from typing import List, Dict, Optional
from datetime import datetime
 
class AdvancedExercise:
    """応用パターンの演習"""
 
    def __init__(self, max_size: int = 100):
        self._items: List[Dict] = []
        self._max_size = max_size
        self._created_at = datetime.now()
 
    def add(self, key: str, value: any) -> bool:
        """アイテムの追加(サイズ制限付き)"""
        if len(self._items) >= self._max_size:
            return False
        self._items.append({
            'key': key,
            'value': value,
            'timestamp': datetime.now().isoformat()
        })
        return True
 
    def find(self, key: str) -> Optional[Dict]:
        """キーによる検索"""
        for item in reversed(self._items):
            if item['key'] == key:
                return item
        return None
 
    def remove(self, key: str) -> bool:
        """キーによる削除"""
        for i, item in enumerate(self._items):
            if item['key'] == key:
                self._items.pop(i)
                return True
        return False
 
    def stats(self) -> Dict:
        """統計情報"""
        return {
            'total_items': len(self._items),
            'max_size': self._max_size,
            'usage_percent': len(self._items) / self._max_size * 100,
            'uptime': str(datetime.now() - self._created_at)
        }
 
# テスト
def test_advanced():
    ex = AdvancedExercise(max_size=3)
    assert ex.add("a", 1) == True
    assert ex.add("b", 2) == True
    assert ex.add("c", 3) == True
    assert ex.add("d", 4) == False  # サイズ制限
    assert ex.find("b")['value'] == 2
    assert ex.remove("b") == True
    assert ex.find("b") is None
    stats = ex.stats()
    assert stats['total_items'] == 2
    print("応用テスト全合格!")
 
test_advanced()

演習3: パフォーマンス最適化

以下のコードのパフォーマンスを改善してください。

# 演習3: パフォーマンス最適化
import time
from functools import lru_cache
 
# 最適化前(O(n^2))
def slow_search(data: list, target: int) -> int:
    """非効率な検索"""
    for i in range(len(data)):
        for j in range(i + 1, len(data)):
            if data[i] + data[j] == target:
                return (i, j)
    return (-1, -1)
 
# 最適化後(O(n))
def fast_search(data: list, target: int) -> tuple:
    """ハッシュマップを使った効率的な検索"""
    seen = {}
    for i, num in enumerate(data):
        complement = target - num
        if complement in seen:
            return (seen[complement], i)
        seen[num] = i
    return (-1, -1)
 
# ベンチマーク
def benchmark():
    import random
    data = list(range(5000))
    random.shuffle(data)
    target = data[100] + data[4000]
 
    start = time.time()
    result1 = slow_search(data, target)
    slow_time = time.time() - start
 
    start = time.time()
    result2 = fast_search(data, target)
    fast_time = time.time() - start
 
    print(f"非効率版: {slow_time:.4f}秒")
    print(f"効率版:   {fast_time:.6f}秒")
    print(f"高速化率: {slow_time/fast_time:.0f}倍")
 
benchmark()

ポイント:

  • アルゴリズムの計算量を意識する
  • 適切なデータ構造を選択する
  • ベンチマークで効果を測定する

FAQ

Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?

実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。

Q2: 初心者がよく陥る間違いは何ですか?

基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。

Q3: 実務ではどのように活用されていますか?

このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。


まとめ

構文 文 vs 式 特徴
if (Python, JS) 古典的、三項演算子は式
if (Rust, Kotlin) 値を返せる
switch (JS, C) fall-through に注意
switch (Java 14+) アロー構文、yield
match (Rust) 網羅性チェック、安全
when (Kotlin) 範囲、型チェック対応
for-in - イテレータベース(現代の主流)
for-range (Go) - スライス、マップ、チャネル対応
.filter().map() 関数型スタイル
loop (Rust) break で値を返せる
while let (Rust) - パターンマッチ付きループ

言語ごとのループ構文比較

言語 C風 for for-in while do-while 無限ループ ラベル
C for(;;) - while do-while for(;;) goto
Java for(;;) for(:) while do-while while(true) label
Python - for-in while - while True -
JavaScript for(;;) for-of while do-while while(true) label
Rust - for-in while - loop 'label
Go for for-range for for{...if} for{} label
Kotlin for(;;) for-in while do-while while(true) label

次に読むべきガイド


参考文献

  1. Scott, M. "Programming Language Pragmatics." 4th Ed, Ch.6, Morgan Kaufmann, 2015.
  2. Klabnik, S. & Nichols, C. "The Rust Programming Language." Ch.3, 2023.
  3. Bloch, J. "Effective Java." 3rd Ed, Item 58-65, Addison-Wesley, 2018.
  4. Van Rossum, G. "PEP 20 -- The Zen of Python." python.org, 2004.
  5. Donovan, A. & Kernighan, B. "The Go Programming Language." Ch.1, Addison-Wesley, 2015.
  6. Jemerov, D. & Isakova, S. "Kotlin in Action." Ch.2, Manning, 2017.
  7. Martin, R. "Clean Code." Ch.7 (Error Handling), Prentice Hall, 2008.
  8. "Rust By Example: Flow of Control." doc.rust-lang.org.
  9. Lipovaca, M. "Learn You a Haskell for Great Good!" Ch.4, No Starch Press, 2011.
  10. Odersky, M., Spoon, L. & Venners, B. "Programming in Scala." 5th Ed, Ch.7, Artima, 2021.