Functional Programming #1 : ก้าวแรก

เกริ่นนำ

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

บทความชุด Functional Programming ตั้งใจเอาไว้ว่าจะมีทั้งหมด 5 บทความ โดยที่นับบทความนี้เป็นตอนแรก โปรดติดตามครับ

 

ปุจฉา Functional Programming คืออะไร

วิสัชนา มันคือ paradigm การเขียนโปรแกรมแบบหนึ่ง เออ.. ว่าแต่ paradigm นี่คืออะไร หลายท่านอาจจะงง คำว่า paradigm แปลเป็นไทยได้ว่า “กระบวนทัศน์” เข้าใจมากขึ้นไหมครับ ถ้าไม่เข้าใจ ก็ลืมมันไปนะครับ ถือว่าไม่เคยได้ยินคำแปลนี้ก็แล้วกัน เอาอย่างนี้ครับ อธิบายโดยยกเป็นตัวอย่างจะเข้าใจชัดเจนครับ ภาษา C ใช้ paradigm ที่ชื่อว่า Imperative โดยที่มองการเขียนโปรแกรมเป็นคำสั่งเรียงต่อกัน มีเงื่อนไขมีการวนรอบ ส่วนภาษา C# นั้นใช้ paradigm ที่ชื่อว่า Object-Oriented ซึ่งมองการเขียนโปรแกรมแยกออกเป็นหน่วยอิสระที่เรียกว่าวัตถุ จากนั้นสานสัมพันธ์กันระหว่างวัตถุด้วยกัน แต่ส่วนย่อยในการทำงานจริงๆ ของ C# ก็ยังคงอาศัย Imperative เป็นตัวขับเคลื่อนอยู่ดี ถ้ามอง Imperative เป็นสายบู๊ อยากได้อะไรต้องลุยทำเองทั้งหมด ซึ่งตรงข้ามกับสายบุ๊นที่เรียกว่า Declarative ซึ่งเป็นแนวทางที่ไม่บอกวิธีทำ บอกเพียงความต้องการและให้คอมพิวเตอร์ตัดสินใจหาวิธีทำเอง ถ้านึกไม่ออกให้นึกถึง SQL ครับ ภาษานี้แค่บอกว่าอยากได้รายชื่อคนอายุต่ำกว่า 20 ไปหามาให้หน่อย ส่วนจะหามาได้อย่างไร ผู้เขียนโปรแกรมไม่ต้องเข้าไปยุ่ง สุดท้ายได้ข้อมูลมาก็แล้วกัน FP เองก็เป็นแนวคิดที่โน้มเอียงไปทางสายบุ๊นครับ

FP กำลังได้รับความนิยมอย่างสูงในปัจจุบัน ภาษาชั้นนำหลายๆ ภาษาก็นำเอา FP ไปเป็นส่วนเสริมในรุ่นใหม่ ๆ ของตน ขอเน้นนะครับว่าเป็นส่วนเสริม มิใช่ไปทดแทนของเดิม เพราะถ้าจะเปลี่ยนแนวคิดหลักของภาษา มันก็ไม่ใช่ภาษาเดิมแล้วครับ สร้างเป็นภาษาใหม่จะดีกว่า และมีภาษาจำนวนไม่น้อยที่ใช้ FP เป็นหลัก ที่เกิดใหม่ ๆ อายุไม่เกิน 10 ปี ก็อย่างเช่น Clojure หรือ F# เป็นต้น

FP : ที่มาที่ไป

มาทำความเข้าใจกันก่อนนะครับ แม้ว่า FP จะได้รับความนิยมสูง ใคร ๆ ก็พูดถึงกัน แต่นั่นก็ไม่ได้หมายความว่า FP เป็นของใหม่นะครับ เก่าครับรุ่นลายครามกันเลยทีเดียว เพื่อความเข้าใจ ต้องขอย้อนเวลากลับไปสักเล็กน้อย ไปยังต้นกำเนิดเครื่องคอมพิวเตอร์ เครื่องคอมพิวเตอร์ที่เราใช้กันอยู่ในยุคปัจจุบันสืบเชื้อสายมาจาก Stored-program ซึ่งเป็นแนวคิดในการออกแบบเครื่องคอมพิวเตอร์ที่คิดค้นโดย John von Neumann เมื่อปี ค.ศ. 1945 ซึ่งเป็นปีที่สิ้นสุดสงครามโลกครั้งที่ 2 จริงๆ แล้ว Stored-program คงเกิดก่อนหน้านั้น เพราะมีการใช้งานในการคำนวณค่าต่างๆ ในสงคราม เมื่อสิ้นสงครามจึงนำเอามาเผยแพร่ Stored-program นี้แนวคิดหลักๆ ผมว่านะเหมือนกับการทำอาหาร มีขั้นตอนทำงานเรียงเป็นลำดับ มีเงื่อนไข มีการวนต้มไปไปเรื่อยๆ จนกระทั่งสุก ซึ่งนั่นก็คือ Imperative นั่นเองครับ เมื่อ CPU ที่เราใช้งานนั้นลึกๆ แล้ว ทำงานเป็นแบบ Imperative จึงไม่น่าแปลกใจเลยว่าภาษาคอมพิวเตอร์ที่สร้างขึ้นในยุคแรกๆ ส่วนใหญ่รองรับการทำงานแบบ Imperative

ดังนั้นสรุปได้ว่าเครื่องคอมพิวเตอร์สมัยใหม่เครื่องแรกเกิดยุคปี 40 แต่แนวคิด FP เกิดขึ้นก่อนหน้านั้นสักหนึ่งทศวรรษนั่นคือ ยุคปี 30 อ้าว! เป็นไปได้ยังไงที่แนวคิดในการเขียนโปรแกรมเกิดขึ้นก่อนเครื่องคอมพิวเตอร์ เป็นไปได้ครับ ถ้าแนวคิดนั้นไม่ได้ออกแบบมาเพื่อใช้งานกับเครื่องคอมพิวเตอร์ในขั้นแรก แต่หากมีผู้นำไปประยุกต์ใช้กับเครื่องคอมพิวเตอร์ในเวลาต่อมา

แนวคิดที่ว่าเป็นแนวคิดทางคณิตศาสตร์ครับ มีชื่อว่า Lambda Calculus คิดค้นโดยนักคณิตศาสตร์ชาวอเมริกันชื่อว่า Alonzo Church แนวคิดนี้ใช้เพื่อแก้โจทย์คณิตศาสตร์ที่ใช้ตัวเลขมาก ๆ หรือซับซ้อน ทำได้โดยการกำหนดกฎกติกาในการยุบการขยายของนิพจน์อย่างเป็นลำดับขั้นตอนและเป็นระบบ ทำให้ไม่หลงจึงสามารถแก้โจทย์ที่ซับซ้อนได้ ซึ่งเมื่อประยุกต์ใช้กับคอมพิวเตอร์และกลายเป็น FP ในที่สุด

กลิ่นตุๆ คำว่า Functional คือฟังก์ชันทางคณิตศาสตร์ใช่ไหม

ถูกต้องครับ ตามนั้นเลยครับ มันก็คือฟังก์ชันในเชิงการเขียนโปรแกรมธรรมดานี้เอง ท่านอาจสงสัย (อย่างหนัก) ฟังก์ชันทุกคนก็รู้จัก ทุกคนก็ใช้ก็สร้างฟังก์ชันอยู่แล้ว ไม่เห็นจะมีอะไรเป็นพิเศษเลย ยิ่งภาษาคอมในยุคปัจจุบัน เราปรับปรุงให้กลายเป็น method รวมหลายๆ ตัวเข้าด้วยกันเพื่อบรรจุเข้าไปใน class มันจะไม่ย้อนยุคกันเกินไปหรือที่มาเน้นการเขียนโปรแกรมโดยใช้ฟังก์ชัน อย่างนั้น Object-Oriented มิถอยหลังกลายเป็น Impertive หรืออย่างไร

ใจเย็นๆ ครับ ลำพังฟังก์ชันที่ภาษาคอมพิวเตอร์ทั่วไปนั้น มันไม่เพียงพอครับ ต้องเพิ่มความสามารถบางอย่างเข้าไป จับเข้าตู้โทรศัพท์แล้วแปลงร่าง ออกมาแล้วบินได้เลย เป็นฟังก์ชันชนิดพิเศษ จะมีความสามารถเพิ่มขึ้นครับ แต่บางท่านก็ยังคาใจอยู่ว่า ฟังก์ชันมันก็คือฟังก์ชันอยู่วันยังค่ำ ไม่ว่าเพิ่มประสิทธิภาพอย่างไรมันก็เป็นเพียงแค่ฟังก์ชันอยู่ดี มองมุมนี้ก็ไม่ผิดครับ แต่….

ขุมพลัง Functional Programming อยู่ที่ Declarative

Declarative เป็นร่มของ FP อีกที แนวคิด Declarative นั้นเราบอกเฉพาะสิ่งที่เราต้องการ แต่ไม่บอกขั้นตอนในการทำงานเลย ดังนั้นมันจึงไม่พลาดและอาจทำงานได้เร็วกว่า แม้มันอาจไม่เร็วกว่าในตอนนี้ แต่อนาคตก็ไม่แน่ พูดกันตรงนี้ ผมขอแชร์ประสบการณ์หน่อย

ในยุคที่ผมเขียนโปรแกรมพวก Clipper หรือ FoxPro ซึ่งเป็นภาษาสำหรับงานฐานข้อมูลโดยเฉพาะ เมื่อก่อนผมต้องเขียนโปรแกรมวิ่งเข้าไปที่ตาราง เพื่อหาข้อมูลเอง มีวันหนึ่ง ถ้าผมจำไม่ผิด FoxPro สามารถเขียนโปรแกรมโดยใช้คำสั่ง SQL ได้ ซึ่งผมว่าผมเขียนเองเร็วกว่าครับ เพื่อนๆ ไม่ค่อยมีคนยอมใช้กันตอนนั้น ต่อมา FoxPro ก็พัฒนาอัลกอริทึมในการค้นหาคำสั่ง SQL ที่มีประสิทธิภาพเรียกว่า Rushmore Technology ซึ่งทำงานได้เร็วมาก จนในที่สุดก็เร็วกว่าเขียนเชื่อมฐานข้อมูลเอง เมื่อ Microsoft ซื้อ Fox Software ไป ก็เอา Rushmore นี่เองไปพัฒนาต่อจนกลายเป็น Jet Engine ที่ใช้ใน Microsoft Access และทุกวันนี้ SQL กลายเป็นภาษาหลักในการเข้าถึงฐานข้อมูล เรื่องเขียนเชื่อมฐานข้อมูลเองก็คงไม่ทำกันแล้วนะครับ

สิ่งที่ผมอยากจะบอกก็คือ FP ก็เป็น Declarative เหมือนกับ SQL แน่นอนสิ่งที่เราได้ก็คือการเขียนโปรแกรมเสร็จอย่างรวดเร็ว โอกาสผิดพลาดน้อย และที่สำคัญโปรแกรมรู้เรื่องเลยว่ามันทำอะไร ไม่ต้องไล่การโปรแกรมทำความเข้าใจ แต่เรื่องประสิทธิภาพ อันนี้ก็แล้วแต่ ว่ากันเป็นกรณีๆ ไป

ถ้าพูดกันถึงความเร็วแล้ว ถ้ามีของอยู่หนึ่งร้อยชิ้นต้องทำ แน่นอนถ้าเป็น Imperative จะต้องกำหนดการวนรอบ ทำทีละชิ้นๆ แต่ใน FP จะพูดเหมาเลยว่าทำทั้งร้อยชิ้นนี้ บอกมาว่าจะให้ทำอะไร ดังนั้นภายในมันจะไปทำอย่างไร กระจายไปหลายแกนประมวลผล แม้กระทั่งช่วยกันข้ามเครื่องก็สุดแล้วแต่ สุดท้ายขอได้ผลลัพธ์ที่ถูกต้องก็พอ Google เองก็ใช้แนวคิดของ FP ในการค้นหาข้อมูลมหาศาลของตัวเองเรียกว่า MapReduce ซึ่งมีคนเอาไปทำเป็นของฟรีเรียกว่า Hadoop ซึ่งเป็นการทำงานแบบข้ามเครื่อง คงจะพอมองเห็นภาพพอสังเขปแล้วนะครับ ถึงประโยชน์ของ FP

ขอจบลงที่ประวัติศาสตร์ของ Functional Programming

อย่างที่กล่าวไว้แล้วข้างต้นว่า Alonzo Church คิดค้น Lambda Calculus มาก่อนคอมพิวเตอร์เกิด แล้วมันกลายเป็นภาษาคอมพิวเตอร์ในตอนไหนแน่ อย่างแรกเลยเป็นอันรู้กันว่าภาษาคอมพิวเตอร์ภาษาแรกคือภาษาเครื่อง คือป้อนคำสั่งกันเป็นเลขฐานสอง ฐานแปด และฐานสิบหก จากนั้นก็พัฒนามาเป็นภาษา Assembly และในปี 1957 ก็มีการคิดค้นภาษาแบบ Imperative ที่ชื่อว่า FORTRAN ขึ้นมา นับว่าเป็นภาษาระดับสูงภาษาแรกของโลก จากนั้นสิ่งที่อัดอั้นกันมานาน เพราะแนวคิด Lambda Calculus เกิดมารอก่อนแล้ว ภาษาระดับสูงตัวที่สองจึงกำเนิดขึ้นมาด้วยแนวคิดนั้น หลังจากภาษา FORTRAN เพียงปีเดียวคือ 1958 เกิดภาษา LISP นับได้ว่าเป็นภาษาที่ใช้แนวคิด FP เป็นภาษาแรกของโลก (แต่ยังไม่เป็น FP นัก) และตามด้วย Imperative อีกหนึ่งภาษาชื่อว่า COBOL เป็นภาษาที่สาม ตามมาติด ๆ ในปี 1959

หลังจากเกิด LISP ซัก 4-5 ปี ก็มีภาษาที่เป็น FP ค่อนข้างแท้เกิดขึ้นสองตัวไล่เลี่ยกัน นั่นคือ ML และ Scheme ซึ่งภาษา FP ในยุคใหม่ ๆ ล้วนมีรากฐานมาจากภาษาทั้งสามตัวนี้ โดยส่วนตัวแล้วชอบทางแนว ML มากกว่า เพราะนำเอาโครงสร้างทาง Imperative ที่เราคุ้นเคยกันเป็นอย่างดีอยู่แล้วมาใช้งาน ส่วน Scheme นั้นยึดหลักเหมือนกับ LISP คือเน้นไปทางใช้วงเล็บ วงเล็บซ้อนวงเล็บหลาย ๆ ชั้นอ่านแล้วดูวุ่นวาย ว่ากันไปแล้วภาษา FP ที่ได้รับความนิยม ที่มาทางสาย ML ก็เช่น Haskell, Ocaml และ F# ส่วนแนว Scheme ก็เช่น Clojure เป็นต้น

คราวนี้มาถึงข้อโซ่ที่ขาดหายไปที่เชื่อม Lambda Calculus กับ FP เข้าด้วยกัน อยู่ดี ๆ ทำไมถึงเปลี่ยนชื่อให้กลายเป็น FP ใครเป็นคนเปลี่ยน ถ้าไล่ตามประวัติศาสตร์แล้ว บิดาผู้ตั้งชื่อ FP ก็คือ John Backus แห่ง IBM ครับ ใครเรียนเรื่องการสร้างคอมไพเลอร์ต้องรู้จักเพราะเป็นผู้ออกแบบ BNF ที่ใช้ในการนิยามไวยากรณ์ของภาษานั่นเอง เมื่อปี 1978 Backus เขียนบทความอันลือลั่นที่ชื่อว่า “Can Programming Be Liberated from the von Neumann Style? A Functional Style and It’s Algebra of Programs” คำว่า von Neumann Style ในที่นี้คือ Imperative นั่นเอง บทความนี้เป็นที่กำเนิดของคำว่า Functional Programming นั่นเอง แต่ภาษาทาง FP เกิดขึ้นก่อนหน้านั้นแล้ว อย่างที่กล่าวไว้ในย่อหน้าที่ผ่านมา ดังนั้น Backus จึงได้ชื่อว่าเป็นบิดาของคำว่า “Functional Programming” ครับ

Backus พูดถึงโครงสร้างการออกแบบภาษาที่ต่างกันของ Paradigm สองตัว แต่ไม่ว่าจะใช้ Paradigm ใด ถ้าเทียบกันแล้ว ไม่อาจทำงานได้เร็วว่า Imperative ครับ เนื่องจากตัว CPU นั้น ภาษาเครื่องก็ยังใช้แนวคิดของ von Neumann หรือ Imperative นั่นเอง สุดท้ายแล้วทุก Paradigm ก็ต้องแปลงกลับมาเป็น Imperative อยู่ดี ถามว่า แล้วเป็นไปได้ไหม ที่จะสร้าง CPU ที่ไม่อิงตาม Imperative แต่ใช้แนวคิด FP จะได้เมื่อใช้กับ FP และจะทำงานได้อย่างรวดเร็วขึ้น ก็บอกได้เลยว่าเป็นไปได้ และเป็นไปนานแล้วครับ เพียงแค่ยังไม่มีใครทำออกมาวางขาย เพราะคงขายไม่ออก คนเสพติด von Neumann ไปแล้วครับ เอาไว้ถ้ามีเวลา ผมจะลองสร้างของจริง มาให้ดูเป็นของเล่นซักตัวหนึ่ง โดยการออกแบบใน FPGA

แม้ว่ามีภาษาหลายภาษาที่ใช้ paradigm FP แต่ถ้าถามว่าฮิตไหม ก็บอกเลยว่าไม่ ตามประวัติศาสตร์แล้วในยุค ’80 เป็นยุคทองของ Imperative จากนั้น Object-Oriented เริ่มเข้ามาทดแทนในช่วงปลายยุค ‘90 จนกระทั่ง ยุคทศวรรษ 2000 ถนนทุกสายมุ่งสู่ Object-Oriented ทีผ่านมา FP เป็นภาษาของชนกลุ่มน้อย ใช้งานกันจริงจังในวงการการศึกษาแคบๆ ไม่ค่อยมีคนรู้จักมากนัก ที่ก็ใช่ว่าจะไม่มีใครใช้งานนะครับ FP ก็ถูกนำไปฝังตัวเพื่อเพิ่มความสามารถของโปรแกรมอย่างเช่น ภาษา Wolfram เพื่อเชื่อมต่อกับ Mathematica ซึ่งเป็นโปรแกรมคำนวณทางคณิตศาสตร์ ภาษา AutoLISP ใช้ในการเขียนโปรแกรมเชื่อมต่อกับ AutoCAD ซึ่งเป็นโปรแกรมสำหรับการวาดทางวิศวกรรมเป็นต้น

จุดเปลี่ยนที่สำคัญเกิดขึ้นกลางยุคปี 90 ของชาวอาทิตย์อุทัยท่านหนึ่งชื่อว่า ยูกิฮิโร่ มัตซูโมโต้ หรือที่เรียกว่า แม๊ทซ์ นำเอา Imperative, Object-Oriented และ FP มาฟีเจอริ่งกัน ผนวกกับความเป็น Interpreter ใช้งานเลยได้ไม่ต้องคอมไพล์เป็น executable file ก่อน ซึ่งการทำงานแบบ Interpreter นั้นมีความยืดหยุ่นของการออกแบบภาษาสูงกว่าแบบคอมไพล์มาก นำมาปั่นรวมข้อดีที่กล่าวมาแล้วทั้งหมดเข้าด้วยกัน จึงกลายเป็นอัญมณีเม็ดงามนามว่า Ruby ภาษาสวยงาม สั้นกะทัดรัด และยืดหยุ่นสูง แต่ถ้าคุยกันจริงๆ แล้ว Ruby เองก็น่าเอาอย่างมาจากอีกภาษาหนึ่งที่ปั่นทุกอย่างข้างต้นมาก่อน เป็นภาษาที่คิดคนโดยชาวดัทซ์ที่ชื่อว่า กิลโดแวนรอสซัม ภาษานั้นก็คืองูหลาม Python นั่นเอง คิดค้นขึ้นเมื่อต้นยุค ‘90 ท่านอาจสงสัยว่าทำไมผมจึงเอา Ruby ขึ้นก่อน Python ทั้งๆ ที่ควรให้เครดิต Python ในการปั่นรวมเสียมากกว่า จริงๆ แล้วไม่มีอะไรครับ ผมพิจารณาเฉพาะในส่วนของ FP ซึ่ง Ruby ทำได้เหนือกว่า Python มาก แต่ส่วนอื่นที่เหลือส่วนมาก Python มักทำได้ดีกว่า Ruby ถ้าถามผมปัจจุบันผมใช้งานภาษาอะไรระหว่างสองภาษานี้ ผมตอบได้ชัดเจนครับ Python เท่านั้นครับ

ในยุคแรกๆ ไม่ว่า Python หรือ Ruby ก็ได้รับความนิยมค่อนข้างจำกัด ปัญหาเนื่องมาจากความเร็วครับ Interpreter ทำงานช้ากว่าคอมไพล์มากอยู่ แต่ในยุคปัจจุบัน เมื่อความเร็วของเครื่องคอมพิวเตอร์พัฒนาสูงขึ้น ทำให้ช่องห่างเรื่องนี้แคบมาก ๆ จนแทบไม่รู้สึกแล้ว Embedded Computer ขนาดเล็กมาก ๆ อย่าง Raspberry Pi ราคาพันกว่ามา ก็ใช้งานภาษา Python และ Ruby ได้โดยไม่รู้สึกว่าช้า ทำให้ภาษาทั้งสองโด่งดังมากในยุคสิบปีที่ผ่านมา เมื่อภาษาทั้งสองไปที่ใด FP ก็ตามไปที่นั้นด้วย เป็นการปลุกกระแส FP ในวงกว้างขึ้นครับ

ภาษาคอมพิวเตอร์ที่เป็นกระแสหลักของโลก C++, C#, VB.NET, Java และ JavaScript นั้น ในยุคทศวรรษปี 2000 ก็แข่งขันกันเสริมสร้างความสามารถด้าน Object-Oriented กันทำไปทำมามันตันครับ ไม่รู้จะเอาอะไรมาใส่เพิ่มความสามารถอีก แต่การออกรุ่นใหม่ๆ เป็นระยะ เป็นสิ่งที่จำเป็นครับ มิฉะนั้นจะสู้คู่แข่งไม่ได้ ก็เลยต้องฟีเจอริ่งกับ FP กันในทุกภาษาที่เพิ่งกล่าวมา ตอนนี้ถนนทุกสายมุ่งสู่ FP ไม่น่าเชื่อว่า 4-5 ปีที่ผ่านมานี้ FP กลายเป็นกระแสหลักไปแล้ว ถ้า Alonzo Church สามารถรับรู้ได้ ก็คงดีใจไม่น้อย

ทิ้งท้าย

ผมได้นำเสนอ FP เพื่อฝากฝังให้เป็นเพื่อนที่ดีของท่านต่อไปในอนาคต ถ้าท่านรับ FP เป็นเพื่อนก็กรุณาอ่านบทความทั้ง 5 บทความจนจบ เมื่อจบแล้วแนะนำให้ท่านอ่านบทความ  JavaScript #03: ในมิติ Functional Programming ซึ่งเป็นบทความขยายและต่อยอดบทความชุดนี้อีกที่หนึ่ง

[Total: 29    Average: 3.7/5]

You may also like...

6 Responses

  1. Kai says:

    ขอบพระคุณท่านอาจารย์ครับ

  2. dixonsatit says:

    ขอบคุณครับ บทความดีมากๆ จะรอติดตามนะครับ

  3. saimon says:

    พึ่งตามมาเจอนะครับ บทความเก่า C# ช่วยชีวิตไว้ได้เยอะเลย ขอบคุณมากๆ ครับ

  4. CHamp says:

    เป็นบทความที่ดีครับ ขอบคุณมากๆ

  5. ภูริเดช says:

    ขอบคุณครับเป็นประโยนช์มาก

  6. FoxDev says:

    สาย FoxPro ตามมาอ่านด้วยคนหนึ่งครับ

Leave a Reply

Your email address will not be published. Required fields are marked *