หน้าเว็บ

วันศุกร์ที่ 31 กรกฎาคม พ.ศ. 2552

ความเข้าใจพื้นฐานก่อนเริ่มเขียน Ruby ตอนที่ 1

[Update: 9 Aug 2014]
รื่องที่ควรรู้และทำความเข้าใจกันก่อนที่จะเริ่มเขียนภาษา Ruby
แต่ละหัวข้อที่เขียนนั้ผมไม่ได้เรียงลำดับตามความสำคัญนะครับ แบบว่า คิดอะไรได้ก่อนก็ว่าไปก่อน ถึงแม้เนื้อหาจะค่อนข้างเบาบางออกแนว งงๆ แต่อย่างน้อยมันก็น่าจะช่วยให้เพื่อนรู้จักภาษา Ruby ได้มากขึ้นบ้างล่ะน่า
มาลองไล่ดูกันไปทีละข้อครับ

1) Ruby เป็นภาษาที่ทำงานในแบบ OOP (Object Oriented Programming)
ตามที่จั่วหัวไว้เลยครับ Ruby ถูกสร้างขึ้นด้วยความตั้งใจที่จะให้เป็นภาษาที่ทำงานแบบ OOP อย่างเต็มรูปแบบ 

ดังนั้นการทำงานของ Ruby ในทุกๆส่วน จึงเป็นการทำงานที่ต้องเกี่ยวกับอ็อบเจกต์ทั้งสิ้น โครงสร้างหรือข้อมูลพื้นฐานต่างๆ ใน Ruby ก็เป็นอ็อบเจกต์เช่นกัน ข้อมูลพื้นๆ ที่คุณอาจเจอในภาษา programming อื่นๆ อย่าง String, Integer, Floating point, Array, Boolean หรือ แม้กระทั่งค่าว่างอย่าง nil เหล่านี้เป็นอ็อบเจกต์หมด

แล้วยังไง ?
ก็สบายสิครับ เพราะหากว่าข้อมูลพื้นฐานล้วนเป็นอ็อบเจกต์กันหมด นั้นหมายความว่ามันต้องมีคลาสของข้อมูลชนิดนั้นๆ (หรือของอ็อบเจกต์นั้นๆ) ถูกกำหนดไว้ในภาษา Ruby ด้วย ซึ่งคลาสพื้นฐานเหล่านี้ติดมาพร้อมกับภาษา Ruby เรียบร้อยแล้ว ในรูปของ (Build-in class)  ทำให้เราสามารถเรียกใช้ความสามารถของอ็อบเจกต์แต่ละชนิดจากเมธอดที่มาพร้อมกับคลาสได้เลย

ตัวอย่างความสามารถพื้นๆ ของข้อมูลในแต่ละประเภท
เมื่อทดลองรันผ่าน irb จะได้ผลประมาณนี้

"This is Ruby world!".upcase  # => THIS IS RUBY WORLD!
"This is Ruby world!".size  # => 19
text = "This is Ruby world!"
text.gsub(/world/, "land")    # => This is Ruby land!
text.reverse                 # => !dlrow ybuR si sihT

13*2        # => 26
13.to_s     # => "13"
3.times { |t| print "#{t} " }
# => shows "0 1 2" and return 3

text.class  # => String
3.class     # => Fixnum
true.class  # => TrueClass
nil.class   # => NilClass


ย้ำอีกครั้งว่า ข้อมูลแต่ละตัวมันเป็นอ็อบเจกต์ในตัวของมันเองอยู่แล้ว (ข้อมูลพื้นฐานเหล่านี้ไม่จำเป็นต้องสร้างอ็อบเจกต์จากคลาสโดบใช้ new เหมือนอ็อบเจกต์ทั่วๆ ไป เพราะตัวแปรภาษา Ruby จะจัดการให้เอง แต่ขอให้มองว่ามันเป็นอ็อบเจกต์ตัวหนึ่ง) ดังนั้นเราจึงสามารถเรียกเมธอดออกมาใช้ผ่านทางอ็อบเจกต์นั้นได้เลย

