Wall of Text #5: Internet Scale List Comprehension

ในทีมผมมีคนแนะนำ Codewars มา ผมก็เลยเอาไปปล่อยต่อให้น้องเค้าเล่น ซึ่งมันสนุกมาก

เวลาทำโจทย์ใน Codewars ผ่าน มันจะให้ดูว่าคนอื่นแก้โจทย์นี้อย่างไร ซึ่งถ้าเป็น Python เรามักจะเห็นเฉลยระดับบรรทัดเดียวออก เช่น

def find_it(seq):
    return [i for i in seq if seq.count(i) % 2 != 0]

โค้ดนี้เรียกว่า List Comprehension ซึ่งสำหรับมือใหม่อาจจะดูซับซ้อน ผมเองก็เขียน Python มาประมาณ 3-4 ปีก่อนที่จะเริ่มใช้เป็น

List Comprehension ประกอบด้วยคอนเซปต์จาก Functional Programming 2 ตัว คือ Map และ Filter

Map

Map คือการเปลี่ยนทุกสมาชิกของ List ให้เป็นค่าใหม่ โดยเรียกฟังค์ชั่นที่กำหนดไว้กับสมาชิกทุกตัว แล้วเอาคำตอบที่ได้แทนค่าเดิม ดังนั้นผลลัพท์ของ Map จะมีจำนวนสมาชิกเท่ากับค่าตั้งต้นเสมอ

ยกตัวอย่างเช่น ถ้ากำหนดฟังค์ชั่นคือ ยกกำลังสอง แล้ว Map function นี้ลงบน [1, 2, 3, 4] ผลลัพท์ที่ได้คือ [1, 4, 9, 16]

Filter

Filter แปลตรงตัวก็แปลว่ากรอง โดยจะเอาฟังค์ชั่นที่คืนค่าเป็น Boolean รันบนสมาชิกทุกตัวของ List เพื่อถามว่ายังต้องการเก็บสมาชิกตัวนี้ไว้ไหม ผลลัพท์ของ Filter จะมีจำนวนสมาชิกไม่เกินจำนวนตั้งต้นเสมอ และข้อมูลใน List จะไม่ถูกแก้ไข เอาสมาชิกออกได้อย่างเดียว

ยกตัวอย่างเช่น ถ้ากำหนดฟังค์ชั่นคือ ทดสอบว่าเป็นเลขคู่ แล้ว Map ลงบน [1, 2, 3, 4, 5] ผลลัพท์ที่เหลืออยู่คือ [2, 4]

ในหลายๆ ภาษา กระบวนการ Map กับ Filter นี้จะแยกกันเป็นสองคำสั่ง เช่นในภาษา JavaScript [1,2,3].map((x) => x*2).filter((x) => x<5) แต่ใน Python เราสามารถรวบเป็น List Comprehension ครั้งเดียวได้เพื่อความสะดวก

Reduce

โดยปกติแล้วฟังค์ชั่นตระกูลนี้นอกจาก Map – Filter แล้วยังมักจะมี Reduce ด้วย (ซึ่ง List Comprehension ทำไม่ได้)

Reduce คือการนำสมาชิก 2 ตัวเข้ามารวมกันเหลือค่าเดียว เช่น ถ้าหากต้องการหาผลรวมของ [1, 2, 5] เราสามารถใช้ reduce function คือ x+y แล้วระบบจะรันฟังค์ชั่นนี้จนครบทุกสมาชิกให้เอง คือ

  • x=1, y = 2; 1+2 = 3
  • x=3, y = 5; 3+5 = 8
  • คำตอบสุดท้ายคือ 8

คำสั่ง Reduce ใน Python 2 คือ reduce() และ Python 3 ถูกย้ายไปไว้ใน functools.reduce() ด้วยเหตุผลว่าเราสามารถใช้ For loop แทนกันได้และอ่านเข้าใจง่ายกว่า

MapReduce

กระบวนการ Map – Reduce – Filter นี้ทรงพลังมาก ขนาด Google ที่มีข้อมูลมหาศาลก็ยังใช้กระบวนการนี้ประมวลผลข้อมูลบางอย่างอยู่

สาเหตุที่มันทรงพลังมาก ก็เพราะมันทำให้ปัญหานี้สามารถกระจายงานไปหลายๆ เครื่องพร้อมกันได้

ยกตัวอย่างเช่น ถ้าหากเราต้องการทำโปรแกรมนับคะแนนจากภาพบัตรเลือกตั้ง วิธีก็คือ

  1. Map – แปลงภาพบัตรเลือกตั้งเป็นพรรคที่เลือก
  2. Filter – กรองบัตรเสียออก
  3. Reduce – รวมคะแนนเลือกตั้งจาก Map

วิธีนี้จะสังเกตว่าในขั้นตอน Map-Filter ไม่มีใครต้องรอใคร ทำให้เรากระจายงานได้ง่ายมาก

ทั้งนี้ MapReduce ที่ใช้ในงาน BigData จะแตกต่างกับ Map-Filter-Reduce อยู่เล็กน้อยเพราะ Map จะมีหน้าที่ของ Filter ไปด้วย โดยในแต่ละ Input Map อาจจะคืนค่ามากกว่า 1 ก็ได้ หรือไม่คืนค่าเลยก็ได้ (แปลว่า filter ทิ้ง) มันจึงจะหน้าตาประมาณนี้

