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 #4: ทำ Project อะไรดี

เวลาจะเรียนโปรแกรมมิ่ง คำแนะนำเราคือทำ project เยอะๆ แล้วมันจะเก่งขึ้นเร็วมากกว่านั่งทำโจทย์ไปเรื่อยๆ

น้องเค้าบอกว่า ไม่รู้จะทำอะไรดี คิดจะทำอะไรก็มีคนทำไปหมดแล้ว…

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

แต่จุดสำคัญคือ มันทำให้เราใส่ความคิดของเราเข้าไปด้วยได้

ตอนม. 5 เคยไปแข่งเขียนเว็บไซต์ บังเอิญโจทย์รอบนั้นให้เขียนระบบแชต เลยทำโจทย์สบายมาก เพราะนับๆ ดูตอนนั้นน่าจะเขียนแชตมาประมาณ 6 รอบแล้ว แล้วแชตเราต่างกับคนอื่นอย่างไร?

  • แชตตัวแรกๆ น่าจะเขียนเป็นส่วนหนึ่งของ project เกมออนไลน์ ฟีเจอร์ไม่มีอะไรคือพิมพ์คุยกันได้เฉยๆ
  • ตัวถัดมาถ้าจำไม่ผิดจะลองทำ Encrypted Chat โดยเอา AES มาเข้ารหัส end to end บน server จะไม่สามารถอ่านข้อความได้
  • แชตตัวที่สามเอามาจากที่มีคนปล่อยไว้ในเว็บ Zone-IT แต่เราเอามา modify เพิ่มฟีเจอร์หนักมากๆ มี Bot แบบ IRC
  • แชตตัวที่สี่อยู่ในเกมออนไลน์เหมือนกัน แต่มันเป็นส่วนหนึ่งของ Realtime ของเกมซึ่งทำให้เห็น player อื่นเดินในเกมได้ด้วย, แชตแยกตามแมพได้ และ GM สามารถใช้ command ประกาศ global message ได้
  • แชตตัวที่ห้าใช้ในวิทยุออนไลน์ของโรงเรียน ตัวนี้เขียนเร็วๆ ตั้งชื่อพิมพ์ข้อความได้แค่นั้น ถ้าจำไม่ผิดตัวนี้เป็นครั้งแรกที่เขียนระบบแสดงชื่อคนออนไลน์เอง
  • แชตตัวที่หกเกิดจากมีคนป่วนแชตตัวข้างบน เลยเขียนใหม่ให้มีระบบ Login ที่แน่นหนาขึ้น รวมถึงสามารถ Login โดยใช้ username/password ฐานข้อมูลนักเรียนได้ด้วยถ้าไม่มี Social login

คือขนาดแชตซึ่ง scope จริงๆ มันเล็กมากแต่เราสามารถ innovate ให้เข้ากับการใช้งานได้หลายรูปแบบ


นอกจาก Chat แล้ว ในสมัยที่เราหัดเขียนเว็บนั้นก็จะมี script PHP แจกฟรีอยู่หลายประเภท เช่น Guestbook, Login, Webboard, Blog/Diary ตอนนั้นก็คิดว่าทุกประเภทที่เรามีเหตุต้องใช้ก็น่าจะเคยเขียนเองหมดแล้ว แต่ขาดอยู่ประเภทเดียวคือ Web board เพราะเป็นระบบใหญ่มาก

คำถามเราตอนนั้นเหมือนกับคำถามที่น้องถามเรา มันค้างอยู่เป็นปีว่าถ้าเราเขียนยังไงมันก็ไม่ดีเท่า Script ที่มีแจกฟรีอยู่แล้ว แล้วเราจะเขียนทำไม?

ก็ใช้เวลาอยู่เป็นปีเหมือนกันกว่าจะค้นพบคำตอบ และคำตอบนั้นคือเราจะสร้าง Webboard แนวคิดใหม่ในเว็บ menome

ไอเดียตอนนั้นได้แรงบันดาลใจจากกระทู้รีพอร์ทที่จะต้อง Refresh ติดตามตลอดเวลา (เหมือน Live blogging สมัยนี้) รวมถึงเว็บ Siamzone ที่มีกระทู้ประเภทห้องแชต เราก็เลยคิดว่าถ้าอย่างนั้นเราน่าจะเอาข้อดีทั้งระบบ Chat และ Web board ไปรวมกัน นั่นคือ

  • เป็นระบบ Web board ที่ update Realtime เหมือนแชต
  • เป็นระบบ Chat ที่ Persistent เหมือน Web board พิมพ์แล้วไม่หายไป ย้อนอ่านได้ตลอด

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

นอกไปจากนี้ยังมีระบบคล้ายๆ Tag ของกระทู้ด้วย (เหมือนเว็บบอร์ดของ GOG ไม่แน่ใจว่าเคยเห็นของ GOG มาก่อนคิดหรือเปล่า) วิธีการใช้งานคือทุก object บนเว็บ (ที่อนุญาต) จะมีห้องของตัวเอง สามารถตั้งกระทู้ได้ และกระทู้สามารถอยู่หลายห้องพร้อมกันได้

ถามว่าเว็บบอร์ดแบบนี้ดีกว่า Simple Machines ที่นิยมในสมัยนั้นหรือเปล่า? บอกเลยว่าไม่ ระบบบริหารจัดการมันแทบไม่มีและลำบากมาก เพราะทีมเราไม่พอที่จะบริหารห้องนับพันห้องได้ (ถ้าใครเคยทำ Web board ก็น่าจะรู้ว่าเปิดห้องทีละมากๆ ก็จะเจอปัญหาว่าห้องร้าง) การใช้งานเองนอกจากเราแล้วก็คิดว่าไม่มีใครเข้าใจระบบใหม่ที่เดียวในโลกแบบนี้

สุดท้าย section เว็บบอร์ดก็ร้างไป เพราะเราไม่มีเวลาไปแก้ต่อให้มันใช้งานได้ง่ายขึ้น เอาไปทำส่วนอื่นแทน

ที่เล่านี่ก็คือไม่ว่ามันเวิร์คหรือไม่เวิร์คเราก็ยังภูมิใจกับไอเดียนี้นะ ที่ไม่ใช่แค่คิดแต่ได้ทำ Proof of Concept จริงๆ ด้วย