กระบวนทัศน์การเขียนโปรแกรม
หนึ่งในอุปสรรคที่ใหญ่ที่สุดในการทำความเข้าใจการเขียนโปรแกรมเชิงฟังก์ชัน สำหรับผู้ที่มีพื้นฐานมาจากการเขียนโปรแกรมเชิงอิมเพอเรทีฟ (Imperative) คือการเปลี่ยนกระบวนการคิด เพราะโปรแกรมเชิงอิมเพอเรทีฟจะบอกว่าต้องทำสิ่งนั้นอย่างไร (How) ทีละขั้นตอน ขณะที่โปรแกรมเชิงประกาศ (Declarative) จะบอกว่าต้องการผลลัพธ์อะไร (What) ลองมาดูตัวอย่างการหาผลรวมของตัวเลขตั้งแต่ 1 ถึง 10 เพื่อเปรียบเทียบความแตกต่างนี้กัน
เชิงอิมเพอเรทีฟ
#![allow(unused)]
fn main() {
let mut sum = 0;
for i in 1..11 {
sum += i;
}
println!("{sum}");
}
สำหรับการเขียนโปรแกรมเชิงอิมเพอเรทีฟ เรามักจะต้องสวมบทบาทเป็นคอมไพเลอร์เพื่อไล่โค้ด (Trace) ดูทีละบรรทัดว่าเกิดอะไรขึ้นบ้าง ในที่นี้ เราเริ่มต้นด้วยการกำหนดค่า sum เป็น 0 จากนั้นจึงวนลูปผ่านช่วงตัวเลขตั้งแต่ 1 ถึง 10 โดยในแต่ละรอบของลูป เราจะนำตัวเลขปัจจุบันไปบวกสะสมเข้าไปใน sum แล้วสุดท้ายจึงพิมพ์ค่าออกมา
i | sum |
|---|---|
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 15 |
| 6 | 21 |
| 7 | 28 |
| 8 | 36 |
| 9 | 45 |
| 10 | 55 |
นี่คือจุดเริ่มต้นของการเขียนโปรแกรมที่คนส่วนใหญ่คุ้นเคย ซึ่งเราถูกสอนมาว่าโปรแกรมคอมพิวเตอร์คือลำดับของขั้นตอนคำสั่ง (Step-by-step instructions)
แบบประกาศ (Declarative)
#![allow(unused)]
fn main() {
println!("{}", (1..11).fold(0, |a, b| a + b));
}
ว้าว! วิธีนี้แตกต่างไปอย่างสิ้นเชิง! เกิดอะไรขึ้นในโค้ดบรรทัดเดียวนี้? ขอให้จำไว้ว่าในแนวคิดแบบ Declarative เรากำลังระบุว่าเราต้องการผลลัพธ์อะไร (What) มากกว่าการสั่งว่าต้องทำอย่างไร (How) โดย fold คือฟังก์ชันที่ทำหน้าที่ประกอบฟังก์ชันเข้าด้วยกัน (Function Composition) ซึ่งชื่อนี้เป็นธรรมเนียมปฏิบัติที่สืบทอดมาจากภาษา Haskell
ในตัวอย่างนี้ เรากำลังประกอบฟังก์ชันการบวก (เขียนในรูปของ closure: |a, b| a + b) เข้ากับช่วงตัวเลขตั้งแต่ 1 ถึง 10 โดยกำหนดให้ 0 เป็นค่าเริ่มต้น ดังนั้นในรอบแรก a จึงมีค่าเป็น 0 ส่วน b คือสมาชิกตัวแรกของช่วง นั่นคือ 1 ผลลัพธ์จึงเป็น 0 + 1 = 1 จากนั้นกระบวนการ fold ก็จะทำงานต่อในรอบถัดไปโดยนำผลลัพธ์เดิมมาเป็นตัวตั้ง a = 1, b = 2 ได้ผลลัพธ์ใหม่เป็น 1 + 2 = 3 กระบวนการนี้จะดำเนินต่อไปเรื่อยๆ จนกระทั่งประมวลผลสมาชิกตัวสุดท้ายของช่วง นั่นคือ 10 ครบถ้วน
a | b | result |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 2 | 3 |
| 3 | 3 | 6 |
| 6 | 4 | 10 |
| 10 | 5 | 15 |
| 15 | 6 | 21 |
| 21 | 7 | 28 |
| 28 | 8 | 36 |
| 36 | 9 | 45 |
| 45 | 10 | 55 |