def map_ballot(ballot_img):
    result = ocr(ballot_img)
    if result is not None:
        emit({result: 1})

จากโค้ดด้านบน เราสามารถเรียก emit หลายๆ ครั้งได้ถ้าหากต้องการ output หลายๆ ค่า หรือถ้า result เป็น None จะไม่มีการเรียก emit ก็คือ input นี้จะถูก filter ทิ้งนั่นเอง

Wall of Text #3: Master/slave

มีเรื่องเล่าให้น้องเค้าฟังว่า

ปีที่แล้วมีกระแส Political Correctness มาบอกว่า คำว่า Master/slave ไม่เหมาะสม ไม่ควรใช้ เพราะเราเลิกทาสนานแล้ว บางคนอาจจะมองว่าเป็นคำไม่ดี

ตัวอย่างการใช้งานเช่น ในเว็บไซต์เมื่อ user ทำงานบางอย่างแล้วเราจะขอให้ slave ช่วยส่งข้อมูลไปให้บริการอื่นๆ ด้วย

กรณีนี้ Master คือตัวหน้าเว็บไซต์ สั่งงานให้ Slave ทำงาน แล้ว Slave ไม่มีสิทธิ์ขึ้นมาเป็น Master ได้เพราะ Slave ทำหน้าเว็บไซต์ไม่เป็น

กรณีนี้ Slave อาจจะเรียกว่า Worker ก็ได้

อีกการใช้งานหนึ่งมักจะเห็นในระบบฐานข้อมูล ถ้าเราต้องการให้ฐานข้อมูลใช้งานได้ตลอดเวลา เราจะตั้งระบบสำรอง

ระบบหลักนี้ก็จะเรียกว่า Master และ Slave คือสำรอง จะแตกต่างกับข้างบนคือในกรณีที่ Master ล้ม Slave จะกลายเป็น Master ก็ได้

กรณีนี้บางคนอาจจะเรียกว่า Primary กับ Replica ซึ่งคำว่า Replica แปลว่าต้องเป็นโคลนของ Primary เท่านั้น

ดังนั้นเราจะเห็นข้อดีของการใช้คำว่า Master/Slave คือ มันเข้าใจง่าย ทาสทำงานให้เจ้านาย เห็นภาพชัดเจนไม่ต้องตีความซับซ้อน และมันใช้ได้หลายบริบทมากกว่าคำที่เฉพาะเจาะจงลงไป

เมื่อปีที่แล้วกลุ่มคนที่มองว่าคำนี้ไม่เหมาะสมนั้นก็ได้ส่ง Pull request ไปหลายโครงการ open source ที่มีชื่อเสียง เพื่อให้ยกเลิกใช้คำเหล่านี้

ปัญหาก็คือมันไม่ได้ง่ายแบบนั้น

เพราะถ้าคำเหล่านี้อยู่ในเอกสาร ก็ต้องอัพเดตเอกสาร

อยู่ในโค้ด ก็ต้องแก้ในโค้ด

แก้โค้ดก็ต้องเทสว่ามันจะไม่มีอะไรพัง

แล้วพอเป็น Library แก้แค่โค้ดอย่างเดียวไม่พอ คนที่ใช้ Library ก็ต้องแก้ตามไปด้วย

ทั้งหมดนี้เพียงเพราะว่าคนบางกลุ่มมองว่าคำเหล่านี้ไม่เหมาะสม

ที่สำคัญ แล้วคำใหม่จะเป็นอะไรล่ะที่ยังกินความมากเหมือนเดิม?

คำว่า Leader กับ follower ดูเหมือนจะใช่ แต่ก็ยังไม่ถูก เพราะผู้ตามที่ฉลาดอาจจะแซงกลายเป็นผู้นำได้ จะไปใช้แทนความหมาย Worker ก็ไม่ได้เท่าไรนัก

แล้วก็ยังมีกลุ่ม BDSM (ซาดิสม์, มาโซคิสม์) ที่มีการเล่นโรลเพลย์ทาส ก็บอกว่าถ้าคุณบอกว่าคำนี้คือเหยียดทาส คุณกำลังเหยียดเรานะ เอาเป็นว่าเราเปลี่ยนเป็น Dominant (รุก) กับ Submissive (รับ) ไหมล่ะ…

เอากับเค้าสิ… แต่ผมว่าเรียก dom กับ sub ก็ดีนะ

สุดท้ายแล้วผมจำไม่ได้ว่าได้ข้อสรุปอะไรออกมา แต่กลายเป็นว่ามีคำอีกคู่หนึ่งที่ถูกเพ่งเล็งขึ้นมา คำนั้นคือ Whitelist และ Blacklist

เพราะ Whitelist แปลว่าดี แล้วทำไมต้อง White กับ Black จะแปลว่าคนขาวดี คนดำไม่ดีหรือเปล่า…

หลังๆ มานี้ผมจะเห็น Google กับ Microsoft ก็เลิกใช้คำนี้ไปแล้วเปลี่ยนเป็นคำว่า Allowlist กับ Denylist ความหมายเหมือนกัน แต่อาจจะไม่คุ้นหูเท่านั้น

ที่ยังไม่รู้คือแล้ว Greylist ล่ะจะเป็นคำว่าอะไรดี?