Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

กระบวนทัศน์การเขียนโปรแกรม

หนึ่งในอุปสรรคที่ใหญ่ที่สุดในการทำความเข้าใจการเขียนโปรแกรมเชิงฟังก์ชัน สำหรับผู้ที่มีพื้นฐานมาจากการเขียนโปรแกรมเชิงอิมเพอเรทีฟ (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 แล้วสุดท้ายจึงพิมพ์ค่าออกมา

isum
11
23
36
410
515
621
728
836
945
1055

นี่คือจุดเริ่มต้นของการเขียนโปรแกรมที่คนส่วนใหญ่คุ้นเคย ซึ่งเราถูกสอนมาว่าโปรแกรมคอมพิวเตอร์คือลำดับของขั้นตอนคำสั่ง (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 ครบถ้วน

abresult
011
123
336
6410
10515
15621
21728
28836
36945
451055