| ตอนที่ 1 | ตอนที่ 2 | ตอนที่ 3 |
TLDR: ภาษา functional ช่วยให้เห็น function composition ได้ชัดเจน ในขณะที่ภาษา imperative อาจะทำ function composition ได้แต่มักจะอ่านยากกว่า
ตอนที่ 1 : map/reduce pipeline
ได้พบโจทย์ปัญหาโปรแกรมข้อหนึ่งในกลุ่ม programming เห็นว่าเป็นปัญหาง่าย ๆ ที่น่าสนใจดี (เปลี่ยนราคาเป็น 100 เพื่อความง่ายในการคิดในใจ)
ร้านหมูกะทะแห่งหนึ่งจัดรายการส่งเสริมการขายมา 4 จ่าย 3 จงเขียนโปรแกรมรับจำนวนคนที่ใช้บริการแล้วคำนวณหาจำนวนเงินที่ต้องจ่าย เมื่อร้านคิดราคาหัวละ 100 บาท
ความน่าสนใจของปัญหานี้คือมี pattern ของการโปรแกรมที่พบได้บ่อยคือ
map/reduce (split-apply-combine) ที่ประกอบด้วย
- filter
- map
- sum (reduce)

Scratch
ได้ทดลองแก้ปัญหาแบบ imperative ด้วย scratch ไว้ ที่นี่

Python
ซึ่งแปลงเป็น python แบบตรงตัวได้ประมาณนี้
def calculate(n):
count = 1
paid = 0
answer = int(n)
while True: # python doesn't have repeat until
if count > answer: # emulate with while and if
break
if count % 4 == 0:
print(f'{count} ฟรี', end='')
else:
print(f'{count} จ่าย', end='')
paid += 100
print(', ', end = '')
count += 1
print()
print(f'รวมต้องจ่าย {paid}')
while True:
calculate(input('จำนวนคน ?\n> '))$ python 4for3.py
จำนวนคน ?
> 6
1 จ่าย, 2 จ่าย, 3 จ่าย, 4 ฟรี, 5 จ่าย, 6 จ่าย,
รวมต้องจ่าย 500หรือถ้าอยากทำใ้ห้สั้นแบบ code golf ก็ได้ประมาณนี้
while True:
print(sum([100 for x in range(int(input('จำนวนคน ?> ')) + 1) if x % 4 != 0]))$ python 4for3.py
จำนวนคน ?> 6
500ซึ่งการเขียนด้วย Python แบบทั่วไปจะมองไม่เห็น map, filter, fold ที่ซ่อนอยู่ได้ชัดเจน
หากลองเขียนด้วย Python ตาม pipeline ก็จะได้ประมาณนี้
while True:
heads = range(int(input('จำนวนคน ?> ')) +1)
fheads = filter(lambda x: x % 4 != 0,heads)
pheads = map(lambda _: 100, fheads)
ans = sum(pheads)
print(f'รวมจ่าย {ans}')ลองเอาตัวแปรชั่วคราว (intermediate) ที่ไม่จำเป็นต้องใช้ออก เพื่อทดลองทำ function composition จะพบว่าอ่านยากขึ้นกว่าเดิมเพราะวงเล็บซ้อนกันหลายชั้น (subjective)่่
while True:
print(sum(
map(lambda _: 100,
filter(lambda x: x % 4 != 0,
range(int(input('จำนวนคน ?> ')) +1)))))นอกจากนี้แล้ว การเขียนด้วย map, filter และ reduce ออกจะไม่เป็น pythonic เมื่อเทียบกับการใช้ list comprehension (ตีความตามที่ผู้ออกแบบภาษา Python ได้เขียนไว้)
Haskell
การแสดงการ compose ที่ชัดเจนจากได้จากการเขียนด้วยภาษา functional เช่น Haskell
main :: IO ()
main = do
putStr "จำนวนคน?\n> "
n <- readLn
print $ compute [1..n]
main
compute :: [Int] -> Int
compute = sum . map (const 100) . filter ((/= 0) . flip mod 4)$ runhaskell 4for3.hs
จำนวนคน?
> 6
500argument ของ map คือ function
const :: Int -> a -> Int ที่ไม่สนใจสมาชิกใน List
เดิมว่าคืออะไร เปลี่ยนให้เป็น 100 :: Int
argument ของ filter ดูจะอ่านยากหน่อย เพราะลดรูปไปอยู่ในแบบ point free
λx -> x `mod` 4 /= 0 ~~ λx -> (/= 0) $ mod x 4
λx -> (/= 0) $ flip mod 4 x
(/= 0) . flip mod 4หากแยก filter predicate กับ mapping function ออกมาและเขียน type กำกับเพื่อให้อ่านง่ายขึ้น
compute :: [Int] -> Int
compute = sum . map m . filter p
where
p :: Int -> Bool
p x = x `mod` 4 /= 0
m :: Int -> Int
m _ = 100ตอนหน้า เขียนเรื่องของโครงสร้าง recursive ของ List และ fold เพื่อสร้าง high order function ของ List (map, filter, sum)