ข้ามไปที่เนื้อหา

Ch6: Loops

TL;DR: Loop Paradigms: C++ รองรับคำสั่งลูป for, while, และ do-while โดยที่ do-while จะประเมินเงื่อนไขหลังจากทำโค้ดในบล็อกเสร็จแล้ว ทำให้รับประกันการทำงานอย่างน้อยหนึ่งครั้งเสมอ

⚡ Quick Reference

#include <iostream>

int main() {
    // for (init; condition; increment) - ลูปสำหรับทำซ้ำแบบกำหนดจำนวนครั้ง
    for (int i = 0; i < 5; ++i) {
        // continue - ข้ามคำสั่งที่เหลือในรอบการรันปัจจุบันไปรอบถัดไป
        if (i == 2) continue;
        // break - สั่งหยุดและออกจากลูปการทำงานทันที
        if (i == 4) break;
        std::cout << i << " ";
    }
    std::cout << std::endl;

    // while (condition) - ลูปตรวจสอบเงื่อนไขก่อนเข้ารอบการทำงาน
    int count = 0;
    while (count < 3) {
        std::cout << count << " ";
        ++count;
    }
    std::cout << std::endl;

    // do { ... } while (condition); - ลูปตรวจสอบเงื่อนไขหลังจากทำงานไปแล้วอย่างน้อยหนึ่งรอบ
    int k = 0;
    do {
        std::cout << k << " ";
        ++k;
    } while (k < 3);
    std::cout << std::endl;

    return 0;
}

🧠 Core Concepts

  • Loop Paradigms: C++ รองรับคำสั่งลูป for, while, และ do-while โดยที่ do-while จะประเมินเงื่อนไขหลังจากทำโค้ดในบล็อกเสร็จแล้ว ทำให้รับประกันการทำงานอย่างน้อยหนึ่งครั้งเสมอ
  • Standard Iterator Types: size_t เป็นชนิดข้อมูลจำนวนเต็มไม่มีเครื่องหมายมาตรฐานสำหรับการระบุขนาด ในระบบ 64-bit จะใช้ขนาด 8 Byte และจะเกิดการ Underflow หากมีค่าลดลงต่ำกว่าศูนย์
  • Range-Based Iteration: คำสั่ง Range-based for ช่วยอำนวยความสะดวกในการวิ่งวนผ่าน Collection หรือ Initializer List โดยไม่ต้องคุม Index เอง แต่ลูปชนิดนี้ไม่รองรับการวนย้อนกลับแบบดั้งเดิม

⚠️ Pitfalls (Quick Scan)

ข้อผิดพลาด วิธีแก้
การเรียกใช้ตัวนับลูปนอกสโคปของลูป ประกาศตัวแปรนอกลูปหากจำเป็นต้องนำค่าสุดท้ายมาประมวลผลต่อ
การระบุขอบเขตลูปแบบ Hardcoded ใช้ const size_t ในการระบุจำนวนรอบการวนซ้ำ
ลืมใส่การอัปเดตค่า (Increment) ในลูป while ตรวจสอบให้มีคำสั่งอัปเดตตัวแปรควบคุมอยู่ในบล็อกลูปเสมอ
เข้าใจผิดว่าลูป do-while จะไม่ทำงานหากเงื่อนไขเริ่มต้นเป็นเท็จ ใช้ลูป for หรือ while หากกรณีไม่วนลูปเลยเป็นสิ่งสำคัญ
การลดค่าของข้อมูล unsigned size_t ให้ต่ำกว่าศูนย์ หลีกเลี่ยงการลดค่าลงต่ำกว่าศูนย์ เช่น เปลี่ยนเงื่อนไขเปรียบเทียบเป็น i > 0 (11.11)
ใช้เงื่อนไขตรวจสอบ i >= 0 กับตัวแปรไม่มีเครื่องหมาย (unsigned) ในลูปลดค่า เปลี่ยนไปใช้ชนิดข้อมูลแบบมีเครื่องหมาย (signed) หรือปรับเงื่อนไขเป็น i > 0 (11.11)
พยายามใช้ Range-based for วนลูปย้อนกลับ ใช้ลูปนับดั้งเดิมหรือใช้ std::reverse_iterator ในการวนย้อนกลับ (11.11)
ลืมรีเซ็ตค่าตัวนับภายในในลูปซ้อน (Nested Loops) เขียนคำสั่งกำหนดค่าตัวแปรควบคุมภายในใหม่ทุกครั้งหลังเริ่มลูปชั้นนอก (11.12)
เรียกใช้คำสั่ง continue โดยไม่ทำการอัปเดตตัวนับในลูป while อัปเดตตัวควบคุมการวนลูปทันทีก่อนเรียกสั่งงาน continue (11.13)
ความเข้าใจคลาดเคลื่อนเกี่ยวกับความสำคัญของตัวดำเนินการ Comma ใช้วงเล็บครอบกลุ่มคำสั่งหรือแยกบรรทัดเขียนเพื่อป้องกันความสับสน (11.4)

📖 Full Details