Ruby มีสิ่งที่เรียกว่า module ซึ่ง ทำหน้าที่ระบุความสามารถเฉพาะในลักษณะใดลักษณะหนึ่ง โดยเราสามารถนำความสามารถที่กำหนดไว้เป็น module เข้ามา "ผสม (mix-in)" ลงไปในคลาสเพื่อให้คลาสมีความสามารถแบบเดียวกับที่ระบุไว้ใน module ด้วย 
เราสามารถนำ module มาประยุกต์ใช้เพื่อให้ คลาส เสมือนมีความสามารถของการสืบทอดคุณสมบัติจากคลาสมากกว่าหนึ่งคลาส (multiple inheritance) นอกจากนี้ยังใช้ module เพื่อแบ่งแยกหมวดหมู่ของคลาสแต่และคลาสให้ชัดเจน (namespacomg) ได้ด้วย

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


2) Dynamic type: ไม่ต้องกำหนดชนิดของตัวแปร
อย่างที่เข้าใจกันว่า ตัวแปรคือสิ่งสมมติที่เรานำมาใช้ในการอ้างอิงถึงข้อมูลตัวใดตัวหนึ่ง เวลาเราจะนำข้อมูลไปใช้เพื่อคำนวณหรือกระทำการใดๆเราก็จะอ้างอิงถึงตัวแปรที่เก็บข้อมูลนั้นๆอยู่แทน
ข้อมูลที่พูดถึงก็มีอยู่ด้วยกันหลายประเภทครับ เช่น สตริง เลขจำนวนเต็ม เลขทศนิยม เป็นต้น

เพื่อให้คนเขียนโปรแกรมและตัวภาษาไม่งง และป้องกันข้อผิดพลาดที่อาจเกิดขึ้นจากการคำนวณด้วย แปรคนละชนิด ภาษาอย่าง Java หรือ C# จึงมีข้อกำหนดว่า ก่อนที่จะนำตัวแปรมาใช้ เราจะต้องประกาศตัวแปรกันก่อนเพื่อให้รู้ว่าตัวแปรตัวนั้นๆใช้เก็บข้อมูลประเภทไหน จะได้ไม่นำมาใช้มั่วกัน คนเขียนโปรแกรมจะเป็นคนรับผิดชอบในการกำหนดค่าให้กับข้อมูลแต่ละประเภทแต่ละตัว
เราเรียกลักษณะเช่นนี้ว่าเป็นการทำงานแบบ static type

การประกาศตัวแปรทำได้โดยการเขียนชื่อประเภทของข้อมูลไว้หน้าชื่อของตัวแปรในครั้งแรกที่เราสร้างตัวแปรนั้นขึ้นมา

ตัวอย่างการประกาศตัวแปรในภาษา C# หรือ Java สามารถเขียนได้ดังนี้
int a;
int b;
int sum;

a = 10;
b = 15;
sum = a + b;

ทีนี้กลับมาดูทางฝั่งภาษา Ruby บ้าง
ภาษา Ruby เป็น Dynamic type การจัดการกับประเภทของข้อมูลจะตรงข้ามกับ Static type
เราจะไม่เห็นการประกาศตัวแปรเพื่อกำหนดประเภทของข้อมูลเหมือนกับตัวอย่างที่ผ่านมา เพราะภาษา Ruby จะจัดการกับประเภทข้อมูลของตัวแปรให้เราโดยอัตโนมัติ เรามีหน้าที่แค่สร้างและนำตัวแปรไปใช้ให้ถูกต้องเท่านั้น ไม่ต้องไปกำหนดให้มัน

โค้ดแบบเดียวกับตัวอย่างที่ผ่านมาจึงเขียนใหม่ได้ดังนี้
#  Ruby code
a = 10
b = 15
sum = a + b   # => 25

