Dark mode
แนวคิดหลัก
1. Pure Function (ฟังก์ชันบริสุทธิ์)
ฟังก์ชันที่ไม่มีผลกระทบต่อสถานะภายนอก (side effects) และให้ผลลัพธ์ที่คงที่สำหรับอินพุตเดียวกันเสมอ
❌ ตัวอย่างที่ผิด (Impure Function)
js
let count = 0;
function increment() {
count += 1; // เปลี่ยนแปลงสถานะภายนอก
return count;
}
- ปัญหา: ฟังก์ชันนี้เปลี่ยนค่า
count
ซึ่งเป็นตัวแปรภายนอก ทำให้ไม่ใช่ pure function
✅ ตัวอย่างที่ถูกต้อง (Pure Function)
js
function add(a, b) {
return a + b;
}
- เหตุผล: ไม่มีการเปลี่ยนแปลงสถานะภายนอก และผลลัพธ์ขึ้นอยู่กับอินพุตเท่านั้น
2. Immutability (ไม่เปลี่ยนแปลงข้อมูลเดิม)
การหลีกเลี่ยงการแก้ไขข้อมูลต้นฉบับ โดยสร้างข้อมูลใหม่แทน
❌ ตัวอย่างที่ผิด (Mutable)
js
const arr = [1, 2, 3];
arr.push(4); // เปลี่ยนแปลง arr โดยตรง
- ปัญหา: การใช้
push
แก้ไขอาร์เรย์arr
เดิม ทำให้ไม่เป็น immutable
✅ ตัวอย่างที่ถูกต้อง (Immutable)
js
const arr = [1, 2, 3];
const newArr = [...arr, 4];
- เหตุผล: ใช้ spread operator (
...
) เพื่อสร้างอาร์เรย์ใหม่ โดยไม่กระทบarr
เดิม
3. First-class Function (ฟังก์ชันเป็น first-class citizen)
ฟังก์ชันสามารถถูกเก็บในตัวแปร, ส่งผ่านเป็นพารามิเตอร์, หรือส่งกลับจากฟังก์ชันอื่นได้
❌ ตัวอย่างที่ผิด
- ใน JavaScript ฟังก์ชันเป็น first-class citizen โดยธรรมชาติ จึงไม่มีตัวอย่างที่ "ไม่ใช่" first-class function ในบริบทนี้
✅ ตัวอย่างที่ถูกต้อง (First-class Function)
js
const add = (a, b) => a + b;
- เหตุผล: ฟังก์ชันถูกเก็บในตัวแปร
add
ซึ่งแสดงถึงคุณสมบัติของ first-class function
4. Higher-order Function (ฟังก์ชันระดับสูง)
ฟังก์ชันที่รับฟังก์ชันอื่นเป็นพารามิเตอร์หรือส่งฟังก์ชันกลับ
❌ ตัวอย่างที่ผิด (Non-higher-order Function)
js
function add(a, b) {
return a + b;
}
- ปัญหา: ฟังก์ชันนี้ไม่รับฟังก์ชันเป็นพารามิเตอร์หรือส่งฟังก์ชันกลับ จึงไม่ใช่ higher-order function
✅ ตัวอย่างที่ถูกต้อง (Higher-order Function)
js
const add = (a, b) => a + b;
const higherOrder = (fn) => fn(1, 2);
higherOrder(add); // ผลลัพธ์: 3
- เหตุผล:
higherOrder
รับฟังก์ชันfn
เป็นพารามิเตอร์ จึงเป็น higher-order function
5. Recursion (การเรียกฟังก์ชันตัวเอง)
ฟังก์ชันที่เรียกตัวเองเพื่อแก้ปัญหา แทนการใช้ลูป
❌ ตัวอย่างที่ผิด (Non-recursion)
js
function factorial(n) {
let result = 1;
for (let i = 1; i <= n; i++) {
result *= i; // ใช้ลูปแทน recursion
}
return result;
}
- ปัญหา: ใช้ลูปแทนการเรียกฟังก์ชันตัวเอง จึงไม่ใช่ recursion
✅ ตัวอย่างที่ถูกต้อง (Recursion)
js
const factorial = (n) => {
if (n === 1) return 1;
return n * factorial(n - 1);
};
- เหตุผล: ฟังก์ชันเรียกตัวเองเพื่อคำนวณ factorial จึงเป็น recursion
ข้อดี
- ความบริสุทธิ์และความคงที่: Pure functions ทำให้โค้ดคาดเดาได้และทดสอบง่าย
- หลีกเลี่ยงผลข้างเคียง: ลดข้อผิดพลาดจากสถานะที่เปลี่ยนแปลง
- โค้ดกระชับและอ่านง่าย: การใช้ higher-order functions และ composition ทำให้โค้ดสั้นลง
- รองรับการประมวลผลคู่ขนาน: ไม่มีสถานะที่แชร์กัน ทำให้เหมาะกับการทำงานแบบคู่ขนาน
- บำรุงรักษาง่าย: โค้ดที่ไม่มีผลข้างเคียงและ immutable ทำให้ดีบักและปรับปรุงง่าย
ข้อเสีย
- เส้นโค้งการเรียนรู้: ผู้ที่คุ้นเคยกับการเขียนโค้ดแบบ imperative อาจต้องใช้เวลาปรับตัว
- ประสิทธิภาพ: การใช้ recursion มากเกินไปอาจทำให้เกิด stack overflow หรือทำงานช้ากว่าลูป
- การจัดการสถานะ: การหลีกเลี่ยงสถานะที่เปลี่ยนแปลงอาจทำให้การจัดการแอปพลิเคชันซับซ้อนขึ้น
- การสนับสนุนจากภาษา: บางภาษาไม่รองรับ FP อย่างเต็มที่ ซึ่งอาจจำกัดการใช้งาน
เหมาะสำหรับ
- การประมวลผลข้อมูล: เช่น การกรอง, การแปลงข้อมูล, หรือการลดรูป (reduce)
- โปรแกรมแบบคู่ขนานและกระจาย: เนื่องจากไม่มีสถานะที่แชร์กัน
- ซอฟต์แวร์ที่ต้องการความน่าเชื่อถือสูง: เช่น ระบบที่เน้นความปลอดภัยและเสถียรภาพ
- การทดสอบหน่วย (unit testing): Pure functions ทำให้การทดสอบง่ายและแม่นยำ