Cause → Effect → Fix พร้อม timestamp (คลิกเพื่อดู) * **การเรียกใช้ตัวนับลูปนอกสโคปของลูป** -> **เกิด Compile-time error เนื่องจากตัวแปรถูกทำลายเมื่อหลุดจากลูป** -> **ประกาศตัวแปรนอกลูปหากจำเป็นต้องนำค่าสุดท้ายมาประมวลผลต่อ (12:20)** * **การระบุขอบเขตลูปแบบ Hardcoded** -> **โปรแกรมดูแลรักษายากขึ้นเมื่อขนาดของข้อมูลหรือ Array เปลี่ยนแปลง** -> **ใช้ `const size_t` ในการระบุจำนวนรอบการวนซ้ำ (15:48)** * **ลืมใส่การอัปเดตค่า (Increment) ในลูป `while`** -> **เกิด Infinite Loop ทำให้โปรแกรมทำงานไม่หยุด** -> **ตรวจสอบให้มีคำสั่งอัปเดตตัวแปรควบคุมอยู่ในบล็อกลูปเสมอ (13:00)** * **เข้าใจผิดว่าลูป `do-while` จะไม่ทำงานหากเงื่อนไขเริ่มต้นเป็นเท็จ** -> **บล็อกคำสั่งจะทำงานก่อนหนึ่งรอบเสมอโดยไม่สนใจเงื่อนไขเริ่มต้น** -> **ใช้ลูป `for` หรือ `while` หากกรณีไม่วนลูปเลยเป็นสิ่งสำคัญ (18:00)** * **การลดค่าของข้อมูล unsigned `size_t` ให้ต่ำกว่าศูนย์** -> **เกิดค่าเกิดขีดจำกัดล่าง (Underflow) วนกลับไปเป็นค่าบวกที่สูงที่สุด** -> **หลีกเลี่ยงการลดค่าลงต่ำกว่าศูนย์ เช่น เปลี่ยนเงื่อนไขเปรียบเทียบเป็น `i > 0` (11.11)** * **ใช้เงื่อนไขตรวจสอบ `i >= 0` กับตัวแปรไม่มีเครื่องหมาย (unsigned) ในลูปลดค่า** -> **เกิดลูปอนันต์ (Infinite loop) เนื่องจากตัวแปรไม่มีเครื่องหมายไม่มีทางติดลบ** -> **เปลี่ยนไปใช้ชนิดข้อมูลแบบมีเครื่องหมาย (signed) หรือปรับเงื่อนไขเป็น `i > 0` (11.11)** * **พยายามใช้ Range-based for วนลูปย้อนกลับ** -> **ไม่มีไวยากรณ์รองรับการวนย้อนกลับในตัวภาษาโดยตรง** -> **ใช้ลูปนับดั้งเดิมหรือใช้ `std::reverse_iterator` ในการวนย้อนกลับ (11.11)** * **ลืมรีเซ็ตค่าตัวนับภายในในลูปซ้อน (Nested Loops)** -> **ลูปชั้นในจะไม่ถูกเรียกทำงานในรอบถัดไปของลูปนอก** -> **เขียนคำสั่งกำหนดค่าตัวแปรควบคุมภายในใหม่ทุกครั้งหลังเริ่มลูปชั้นนอก (11.12)** * **เรียกใช้คำสั่ง `continue` โดยไม่ทำการอัปเดตตัวนับในลูป `while`** -> **คำสั่งข้ามทำให้ข้ามฟังก์ชันการอัปเดตตัวแปรจนเกิดลูปอนันต์** -> **อัปเดตตัวควบคุมการวนลูปทันทีก่อนเรียกสั่งงาน `continue` (11.13)** * **ความเข้าใจคลาดเคลื่อนเกี่ยวกับความสำคัญของตัวดำเนินการ Comma** -> **ตัวแปรจะได้รับการกำหนดค่าเฉพาะส่วนขวาสุดเท่านั้นไม่ใช่เป็น Tuple** -> **ใช้วงเล็บครอบกลุ่มคำสั่งหรือแยกบรรทัดเขียนเพื่อป้องกันความสับสน (11.4)**

📎 Repo Files

  • 11.Loops/11.2ForLoop/main.cpp
  • 11.Loops/11.3ForLoopMultipleDeclarations/main.cpp
  • 11.Loops/11.4CommaOperator/main.cpp
  • 11.Loops/11.5RangeBasedForLoop/main.cpp
  • 11.Loops/11.6WhileLoop/main.cpp
  • 11.Loops/11.7HugeLoopsWithOutput/main.cpp
  • 11.Loops/11.8DoWhileLoop/main.cpp
  • 11.Loops/11.9InfiniteLoops/main.cpp
  • 11.Loops/11.10InfiniteLoopPractice/main.cpp
  • 11.Loops/11.11DecrementingLoops/main.cpp
  • 11.Loops/11.12NestedLoops/main.cpp
  • 11.Loops/11.13BreakAndContinue/main.cpp
  • 11.Loops/11.14FixCalculator/main.cpp
  • 11.Loops/11.15ForLoopWithInitCondition/main.cpp