จะเห็นว่าเราไม่ต้องประกาศประเภทของตัวแปร a และ b อีก สามารถนำไปใช้เก็บค่าของข้อมูลได้เลย
(ในที่นี้ตัวแปร a และ b จะเก็บค่าเลขจำนวนเต็ม)

นอกจากนี้ ตัวแปรตัวเดียวกันสามารถใช้เก็บข้อมูลคนละประเภทกันได้ด้วย
Ruby จะรู้เองโดยอัตโนมัติว่าในขณะนั้นตัวแปรตัวเก็บข้อมูลประเภทไหนอยู่ ซึ่งทำให้ Ruby นำตัวแปรไปใข้ได้อย่างถูกต้อง

a = 10
puts a  # => 10
a = "ten"
puts a  # => ten

การแปลงค่าจากตัวแปรประเภทหนึ่งไปเป็นตัวแปรอีกประเภทหนึ่ง (type casting) ไม่ได้เป็นเรื่องยากเย็นสำหรับ Ruby
การแปลงจากสตริงไปเป็นตัวเลข, ตัวเลขเป็นสตริง หรือสตริงเป็นอาเรย์ นั้ค่อนข้าวจะชิวทีเดียว
ลองดูผลลัพธ์ของการแปลงค่าจากตัวอย่างข้างล่างดูครับ

100.to_s      # => "100"
"1234".to_i   # => 1234
"1234".to_f   # => 1234.0
"one two three".split   # => ["one", "two", "three"]
[1,2,3].to_s.inspect    # => "[1,2,3]"


อย่างไรก็ตามเพื่อให้คนเขียนโปรแกรมไม่งง ว่าอันไหนเป็นตัวแปรอันไหนเป็นข้อมูล ภาษา Ruby จึงมีกฏเกณในการจำแนกประเภทของข้อมูลง่ายๆ ดังนี้ครับ

3) รันโค้ด Ruby ในแบบ procedure programming ได้ ไม่ต้องสร้างฟังชั่น main ก็ได้
ถึงแม้ว่าภาษา Ruby จะเป็น OOP ทั้งแท่ง แต่เราสามารถกำหนดให้มันรันโค้ดในลักษณะเดียวกับ Procedure programming ได้ด้วย คือรันแบบลุยไปเรื่อยทีบรรทัดต่อบรรทัด 

อัน นี้จะสะดวกมากเวลาที่เราต้องการทดสอบโค้ด หรือต้องการทดลองร่างโค้ดแบบ Top-down เพราะเราไม่จำเป็นต้องไปสร้างคลาสแล้วเริ่มรันโปรแกรมจาก main เหมือนกับภาษาอื่นๆเขียนโค้ดปุ๊บก็รันแล้วดูผลลัพธ์กันเลย

ลองเปรียบเทียบโค้ดโปรแกรม hello ระหว่าง java และ ruby ดู

ถ้าเป็น Java โค้ดที่จะเริ่มรันต้องอยู่ในเมธอด main ของคลาสที่เราสร้างขึ้นมาก่อน

class HelloWorldApp {
    public static void main(String[] args) {
        System.out.println("Hello World!"); // Display the string.
    }
}

แต่ถ้าเป็น Ruby เราไม่ต้องสร้างคลาสก็ได้ (ทุกอย่างเป็นอ็อบเจกต์และคลาสถูกกำหนดไว้ก่อนในฉากหลังแล้ว) เพียงเขียนโค้ดลงในซอร์ซไฟล์แล้วรันเลย

puts "Hello World!"

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

