ลูปต้องมีทางหยุดเสมอ
ตัวแปรควบคุมต้องถูกอัปเดตทุกครั้งจนบรรลุเงื่อนไขหยุด — Infinite Loop คือความผิดพลาดที่เลวร้ายที่สุด
Iteration & Recursion
ปูพื้นฐาน Iteration ตั้งแต่แนวคิดของลูป for/while/do...while การเปลี่ยน state ความซับซ้อน O(n)/O(1) ไปจนถึงการเลือกใช้ให้เหมาะกับโจทย์
Iteration (การทำซ้ำ) คือเทคนิคการเขียนโปรแกรมที่ให้โค้ดทำงานเดิมซ้ำหลายรอบผ่านโครงสร้างลูป เช่น for, while, do...while โดยใช้ตัวแปรเดิมอัปเดตสถานะไปทีละรอบจนกว่าเงื่อนไขหยุดจะเป็นจริง
ภาพจำง่าย: เดินทีละก้าวบนทางราบ ทุกก้าวอยู่ในฟังก์ชันเดิม ไม่มีการเรียกฟังก์ชันชั้นใหม่ ไม่มีการซ้อน call stack
JavaScript มีโครงสร้างลูปหลัก 3 แบบ — แต่ละแบบเหมาะกับสถานการณ์ต่างกัน มาดูรูปแบบและจุดเด่นของแต่ละแบบ
for / while / do...while — มีที่ใช้ต่างกัน
// 1. for loop — รู้จำนวนรอบแน่นอน
for (let i = 0; i < 5; i += 1) {
console.log("รอบที่", i + 1);
}
// 2. while loop — วนตราบใดที่เงื่อนไขเป็นจริง
let count = 0;
while (count < 3) {
console.log("count:", count);
count += 1;
}
// 3. do...while loop — รับประกันอย่างน้อย 1 รอบ
let attempt = 0;
do {
console.log("attempt:", attempt);
attempt += 1;
} while (attempt < 2);⚠️ Infinite Loop — ระวังลูปไม่จบ
สาเหตุอันดับ 1 ของโปรแกรมค้างคือลูปที่เงื่อนไขไม่เป็นเท็จ เช่น ลืมอัปเดตตัวแปรควบคุม (ลืม i++) หรือตั้งเงื่อนไขผิด (while (true) โดยไม่มี break) — ต้องตรวจสอบเสมอว่าเงื่อนไขลูปถึงจุดหยุดได้จริง
ไม่ว่าลูปแบบไหน หัวใจสำคัญคือ 3 ส่วน: (1) จุดเริ่มต้น (2) เงื่อนไขหยุด (3) การอัปเดตค่าทีละรอบ — ถ้าขาดอย่างใดอย่างหนึ่ง ลูปอาจทำงานผิดหรือไม่จบ
โจทย์ factorial (n!) เหมาะมากสำหรับทำความเข้าใจ iteration — เราคูณสะสมผลลัพธ์เข้าไปในตัวแปร result ทีละรอบจนถึง n
ใช้ for loop คูณสะสมทีละรอบ — result เปลี่ยนค่าจาก 1 → 2 → 6 → 24 → 120
function factorialIteration(n) {
if (n < 0) {
throw new Error("n ต้องมากกว่าหรือเท่ากับ 0");
}
let result = 1;
for (let i = 2; i <= n; i += 1) {
result *= i;
}
return result;
}
console.log(factorialIteration(5)); // 120อธิบายทีละส่วน: • `let result = 1` — ตัวสะสมผลลัพธ์เริ่มต้นที่ 1 (เพราะการคูณต้องเริ่มจาก 1 ไม่ใช่ 0) • `for (let i = 2; i <= n; i += 1)` — เริ่ม i=2 (ข้าม 1 เพราะคูณ 1 ไม่เปลี่ยนค่า) วนจนถึง n • `result *= i` — คูณค่าสะสมด้วย i ในแต่ละรอบ • ถ้า `n = 0` หรือ `n = 1` → for loop ไม่รัน → return 1 (ถูกต้องตามนิยาม 0! = 1! = 1)
เวลามอง iteration ให้ดูว่าแต่ละรอบตัวแปรสำคัญเปลี่ยนจากค่าเดิมเป็นค่าใหม่อย่างไร — เรียกแนวนี้ว่า state transition หรือการเปลี่ยนสถานะทีละรอบ
แนวคิดสำคัญ: iteration ไม่ต้องสร้าง call stack เพิ่ม — ทุกอย่างเกิดขึ้นภายในฟังก์ชันเดิม ตัวแปร i และ result ถูกอัปเดตทับค่ากันไปเรื่อย ๆ ทำให้ใช้หน่วยความจำน้อย (O(1))
while loop เหมาะกับสถานการณ์ที่เราไม่รู้ล่วงหน้าว่าต้องวนกี่รอบ แต่รู้จักเงื่อนไขหยุด — เช่น อ่านข้อมูลเรื่อย ๆ จนกว่าจะหมด, รอ input จากผู้ใช้, หรือหาตัวแรกที่เข้าเงื่อนไข
จำนวนรอบไม่ตายตัว — ขึ้นอยู่กับ input
// ตัวอย่าง: หาว่าต้องคูณ 2 กี่ครั้งถึงจะเกิน 1000
function stepsToExceed1000(start) {
let value = start;
let steps = 0;
while (value <= 1000) {
value *= 2;
steps += 1;
}
return steps;
}
console.log(stepsToExceed1000(1)); // 10 (2^10 = 1024)
console.log(stepsToExceed1000(500)); // 1 (500×2 = 1000 → เกินตอน 1000×2)จุดที่ต้องระวัง: • while ตรวจเงื่อนไขก่อนทำเสมอ — ถ้าเงื่อนไขเป็นเท็จตั้งแต่แรก จะไม่เข้าลูปเลย • ต้องมีโค้ดภายในลูปที่พาเข้าใกล้จุดหยุด — ไม่งั้นจะกลายเป็น infinite loop
do...while ทำงานเหมือน while กลับด้าน — ทำก่อนหนึ่งรอบแล้วค่อยตรวจเงื่อนไข เหมาะกับงานที่ต้องทำงานอย่างน้อยครั้งเดียวเสมอ เช่น ทอยลูกเต๋าจนกว่าจะออก 6 หรืออ่านบรรทัดแรกของไฟล์
สุ่มไปเรื่อย ๆ — อย่างน้อยต้องสุ่ม 1 ครั้งเสมอ
// ตัวอย่าง: สุ่มเลข 1-6 จนกว่าจะได้ 6
function rollUntilSix() {
let totalRolls = 0;
let value;
do {
value = Math.floor(Math.random() * 6) + 1;
totalRolls += 1;
console.log("รอบที่", totalRolls, "ได้", value);
} while (value !== 6);
return totalRolls;
}
rollUntilSix();สรุปสั้น: • for — รู้จำนวนรอบแน่นอน (อาเรย์, range) • while — ไม่รู้จำนวนรอบ แต่รู้เงื่อนไขหยุด • do...while — ต้องทำงานอย่างน้อย 1 ครั้งก่อนเช็กเงื่อนไข
Space Complexity = O(1) — ฟังก์ชันแบบ iterative ใช้ตัวแปรเพิ่มแค่ตัวนับและตัวสะสมผลลัพธ์ ไม่ต้องสร้างโครงสร้างข้อมูลใหม่ ไม่ต้องเพิ่ม call stack เหมือน recursion
| n | รอบ (Worst Case) | หน่วยความจำ |
|---|---|---|
| 10 | 10 | O(1) |
| 100 | 100 | O(1) |
| 1,000 | 1,000 | O(1) |
| 1,000,000 | 1,000,000 | O(1) |
หัวใจสำคัญของ Iteration ที่ต้องจำให้ขึ้นใจ
ตัวแปรควบคุมต้องถูกอัปเดตทุกครั้งจนบรรลุเงื่อนไขหยุด — Infinite Loop คือความผิดพลาดที่เลวร้ายที่สุด
for — รู้จำนวนรอบ, while — ไม่รู้จำนวนรอบแต่รู้เงื่อนไขหยุด, do...while — ต้องการอย่างน้อย 1 รอบ
คูณสะสมเริ่มที่ 1, บวกสะสมเริ่มที่ 0, หาค่าสูงสุดเริ่มที่ -Infinity, หาค่าต่ำสุดเริ่มที่ Infinity
วาดตาราง trace 2-3 รอบเพื่อยืนยันว่าจำนวนรอบตรงตามที่ต้องการ — แค่เปลี่ยน < เป็น <= ก็เปลี่ยนผลลัพธ์ทั้งโปรแกรม
จุดเด่นที่สุดของ Iteration คือใช้หน่วยความจำคงที่ — ไม่มี call stack เพิ่ม จึงปลอดภัยสำหรับงานที่มีปริมาณข้อมูลมาก
ทดสอบความเข้าใจก่อนเริ่มทำแบบฝึกหัด
คิดถึงสถานการณ์ที่เราไม่รู้ว่า input จะมีขนาดเท่าไหร่
แบบฝึกหัด 3 ข้อนี้จะช่วยให้คุณเขียน Iteration ได้คล่อง — เริ่มจาก factorial พื้นฐาน ไปจนถึงผลรวม และการหาค่าสูงสุดในอาเรย์