ตัวเลขและตัวดำเนินการ (Numbers and Operators)
มีแค่ไม่กี่คนที่ตอบถูก!
ถ้าคุณเคยไถเฟสบุ๊ก คุณอาจจะเคยเห็นโพสต์แบบนี้มาบ้าง:
ตอบเท่าไหร่ ??
10 + 10 × 0 + 10 = ?(a) 10 (b) 20 (c) 110 (d) 200
มีคนเพียง 8% เท่านั้นที่ตอบถูก!
แล้วในคอมเม้นต์ก็มีแต่คนมาเถียงกันไม่จบไม่สิ้นว่าตกลงคำตอบที่ถูกต้องคือเท่าไหร่
ซึ่งถ้าจะถามว่าตกลงคำตอบที่ถูกต้องมันคือเท่าไหร่ เราก็อาจจะยังฟันธงไม่ได้ซะทีเดียว เพราะคำถามนี้มันกำกวม (ambiguous) มันไม่ชัดเจนพอ มันไม่ได้ระบุเลยด้วยซ้ำ ว่าโจทย์นี้ควรจะตีความยังไง
ถ้าเราเปลี่ยนโจทย์เป็น… ถ้ากดปุ่มตามนี้ใส่ในเครื่องคิดเลขแล้วจะได้คำตอบเท่าไหร่ ดูชัดเจนขึ้นไหมครับ
ผมว่ามันก็ชัดเจนขึ้นนะ แต่ว่าก็ยังกำกวมอยู่ดี เพราะเครื่องคิดเลขมีหลายยี่ห้อ ถ้าใช้เครื่องคิดเลขบน macOS ก็จะได้คำตอบเป็น 20 แต่ถ้าใช้เครื่องคิดเลขบน Windows แบบเบสิค ก็จะได้คำตอบเป็น 10
หรือถ้าเราเปลี่ยนโจทย์เป็น ให้คำนวณคำตอบโดยอิงลำดับการดำเนินการตามกฏ PEMDAS ได้คำตอบเท่าไหร่ อันนี้ค่อนข้างชัดเจนครับ
กฏ PEMDAS คือกฏการคำนวณที่เราเรียนกันในโรงเรียน ซึ่งระบุไว้ว่าให้ทำการคูณก่อนบวก กรณีนี้ก็จะได้คำตอบเป็น 20
ลองเปลี่ยนโจทย์ดูอีกสักรอบ ให้แปลงโจทย์เป็นภาษา JavaScript แล้วเอาไปรันใน Console ได้คำตอบเท่าไหร่ อันนี้ค่อนข้างชัดเจนเช่นกัน ถ้าทำตามก็จะได้คำตอบเป็น 20 ครับ
แต่ทำไมถึงได้ 20 ล่ะ? นี่คือสิ่งที่เราจะมาหาคำตอบกันในหัวข้อนี้ครับ
เอกซ์เพรสชัน (นิพจน์) และตัวดำเนินการ
ก่อนที่เราจะไปกันต่อ ผมอยากจะแนะนำให้รู้จักคำศัพท์ต่างๆ ที่สำคัญก่อนครับ
Expression (แปลเป็นภาษาไทยว่า “นิพจน์” คำว่า “พจน์” มีรากศัพท์มาจากคำว่า ‘วจน’ ที่แปลว่า ‘คำพูด’ หรือ ‘ถ้อยคำ’ ครับ)
คำว่า Expression ในเชิงการเขียนโปรแกรม ความหมายของมันคือ “ชิ้นส่วนของโค้ดที่เราเขียน ที่สามารถนำมาหาค่า หรือ Value ได้”
“An expression is a valid unit of code that resolves to a value”
— MDN. “Expressions and Operators.” JavaScript Guide.อันนี้คือตัวอย่าง Expression อย่างง่ายครับ คือมีแค่ตัวเลขเดียว “ค่าของ
123
คือเท่าไหร่” คำตอบคือ 123 ครับเราสามารถสร้าง Expression ที่ซับซ้อนขึ้นได้ โดยการเอา Expression ที่ขนาดเล็กกว่ามาประกอบกัน โดยใช้สิ่งที่เรียกว่า Operator (หรือ ตัวดำเนินการ) เป็นตัวเชื่อมครับ “ค่าของ
123 * 456
คือเท่าไหร่” คำตอบคือ 56088 ครับ“complex expressions are joined by operators”
— MDN. “Expressions and Operators.” JavaScript Guide.ภาษา JavaScript มี Operator ให้เราเลือกใช้เยอะมาก แต่ในคอร์สนี้เราจะได้เรียนรู้แค่บางตัว
ลำดับการทำงานของตัวดำเนินการ (Operator precedence and associativity)
กลับมาที่โจทย์เดิิมกัน:
เรามีข้อมูลอยู่ 4 ชิ้น (แทนด้วย a, b, c, d ในรูปข้างล่าง)
เราต้องการนำข้อมูลพวกนี้มาดำเนินการอะไรซักอย่าง (แทนด้วยวงกลมสามวงในรูปข้างล่าง)
แต่เราควรจะทำอันไหนก่อน?
จะเห็นว่าในตัวอย่างนี้ เราสามารถจัดรูปแบบ (ใส่วงเล็บ) ให้มันได้ถึง 5 วิธีที่แตกต่างกัน ซึ่งแต่ละวิธีก็มีโอกาสที่จะได้คำตอบที่แตกต่างกันออกไป
อย่างเช่น
10 + 10 * 10 + 10
ถ้าเรายังไม่รู้เรื่องลำดับการทำงานของตัวดำเนินการ เราก็อาจจะใส่วงเล็บได้ 5 วิธีดังนี้ครับ:ตัวอย่างการใส่วงเล็บ คำตอบ ((10 + 10) * 0) + 10
10 10 + (10 * (0 + 10))
110 (10 + (10 * 0)) + 10
20 10 + ((10 * 0) + 10)
20 (10 + 10) * (0 + 10)
200 แล้วการใส่วงเล็บแบบไหนล่ะ ที่ถูกต้อง?
แหล่งข้อมูลสำคัญ ที่จะช่วยให้เราหาคำตอบนี้ได้ก็คือ JavaScript Reference หรือ “เอกสารอ้างอิงภาษา JavaScript” ครับ
ในเอกสารนี้จะมีบทนึงที่พูดถึงสิ่งที่เรียกว่า Operator precedence หรือ “ลำดับการทำงานของตัวดำเนินการ” ครับ เลื่อนลงมาเราจะเจอกับตารางที่บอกลำดับการทำงานของ Operator แต่ละตัวครับ
และในตารางนี้เอง ที่เราจะได้เห็นว่า การคูณ (
*
) มีลำดับความสำคัญที่สูงกว่าการบวก (+
) ครับนั่นหมายความว่า ถ้าเรามีตัวดำเนินการคุณ กับตัวดำเนินการบวก เราจะจับกลุ่มให้ตัวดำเนินการคูณก่อนครับ
js// โจทย์ 10 + 10 * 0 + 10 // ⬇ // ทำการคูณก่อนการบวก 10 + (10 * 0) + 10
คราวนี้ เราเหลือของ 3 อย่าง มาบวกกันแล้ว แต่ทีนี้ เนื่องจากทั้ง Operator ด้านซ้ายเป็นการบวก และ Operator ด้านขวาก็เป็นการบวกด้วย แปลว่าทั้งสองตัวมีลำดับความสำคัญเท่ากัน
แต่ในตารางเดียวกันนี้ ก็ได้ระบุไว้ด้วยว่า เครื่องหมาย
+
จะมีสิ่งที่เรียกว่า associativity จากซ้ายไปขวานั่นแปลว่า ให้เราจับกลุ่มเครื่องหมาย + จากซ้ายไปขวาครับ
js// บวกค่าด้านซ้ายก่อน แล้วค่อยไปบวกค่าด้านขวา (10 + (10 * 0)) + 10 // ⬇ (10 + 0) + 10 // ⬇ 10 + 10 // ⬇ 20
แค่นี้มันก็ไม่กำกวมแล้ว และเราสามารถคำนวณคำตอบได้เป็น 20 ครับ