4) การจำแนกตัวแปรประเภทต่างๆ ใน Ruby
ถึงแม้ว่า Ruby จะเป็น Dynamic type ไม่ต้องประกาศตัวแปร แต่เราเองก็ต้องจำแนกแยกแยะให้ได้ว่า สิ่งที่ปรากฏอยู่ในโค้ด Ruby นั้นเป็นข้อมูล, ตัวแปร, เมธอด หรือคลาส
วิธีดูและแยกข้อมูลหรือตัวแปรในโค้ดให้ออกนั้นไม่ยาก มีหลักการง่ายๆ ดังนี้
  • อะไรก็ตามที่อยู่ภายใต้เครื่องหมายคำพูด(" ") หรือเครื่องหมาย (' ') ให้ถือว่ามันคือข้อมูลประเภทสตริง (String) เช่น "a", "hello", "Arsenal"
  • ตัวเลขที่ไม่มีจุดทศนิยมถือว่าเป็นข้อมูลประเภทเลขจำนวนเต็ม(Integer) เช่น 3, 25670 โดยเลขจำนวนเต็มใน Ruby เป็นอ็อบเจกต์ของคลาส Fixnum
  • ตัวเลขที่มีจุดทศนิยมถือว่าเป็นข้อมูลประเภทเลขทศนิยม(Floating point) เช่น 3.141, 0.0005 โดยตัวเลขทศนิยมเป็นอ็อบเจกต์ของคลาส Float
  • สิ่งที่อยู่ในเครื่องหมาย "[ , ]" จะถือว่าเป็นข้อมูลประเภทอาเรย์(Array) เช่น ["one", "two"] หรือ [1,2,3]
  • ข้อมูลที่อยู่ในอาเรย์ไม่จำเป็นต้องเป็นข้อมูลชนิดเดียวกันหมด ข้อมูลต่างชนิดกันสามารถปนกันอยู่ในอาเรย์ตัวเดียวได้ด้วย (แน่อนว่าเป็น dynamic type ) เช่น [1, "two", 3.00]
  • ตัวเลขที่อยู่ภายในเครื่องหมาย "( )" จะถือว่าเป็นข้อมูลประเภท range เช่น (0..9)
  • สิ่งที่อยู่ในเครื่องหมาย {} คือข้อมูลประเภท hash โดยใช้เครื่องหมาย => เป็นตัวขั้นระหว่างค่าของ key และ value เช่น {1=>"one", 2 =>"two", 3=>"three"}
  • ตัวอักษร (character) หรือคำ (word) ที่ขึ้นต้นด้วยตัวพิมพ์เล็ก อาจเป็นตัวแปร local หรือเมธอดก็ได้ ไม่ว่ามันจะเป็นตัวแปรหรือเมธอดให้มองไว้ว่าจะมีค่าบางค่าออกมาจากตัวมันแน่ นอน ตัวอย่างเช่น a, count, price_list, email 
  • ตัวอักษรหรือคำที่อยู่หลังเครื่องหมาย ":" ถือว่าเป็นข้อมูลประเภท symbol เช่น :physic, :school

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

5) Comment ในภาษา Ruby
Comment คือข้อความที่เราต้องการเขียนแทรกเอาไว้ในโค้ดเพื่ออธิบายการทำงานของโค้ดหรือเอาไว้ใส่ข้อความเพื่อเตือนความจำเวลาแก้ไขโค้ด ซึ่งข้อความที่เป็น comment นั้นจะไม่ถูกแปลรวมไปกับโค้ดเวลารันโปรแกรม
การใส่ comment ในภาษา Ruby ทำได้ 2 แบบคือ
1. ใช้เครื่องหมาย "#" วางไว้หน้าข้อความที่เราต้องการให้เป็น comment โดยจะสามารถใช้เครื่องหมาย "#" กับข้อความที่มีความยาวไม่เกินหนึ่งบรรทัดเท่านั้น
2. ใช้สัญลักษณ์ "=begin" และ "=end" ครอบข้อความที่เราต้องการให้เป็น comment โดยวิธีนี้จะสามารถใช้กับข้อความที่มีความยาวมากกว่า 1 บรรทัดขึ้นไปได้

5) ภาษา Ruby มีตัวแปลภาษาเป็นแบบ Interpreter
หมายความว่าลักษณะการแปลซอร์ซโค้ดของตัวแปลภาษา Ruby จะแปลโค้ดทีละบรรทัด โดยเริ่มจากบรรทัดแรกไปจนถึงบรรทัดสุดท้าย

4 ความคิดเห็น: