ช่วงที่ผ่านมาหลายๆ คนอาจจะเคยได้ยินคำว่า Functional Programming ซึ่งเป็นเทรนด์การเขียนโปรแกรมที่มาแรงในขณะนี้ (ปี2016) แล้วสงสัยมั้ยว่ามันต่างจากการเขียนโปรแกรมที่เราๆ เคยเขียนกันมาอย่างไรกันนะ
ภาษา C, Java, PHP อะไรพวกนี้ไม่ใช่ Functional เหรอ มันสร้าง Function ได้นะ แล้วถ้าไม่ใช่มันเรียกว่าอะไร?
Programming paradigm
หากเปรียบโลกโปรแกรมมิ่งเป็นยุทธภพ ภาษาโปรแกรมต่างๆ ก็น่าจะเทียบได้กับสำนักวิชา เราๆ ที่เป็นโปรแกรมเมอร์ก็จะเริ่มจากระดับเด็กฝึกหัด เลือกเข้าสำนักใดสำนักหนึ่งก่อนเป็นอันดับแรก ซึ่งโดยสถิติแล้วสำนักยอดฮิตพิมพ์นิยมก็คือสำนักใหญ่ที่มีชื่อว่า C/C++, Java, C#, Python
ซึ่งในขั้นนี้เราจะต้องเรียนวิธีการคิดที่เรียกว่า algorithm และคำสั่งที่ยากและซับซ้อนขึ้นเรื่อยๆจนในที่สุดก็สำเร็จวิชา (ภาษา) ของสำนักนั้นๆ
หลังจากออกจากสำนักแล้วก็จะมีบางคนที่อยากศึกษาเพิ่มเติม ก็จะทำการเลือกเข้าสำนักที่ 2 ที่3 ที่4 ..ต่อๆ ไป
และเนื่องจากเป็นสำนัก (ภาษา) คนละสำนักกัน แนวคิดจึงไม่เหมือนกันซะทีเดียว สำหรับคนที่เพิ่งเขียนเป็นแค่ภาษาเดียวตอนเริ่มศึกษาภาษาที่ 2 จะพบกับสิ่งที่เรียกว่า culture shock เหมือนตอนที่เดินทางไปอยู่ต่างประเทศแล้วปรับตัวไม่ได้ ทำไมคนประเทศนี้ทำตัวแบบนี้ เราเข้ากับเขาไม่ได้อะไรอย่างนั้น
แล้วระดับของ culture shock เนี่ยก็ขึ้นอยู่กับว่า ภาษาแรกที่คุณเขียนเป็นแล้วกับภาษาที่สองที่คุณกำลังจะเริ่มเรียนมันมีความต่างทาง paradigm กันแค่ไหน
paradigm แปลว่า กรอบ แบบอย่าง แนวทางปฏิบัติ เช่นประเทศฝั่งตะวันออกมีparadigmที่ต้องเคารพผู้อาวุโสในขณะที่ประเทศฝั่งตะวันตกไม่ถือเรื่องนี้มากนัก
Programming paradigm หรือที่ภาษาไทยใช้คำว่า กระบวนทัศน์การเขียนโปรแกรม (ต้องแปล ไทย-เป็น-ไทย ต่อมั้ย ฮา) เป็นรูปแบบการเขียนโปรแกรม ซึ่งแต่ละภาษาจะออกแบบมาว่าภาษาโปรแกรมนี้เขียนแบบไหนดี เช่น
- ภาษา C เน้นให้เขียนแบบบอกมันว่าจะทำอะไรบ้างไล่ลงมาเป็นลำดับๆ
- ภาษา Java เน้นให้สร้างคลาสสไตล์OOP
จะเห็นว่าแต่ละภาษาก็มีแนวคิดในการเขียนเป็นของตัวเอง อาจจะมีซ้ำกันหรือยึด paradigm หลักตัวเดียวกัน หรือใช้แนวคิดต่างกันอย่างสิ้นเชิงเลยก็ได้
งั้นเรามาดูกันดีกว่าว่า paradigm หลักๆ ที่เขานิยมใช้เอามาออกแบบภาษาโปรแกรมน่ะมันมีอะไรบ้าง
Imperative Programming
การเขียนโปรแกรมแบบแรกเชื่อว่าทุกคนที่เรียนเขียนโปรแกรมมาจะนึกภาพออก เพราะมันคือการเขียนโปรแกรมที่ง่ายที่สุดโดยการเขียนสิ่งที่จะให้คอมพิวเตอร์ทำในลักษณะ "คำสั่ง หรือ Statement" เรียงกันไปเรื่อยๆ ซึ่งเวลาโปรแกรมรัน มันก็จะโปรเซสตามลำดับคำสั่งพวกนั้นทำให้โปรแกรมเปลี่ยน state ไปเรื่อยๆ ไงล่ะ
ซึ่งเจ้า imperative นี่ก็อาจจะเรียกว่าเป็นการเขียนโปรแกรมแบบ Procedural หรือ Structure ได้เช่นกัน
โดย Structure จะมองว่าการวาง Statement ต่อๆ กันน่ะบางครั้งก็มีการเลี้ยวไปเลี้ยวมา กระโดดข้ามคำสั่งโน้นนี้ได้เป็นโครงสร้างการไหลหรือ Flow แบบต่างๆ
ส่วน Procedural คือ Structure แบบเดิมนั้นแหละ แต่มีการเอา โค้ดคำสั่งที่ใช้บ่อยๆ แพ็ครวมกันเรียกว่า Procedural หรือ routines (ที่คนส่วนใหญ่จะเข้าใจมันในชื่อของ programming function) เพิ่มเข้ามาด้วย
ลักษณะเด่นคือโค้ดโปรแกรมจะเรียงกันไปเรื่อยๆ ด้วยคำสั่งสั้นๆ เช่นตัวอย่างข้างล่าง การทำงานตรงไปตรงมามากๆ เป็นการเขียนโปรแกรมเชิง How? คือต้องบอกโปรแกรมทุกสเต็ปว่าเราอยากให้มันคิดอะไรให้เรา
ตัวอย่างสไลต์การเขียน โจทย์คือ: มีจุดอยู่ 2 จุด คือ P1 กับ P2 อยากรู้ว่าระยะห่างระหว่างจุดทั้งสองมีค่าเท่าไหร่
double p1_x = 125.32; double p1_y = 1253.2; double p2_x = 88.1; double p2_y = 9547.120; double diff = sqrt( pow(p1_x - p2_x, 2) + pow(p1_y - p2_y, 2) ); print(diff);
ข้อดี - เข้าใจง่าย เหมาะกับมือใหม่ เพราะแทบจะแปลงจาก Flowchart มาเป็นโค้ดบรรทัดต่อบรรทัดได้เลย
ข้อเสีย - ไม่เหมาะกับการเขียนโปรแกรมในงานใหญ่ๆ หรือมีความซับซ้อน / โค้ดเข้าใจยากถ้าไม่ได้เป็นคนเขียนเองหรือกลับมาอ่านมันไม่เมื่อเวลาผ่านไปแล้ว 1 เดือน ทำให้มันแก้ไขยากมาก
ตัวอย่างภาษา: C, Go, Fortran, Pascal, BASIC
Object-Oriented Programming
อ่านบทความเกี่ยวกับ OOP โดยเฉพาะได้ที่
บทความชุด: Object Oriented Programming (All About OOP)
รวมบทความเจาะลึกเกี่ยวกับการเขียนโปรแกรมเชิงวัตถุ ตั้งแต่แนวคิด วิธีการใช้งาน ตัวอย่างและหลักการใช้ต่างๆ เช่น S.O.L.I.D หรือการใช้ Design Pattern
ถ้าเทียบกับ Imperative ที่กองโค้ดและตัวแปรทุกอย่างไว้ในที่ๆ เดียวแล้วค่อยๆ บอกว่าจะให้เอาตัวแปรตัวโน้นตัวนี้มาทำอะไรกัน Object-Oriented (OO) จะเน้นสร้างสิ่งที่มนุษย์เรียกในชีวิตจริงขึ้นมาแทน
เช่นถ้าเราต้องการบวกเลข มุมมองของ OO คือเราต้องมี object ของเลข 2 ตัว / มีวิธีการ (+) / และมีตัวที่เอา object ทั้งหมดมาโปรเซสให้ได้คำตอบออกมา
ตัวอย่างสไลต์การเขียน โจทย์เหมือนเดิมคือ: มีจุดอยู่ 2 จุด คือ P1 กับ P2 อยากรู้ว่าระยะห่างระหว่างจุดทั้งสองมีค่าเท่าไหร่
Point p1 = new Point(125.32, 1253.2); Point p2 = new Point(88.1, 9547.120); double diff = p1.diff(p2);
ให้ลองสังเกตดูว่าในตอนที่เราเขียน Imperative เราจะสร้างตัวแปร p1_x / p1_y ขึ้นมาทั้งๆ ที่โจทย์บอกว่าเราจะสร้างจุด 2 จุด แต่ในโค้ดไม่มีส่วนไหนเลยที่อ่านแล้วให้ความรู้สึกว่ามีจุดเกิดขึ้นมา (มีแต่ตัวแปรที่แทนค่า X,Y ของจุด) แต่ถ้าเป็นการเขียนแบบ OO เราจะเน้นสร้างสิ่งที่มนุษย์พูด นั่นคือ "จุด (Point)" จริงๆ
ในแง่ของการทำโปรเจคใหญ่ๆ การเขียนแบบ OO นั้นนิยมมากที่สุด เพราะโค้ดจะถูกแยกออกเป็นส่วนๆ อย่างชัดเจน
ข้อดี - เป็นการจำลองสิ่งที่มนุษย์พูดให้เป็นโค้ด ทำให้อ่านโค้ดและแก้ไขง่ายขึ้นแม้ไม่ใช่โค้ดที่ตัวเองเป็นคนเขียนก็ตาม (ในเคสที่คนเขียนคนแรกต้องเขียนมาพอใช้ได้ด้วยนะ ฮา) มีคอนเซ็ปในการเอา class ที่เคยเขียนไปแล้วกลับมา reuse ซ้ำได้หลายวิธี (implements-extends)
ข้อเสีย - เข้าใจยากสำหรับมือใหม่ รายละเอียดและกรอบการเขียนเยอะกว่าจะได้โปรแกรมมาสักโปรแกรมหนึ่ง
ตัวอย่างภาษา: Common Lisp, Python, C++, Objective-C, Smalltalk, Delphi, Java, Swift, C#, Perl, Ruby, PHP
Functional Programming
เคยเขียนบทความเรื่อง Functional Programming ไปแล้วครั้งนึง สามารถอ่านแบบละเอียดได้ที่: http://www.tamemo.com/post/80/functional-programming-should-be-your/
เมื่อพูดถึงฟังก์ชัน น่าจะมีสัญลักษณ์หนึ่งที่ควรคิดถึง นั่นคือ...
f(x) ... ใช่แล้ว ชื่อของมันก็มาจากฟังก์ชันในวิชาคณิตศาสตร์นั่นเอง
ในการเขียนโปรแกรมแบบนี้เราจะมองว่าเรามีกล่องปริศนาอยู่ หน้าที่ของเราคือรู้แค่เจ้ากล่องนี้ต้องการค่าอะไร แล้วมันจะให้อะไรกลับคืนมาเท่านั้นพอ
เช่นมี "กล่องบวก" (Plus Function) ที่รอรับตัวเลข 2 ตัวแล้วจะบวกค่าทั้งสองกลับมาเป็นคำตอบ ... เราก็ไม่จำเป็นต้องรู้ว่าการบวกน่ะมันทำยังไง รู้แค่ว่าเดี๋ยวฟังก์ชันจะจัดการให้เราพอเลย
อ่านมาถึงตรงนี้อาจจะบอกว่าอย่างนี้ Functional เนี่ยมันก็คือการจับ Imperative มายัดใส่กล่องแค่นั้นน่ะสิ? อา... ก็ทั้งใช่และไม่ใช่ล่ะนะ ลองขยับมาดูตัวอย่างที่ซับซ้อนขึ้นนิดหน่อยกันดีกว่า
โจทย์คือ: มี Array อยู่หนึ่งตัว เป็น Array ของ int ... ซึ่งเราต้องการหาว่าผลรวมของค่าทุกค่าใน Array มายกกำลัง 2 มีค่าเท่าไหร่?
ถ้าเป็นการเขียนแบบ Imperative โค้ดก็จะออกมาหน้าตาประมาณนี้
int x[10] = {1,2,3,4,5,6,7,8,9,10}; int ans = 0, i; for(i=0; i<10; i++) { ans = ans + x[i] * x[i]; } print(ans);
ก็จับวนลูปซะ ไม่เห็นจะยาก ... งั้นลองมาดูโค้ดในมุมมองของภาษาแนว Functional กับบ้างดีกว่า
int x[10] = {1,2,3,4,5,6,7,8,9,10}; ans = x.toList().map(x -> x * x).reduce(x, y -> x + y);
หรือ
$x = [1,2,3,4,5,6,7,8,9,10]; $ans = array_reduce(array_map(function($x){return $x * $x;} $x), function($x, $y){ return $x+$y});
อธิบายโค้ดกันนิดหน่อยสำหรับคนไม่เคยเขียนโค้ดแบบนี้
- map: เป็นฟังก์ชันสำหรับวนลูปกับ list หรือ array โดยเราสามารถบอกได้ว่าอย่างให้สมาชิกทุกตัวใน list ทำอะไร (ในเคสนี้คืออยากให้ทุกตัวยกกำลังสอง คือ x * x)
- reduce: เป็นฟังก์ชันที่เอาไว้รวมค่าหลายๆ ค่า เช่น list/array ให้กลายเป็นค่าๆ เดียวด้วยวิธีการอะไรสักอย่าง (ในเคสนี้บอกให้เอาทุกตัวมารวมกันด้วยวิธีการ +)
จะสังเกตว่าสำหรับ Functional แล้วการวนลูปนั้นก็ยังมี function มารองรับ ซึ่งทำให้เราสามารถตัดสิ่งที่ไม่เกี่ยวกับโค้ดของเราที่ต้องเขียนใน Imperative ทิ้งไปได้ (ในที่นี้หมายถึง i ที่เอาไว้นับลูป ซึ่งไม่เกี่ยวกับสิ่งที่เราต้องการทำเลย แต่ไม่มีก็ไม่ได้เพราะลูปจะขาดตัวนับ)
แล้วทำไมมันถึงมันถึงเพิ่งจะมาฮิตกันปีนี้ล่ะ ขอเล่าก่อนว่าเจ้า functional programming เนี่ยเป็นคอนเซ็ปที่มีมาตั้งแต่ยุคปี 1950 โน่นแล้ว เพราะพื้ฐานของการเขียนโปรแกรมและคอมพิวเตอร์ก็มีที่มาจากคณิตศาสตร์นั่นแหละ แต่เนื่องจากการเขียนโปรแกรมแบบนี้ในยุคนั้น ยุคที่คอมพิวเตอร์ยังไม่แรงพอที่จะโปรเซสอะไรซับๆ ซ้อนๆ กันระดับนี้ได้มันเลยยังไม่บูมเท่าที่ควร (การเขียนแบบ Imperative ใช้พลังงานการโปรเซสน้อยกว่าจึงเป็นที่นิยมมากกว่า)
แต่ในปัจจุบันทุกวันนี้ คอมพิวเตอร์ส่วนใหญ่มีพลังแรงพอที่จะคิดอะไรซับซ้อนขึ้นแล้ว functional programming จึงกลับมาด้วยเหตุผลหลักว่ารูปแบบการเขียนของมันตอบโจทย์การเขียนโค้ดในยุคที่เป็น...
Parallel Programming
ในการเขียนโค้ดแบบ Imperative เราจะเขียนโดยการบอกให้คอมพิวเตอร์ทำงานเป็นลำดับ เช่นการวนลูปในตัวอย่างเมื่อกี้
คำถามคือ...ถ้าเรามีคอมมากกว่า 1 เครื่อง เราสามารถประยุกต์โค้ชุดนี้ให้มันกระจายงานให้คอมหลายๆ เครื่องช่วยกันคิดได้ไหม?
คำตอบคือ ได้นะ! แต่ทำยากมาก เนื่องจากเราเขียนลูปซึ่งมีตัวแปร i เป็นตัวนับ การจะกระจายงานให้หลายๆ เครื่องจะต้องเปลี่ยนวิธีคิดและนับตัว i เสียใหม่
แต่ถ้าเขียนแบบ functional สิ่งที่เราสั่งงานคอมพิวเตอร์จะอยู่ในรปของ เอาทุกตัวในลิสต์ไปยกกำลังสอง (x -> x * x) หรือ เอาทั้งหมดมาบวกกัน (x, y -> x + y) พอมุมมองการสั่งต่างกัน เราสั่งงานเป็น "วิธีการทำงาน" งั้นก็แค่กระจายวิธีการทำงานนี้ไปให้คอมทุกเครื่องในระบบให้พวกมันช่วยกัน รันโค้ดตามที่สั่งไปซึ่งสามารถทำได้ง่ายกว่ามากเลย เพราะไม่มีโค้ดส่วนไหนที่เราคุมลำดับการทำงานของโปรแกรม (หมายถึงตัวแปร i นะ)
ข้อดี - สามารถมองการทำงานทุกอย่างเป็นฟังก์ชันที่เฉพาะเจาะจงได้ ลดการเขียนโค้ดในส่วนที่ไม่จำเป็นได้เยอะ / Parallel Programming
ข้อเสีย - คนส่วนใหญ่ที่เริ่มเรียนเขียนโปรแกรมมักจะเริ่มด้วย Imperative programming ทำให้ลักษณะการคิดจะออกมาเป็นขั้นตอนๆ ทำให้ไม่ค่อยเก็ทคอนเซ็ปของ functional ในช่วงแรกๆ / อย่างน้อยความจะเข้าใจหรือมีพื้นฐานเรื่อง higher-order function สักนิดถึงจะเรียกใช้ฟังก์ชั่นต่างๆ มาผสมๆ มิกซ์ๆ กันให้ผลการคำนวนที่ต้องการออกมาได้อย่างไม่ผิดเพี้ยน
ตัวอย่างภาษา: Common Lisp, Scheme, Clojure, Wolfram Language (Mathematica), Racket, Erlang, OCaml, Haskell, F#
Declarative programming
หาก paradigm ที่ผ่านๆ มายังไม่ทำให้คุณงงได้งั้นลองมาดูภาษาที่ใช้รูปแบบสุดท้ายนี่เป็นแนวคิดในการเขียนกันบ้าง
รูปแบบการเขียนในหัวข้อที่ผ่านมา ถึงแม้จะมีความต่างกันแต่ประเด็นคือ paradigm ทั้งหมดนั้นเป็นการสั่งให้โปรแกรมทำงานแบบตามลำดับ หรือ How to do นั่นเอง แต่สำหรับ Declearative นั่นจะสั่งงานโดยการบอกคอมพิวเตอร์ว่า
What I want?
หมายความว่าเราไม่จำเป็นต้องบอกขั้นตอนให้โปรแกรมว่าแกต้องคิดแบบนี้นะ เอาตัวแปรมา + - * / กัน วนลูป เช็ก if-else แบบนี้ๆๆๆ ที่ต้องทำก็แค่บอกโปรแกรมไปว่า "ฉันอยากได้อะไร" ก็พอ
ฟังดูง่ายเนอะ ไม่ต้องคิด algorithm ให้วุ่นวายอยากได้อะไรก็บอกไปตรงๆ งั้นมาดูตัวอย่างกันดีกว่า ยกตัวอย่างด้วยโจทย์เหมือนเดิมละกัน เข้าใจง่ายดี
โจทย์คือ: อยากได้ชื่อของพนักงานทั้งหมดที่อายุการทำงานเกิน 5 ปี และอยู่ในเขตคลองเตย
Employee emp_list[100] = {..., ..., ...} String name_list[100] = {...} for(emp_list as emp) { if( emp.exp > 5 AND emp.address = 'คลองเตย' ) { name_list.push(emp.name); } }
ก็ต้องเขียนเป็นลูปและในแต่ละรอบก็เช็ก if ไปด้วยละเนอะ
มาดูการเขียนในแบบ Declarative กันบ้าง (ตัวอย่างภาษา SQL)
select name from Employee where exp > 5 and address = 'คลองเตย'
จะเห็นเลยว่าการเขียนแบบ Declarative น่ะไม่มี algorithm อะไรผสมอยู่ในโค้ดเลยสักนิดเดียว เราบอกว่าให้มันไปหาตามเงื่อนไขที่ให้ไป ส่วนจะไปหายังไงก็แล้วแต่คุณ
อีกตัวอย่างของภาษาประเภทนี้ละกัน อันนี้เป็นตัวอย่างภาษา Prolog ซึ่งเป็นภาษาเชิง A.I. ที่โปรแกรมจะเรียนรู้จากสิ่งที่เราสอนไปแล้วคิดออกมาตามข้อมูลที่มี
mother_child("แม่", "ลูกชาย"). father_child("พ่อ", "ลูกชาย"). father_child("พ่อ", "ลูกสาว"). father_child("ปู่", "พ่อ"). sibling(X, Y) :- parent_child(Z, X), parent_child(Z, Y). parent_child(X, Y) :- father_child(X, Y). parent_child(X, Y) :- mother_child(X, Y). -------------- ?-sibling("ลูกชาย", "ลูกสาว") ? --> "คำตอบที่ได้คือ Yes!"
จากโค้ดนี้ ในบรรทัดแรกๆ จะเป็นการสอนโปรแกรมถึงความสัมพันธ์ระหว่าง พ่อ แม่ ลูก ปู่ ผ่านคำสั่ง mother_child และ father_child จากนั้นจึงสอนโปรแกรมต่อว่า ถ้ามี X กับ Y แล้วทั้งสองคนจะเป็น sibling (พี่-น้อง) กันก็ต่อเมื่อมาจาก parent เดียวกัน
ในบรรทัดสุดท้ายเราก็ถามโปรแกรมว่า ลูกชาย-ลูกสาว เป็นพี่น้องกันหรือเปล่า ก็จะได้คำตอบว่า Yes ออกมา
Regular Expression
ภาษาโปรแกรมประเภท Declarative นั้นยังมีอีกตัวหนึ่งที่ใช่กันค่อนข้างบ่อย นั่นคือ RegExp
โจทย์คือ: ต้องการให้ผู้ใช้กรอกอีเมล แต่ปัญหาคือไม่รู้ว่าส่งที่เขากรอกเข้ามานั้นถูกฟอร์แมทอีเมลรึเปล่า?
ถ้าเขียนโปรแกรมมาเช็กแบบปกติเราอาจจะต้องมานั่งเช็กไปทีละตัวว่าเป็นตัวอักษรภาษาอังกฤษตามด้วย @ แล้วลงท้าย..บลาๆๆ รึเปล่า แค่คิดก็เหนื่อยแล้ว แต่ถ้าเราเอา RegExp มาใช้ก็จะทำให้ชีวิตง่ายขึ้น
email = 'contact-me@TAmemo.com'; /^\w+@[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/.match(email);
อย่าเพิ่งคิดว่านี่มันภาษาต่างดาวอะไรกัน (โค้ดโปรแกรมปกติก็ดูเหมือนภาษาเอเลี่ยนอยู่แล้ว เจอ RegExp เข้าไปนี่หนักกว่าเดิมอีก)
หลักการเขียน RegExp คือบอกว่า format ที่เราต้องการมีเคสแบบไหนบ้างในบรรทัดเดียวโดยไม่ต้องเขียน algorithm ว่าวิธีการเช็กต้องทำยังไงเลย
อธิบาย
- ^ แปลว่า ขึ้นประโยคด้วย...
- \w หมายถึงตัวอักษร ส่วน + แปลว่าเอากี่ตัวก็ได้ (แต่ต้องมากกว่า 1 )
- @ อันนี้ตรงไปตรงมา คือตามด้วย @
- [a-zA-Z_] แปลว่าขอตัวอักษรในช่วงของ a-z หรือ A-Z และ _ แล้ว {2,3} ตอนท้ายระบุว่าขอความยาวแค่ 2-3 ตัวอักษรเท่านั้น
- $ แปลว่าจบประโยคตรงนี้
ข้อดี - บอกความหมายกับโปรแกรมได้ตรงๆ ว่าเราอยากได้อะไรโดยไม่ต้องสนว่ามันมีวิธีการทำงาน/คิดเพื่อให้ได้คำตอบออกมาแบบไหน
ข้อเสีย - ใช้ได้กับงานเฉพาะทางเท่านั้น ไม่ใช่ว่าภาษาในกลุ่มนี้จะสั่งให้ทำอะไรก็ทำให้เราได้ หนึ่งภาษามักจะสร้างขึ้นมาให้เหมาะกับงานรูปแบบเดียว
ตัวอย่างภาษา: Prolog, SQL, XQuery, regular expressions,
หากจะแยก paradigm ของทุกภาษาที่มีในโลก คงแยกออกมาได้หลายสิบแบบ แต่ paradigm 4 แบบที่พูดถึงไปแล้วคือประเภทหลักๆ ที่เจอในแทบจะทุกภาษายอดฮิตของโลก
Paradigm อื่นๆ ...
Run-time
บางทีเราอาจจะแบ่งภาษาโปรแกรมตามประเภทของการรันโปรเซสก็ได้ เช่น
- Native: เป็นการเขียน source-code แล้วใช้โปรแกรมพวกคอมไพเลอร์แปลงเป็นภาษาของเครื่องตรงๆ เลย ทำให้เวลาทำงานส่วนใหญ่จะเร็วกว่าพวกกลุ่มอื่นๆ เช่น C/C++
- Script: เป็นภาษาที่เวลารันผลจะต้องแปลความทีละบรรทัด แล้วค่อยๆ ทำงานไปเรื่อยๆ โดยจะต้องมีโปรแกรมที่ทำหน้าที่แปลความให้ และจะต้องแปลการทำงานของโปรแกรมใหม่ทุกครั้งที่รัน เช่น JavaScript, PHP
- Virtual Machine: เป็นภาษาที่เวลารันจะต้องสร้าง VM ขึ้นมาเป็นเครื่องจำลองๆ แล้วโปรแกรมของเราจะรันบนนั้น (แล้วVMจะสั่งงานผ่านไปให้OSอีกที) จุดเด่นของภาษาพวกนี้คือเขียนครั้งเดียวแล้วเอาไปรันได้ในทุก OS แน่ๆ เช่น Java, C#
Event-Driven
เป็นการเขียนโปรแกรมโดยที่โค้ดจะไม่ทำงานทันทีที่รันโปรแกรม แต่จะทำงานเมื่อมี Event อะไรบางอย่างเกิดขึ้น เช่นถ้าต้องการให้โค้ดรันเมื่อมีการ "กดปุ่ม" ก็อาจจะได้โค้ดที่หน้าตาประมาณนี้
final Button button = (Button) ...; button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { alert("กดปุ่มแล้วล่ะนะ"); } });
การเขียนโปรแกรมแบบนี้ โค้ดที่เขียนจะไม่ทำงานจนกว่าจะเกิดที่สิ่งเรารออยู่ แล้วเจ้า Event เนี่ยก็มีได้หลายแบบตั้งแต่ click long-click (กดค้าง) touch drag hover slide ...
Symbolic vs Numeric
เคยได้ยินคำว่า ภาษาโปรแกรมมิ่งสำหรับงานด้านวิทยาศาสตร์และคณิตศาสตร์ บ้างมั้ย ... แล้วสงสัยมั้ยล่ะว่าทำไมภาษาโปรแกรมธรรมดาเอามาคิดเลขหรือสูตรทางวิทยาศาสตร์ไม่ได้หรือยังไงกัน
ก่อนอื่นต้องมาทำความเข้าใจก่อนนะ ว่าคอมพิวเตอร์มันรู้จักแต่ เลขฐาน2 ถึงแม้ว่าเวลาเราเขียนโค้ดเราจะใช้เลขฐาน10ก็ตาม สุดท้ายเวลามันรันจริงๆ มันก็กลายเป็นเลขฐาน2อยู่ดี แล้วการแปลเลขฐาน10ไปเป็นเลขฐาน2เนี่ย มันจะมีบางเคสที่แปลงออกมาแล้วได้เลขที่ไม่รู้จบ
เช่น 0.110 เลขง่ายๆ แค่นี้แหละ พอแปลงเป็นฐาน2ปุ๊บก็จะได้ 0.00011001100110011001100110011001100110011001100110011.....2 ไม่รู้จบ
แล้วเมื่อเกิดเหตุการณ์แบบนี้ขึ้น เพราะคอมพิวเตอร์มีหน่วยความจำที่จำกัด มันก็จะทำการ "ปัด" เศษทิ้งยังไงล่ะ
ทดสอบง่ายด้วยโค้ดชุดนี้
double x = 0.1 * 0.1; print(x); //answer is 0.010000000000000002
แทนที่ 0.1 * 0.1 = 0.01 มันกลับคิดค่าออกมาเพี้ยน ซึ่งเป็นผลจากการแปลเลขไปเป็นฐาน2ก่อนคิดนั่นเอง ภาษาโปรแกรมที่ทำงานแบบนี้เราจะเรียกมันว่า Numeric Programming (ภาษาโปรแกรมส่วนใหญ่เป็นแบบนี้)
แต่ในด้านวิทยาศาสตร์และคณิตศาสตร์แล้ว ข้อผิดพลาดในเรื่องนี้เป็นสิ่งที่เกิดขึ้นไม่ได้หรอกนะ ไม่งั้นคำนวนอะไรละเอียดๆ แล้วค่าเพี้ยนหมด จึงเป็นสาเหตุที่ทำให้เกิดภาษาประเภท Symbolic ขึ้นมา
แล้วการเขียน 0.1 ในภาษาพวกนี้ หรือไม่ว่าจะเป็นการเขียนเลขในรู้เศษส่วนที่หารไม่ลงตัวเช่น 22/7 นั้นจะไม่ถูกคิดให้อยู่ในรูปเลขฐาน2ของคอมพิวเตอร์ แต่จะมองในลักษณะที่มนุษย์คิดกัน
สรุป ... ภาษาโปรแกรมในโลกนี้มีหลักแนวคิดของภาษาที่ต่างกันเพราะเป้าหมายคือรูปแบบงานที่ไม่เหมือนกัน การศึกษารูปแบบการเขียนที่หลายหลากจะทำให้เราเรียนรู้ภาษาใหม่ๆ ได้เร็วขึ้นล่ะนะ
สวัสดีครับคุณต้า ผมจบมาด้ายวิศวกรรมวัดคุม ตอนนี้ทำงานไปด้วยแล้วก็เข้าคอร์สเรียนเรียนโปรแกรม JAVA เนื่องจากอยากเป็น Programmer freelance อยากจะขอคำแนะนำในการเริ่มต้นอาชีพใหม่น่ะครับ ขอบคุณครับ
ติดต่อผ่านทาง facebook.com/tamemocom/ ได้เลยครับ