หน้าเว็บ

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

ใช้ GUI Designer ด้วย WxRuby

วันนี้ต้องขอเขียนถึงการสร้าง GUI หน่อยครับ ต้องขอออกตัวก่อนนะครับว่าอาจไม่ได้ลงรายละเอียดลึกมากถึงที่มาที่ไปของตัวช่วยต่างๆที่จะนำมาใช้ ส่วนนึงเพราะความรู้ที่ยังจำกัดของผม - -'' แต่จุประสงค์ทีสำคัญมากกว่าคืออยากให้คุณผู้อ่านได้รู้ถึงเทคนิกของการสร้าง GUI ใน Ruby ด้วยการใช้ GUI Designer ครับ

อ่าฮ้า Ruby มี GUI designer ให้ใช้ด้วยเหรอ?

อันนี้หลายคนอาจสงสัยเหมือนผม เพราะหลังจากที่ค้นหาพวก tools ที่ใช้ในการสร้าง Ruby GUI อยู่นาน ก็ยังไม่เจอ tools ที่สามารถแบบจับ widget มาวางเหมือนใน Visual studio หรือ NetBean เลย เหมือนจะต้องเขียนโค้ดมือตลอด กรำ...

หลังจากวนเวียนหาอยู่นาน ในที่สุดผมก็พบว่ามันมีหนทางที่จะใช้ tool แบบจับวางมาสร้าง Ruby GUI ซึ่งหนทางที่ว่าก็คือการใช้ WxRuby นั้นเอง
อย่างไรก็ตามการสร้าง Ruby GUI ด้วย WxRuby นั้นมีกระบวนการทำที่ต้องอาศัยการจดจำอยู่หลายขั้นตอน ผมเป็นหนึ่งในคนที่เคยใช้ WxRuby สร้าง Ruby GUI แล้วพอจะกลับมาทำใหม่ก็ลืมวิธีการทำทุกที ดังนั้นในโอกาสที่ผมกำลังนำ WxRuby มาใช้อีกครั้งผมจึงขอใช้โอกาสเดียวกันนี้เขียน how to ตัวนี้ขึ้นมาเพื่อกันลืมและเพื่อเป็นอีกทางเลือกหนึ่งให้คุณผู้อ่านได้ลองเล่นดูนะครับ

จะสร้าง GUI ต้องมีเตรียมอะไรบ้าง
Library ที่สามารถสร้าง GUI ใน Ruby มีอยู่หลายตัวครับเช่น FxRuby, WxRuby, Shoes หรือจะใช้ Win32 ก็ได้ แต่วันนี้เรามา focus กันที่ WxRuby ครับ ซึ่งเจ้า WxRuby เนี่ยก็เปน library ตัวลูกของทาง WxWidgets เขา ซึ่งทางนั้นเขาทำ Cross platform GUI library ออกมาให้ dev ทั้งหลายใช้กัน ก็ถือว่า WxWidget ก็เป็นค่ายใหญ่อีกค่ายนึงในส่วนของ cross plateform GUI ที่เป็นที่รู้จักกันดีของ dev ครับ

เราจะเริ่มจากไป download gem ของ WxRuby มาติดตั้งก่อนโดยการพิมพ์คำสั่งต่อไปนี้ใน command line(ต่อ net ให้เรียบร้อยก่อนนะครับ)


C:\gem install wxruby


จากนั้นให้ download library gem ที่ชื่อว่า wxSugar มาติดตั้งซึ่ง library ตัวนี้จะทำหน้าที่ในการ convert ไฟล์ที่ได้มาจากการสร้าง GUI แบบลากแปะให้กลายเป็นโค้ดภาษา Ruby
ให้พิมพ์คำสั่งนี้ในการติดตั้ง wxSugar นะครับ


C:\gem install wx_sugar


สุดท้ายให้ไป download โปรแกรม DialogBlocks ซึ่งโปรแกรมตัวนี้เป็น IDE ที่ทำหน้าที่สร้าง layout ของ GUI(หรือที่เรียกกันว่า widget) โดยใช้ library มาตรฐานของ WxWidget แล้ว export ออกมาเป็น xrc file เพื่อให้เรานำไป convert เป็นโค้ด ruby ต่อไป

เมื่อพร้อมแล้วก็มาเริ่มลงมือสร้าง GUI กันเลย
1) เปิดโปรแกรม DialogBlocks ขึ้นมาแล้วไปที่ File > New Project จากนั้นก็ตั้งชื่อ project แล้วกด next แล้วทำตามขั้นตอนทีละหน้าดังนี้
- หน้า mode ให้เลือก mode เป็น Generate XRC only แล้วกด next
- หน้า configuration ไปต้องใส่อะไร กด next ไปได้เลย
- หน้า Source encoding ถ้าไม่เปลี่ยนอะไรก็กด finish ได้เลย เป็นอันเสร็จสิ้นการ project

Figure 1


2) เริ่มสร้าง Frame ให้กับ GUI ของเราโดยไปที่เมนู Element > Add Frame แล้วตั้งชื่อ Windows Title และชื่อของ xrc file ซึ่งเจ้า xrc file ตัวนี้จะทำหน้าที่เก็บข้อมูลรูปร่างหน้าตารวมถึงรายละเอียดต่างๆของ GUI ที่เราสร้างให้อยู่ในรูปของ XML format

3) เมื่อตั้งชื่อเสร็จแล้วเราจะได้ Frame ออกมามีหน้าตาดังรูป จากนั้นเราต้องไปกำหนดชื่อคลาสให้กับ Frame ที่เราสร้างขึ้นครับ โดยให้เลือกไปที่ icon ที่ 2 ตรงมุมขวาล่างของ DialogBlock แล้วไปแก้ไขในส่วนของ field ที่ชื่อว่า class ดังรูปที่ 2 ซึ่งในที่นี้ผมตั้งชื่อ class นี้ว่า MyRubyFrame

Figure 2


4)จากนั้นให้ add sizer เข้าไปใน Frame ซึ่งเจ้า Sizer นั้นเป็นตัวกำหนดรูปแบบของ layout ให้กับ component ต่างๆเช่น ปุ่ม หรือ กล่องข้อความที่เราจะนำมาใส่ต่อไป การ add sizer ทำได้โดยการ click ไปที่ icon ของ tabs Sizers ซึ่งในที่นี้ผมเลย layout แบบ BoxSizer Vertical

5)ผมเพิ่ม component ต่างเข้าไปใน sizer โดยให้กำหนดชื่อของ component แต่ละตัวไว้ใน field 'id' ดังรูป เพื่อใช้ในการอ้างอิงตอนที่เราต้องนำไปใช้ในโค้ดภาษา Ruby ต่อไป นอกจากนี้เรายังสามารถกำหนด properties ต่างๆให้กับ components ได้เช่นขนาดของ Frame สี พื้นหลัง ขนาดตัวอักษร etc...ตรงนี้คุณผู้อ่านต้องลองเล่นดูนะครับ

Figure 3


6) เมื่อได้หน้าตาของ GUI ในแบบที่พอใจแล้ว คราวนี้ save project เอาไว้ก่อนเผื่อแก้ไข จากนั้นให้เลือก File > Export XRC เพื่อเก็บข้อมูลของ GUI ให้อยู่ในรูปของไฟล์ xrc

7) ขั้นตอนนี้จะเป็นการ convert file แบบ xrc ที่เรา export มาจากข้อ 6 ให้กลายเป็นโค้ดภาษา Ruby ซึ่งโค้ดดังกล่าวจะเป็นคลาสของ GUI ที่เราสร้างขึ้นโดยมีชื่อคลาสตามที่เราตั้งชื่อเอาไว้จากข้อ 3
ให้ไปที่ directory ที่เราเก็บไฟล์ xrc เอาไว้แล้วรันคำสั่งดังตัวอย่างต่อไปนี้


xrcise -o my_ruby_gui.rb my_ruby_gui.xrc


8) ขั้นตอนสุดท้ายคือการเขียนโค้ดเพื่อแสดงผล GUI ที่เราสร้างขึ้น โดยผมสร้างไฟล์ขึ้นมาใหม่อีกไฟล์ชื่อว่า the_gui.rb เพื่อเรียกให้ GUI ซึ่งมีโค้ดดังนี้ (โค้ดที่เขียนเพื่อเรียกใช้ library WxRuby นั้นต้องไปศึกษาเพิ่มเติมต่างหากครับ ไม่ยากครับไม่ยาก)


the_gui.rb
1
2
3
4
5
6
7
8
9
10
11
require 'Wx'
require 'my_ruby_gui.rb'

class MyRubyGUI < MyRubyFrame

end

# Run GUI by wx show method
Wx::App.run do
MyRubyGUI.new.show
end



9) เมื่อรันโค้ด the_gui.rb แล้วก็จะได้ผลลัพธ์เป็น GUI ที่เราสร้างจากโปรแกรม DialogBlock ดังรูปครับ เย้ๆๆๆ

Figure 4


โดยส่วนตัวแล้วการสร้าง GUI ด้วยวิธีนี้ช่วยเราได้เยอะเลยครับ อยางน้องก็ไม่ต้องปวดหัวเวลาเขียนโค้ดจัดการกับพวก Sizer ทำให้เหลือแต่โค้ดที่เกี่ยวกับ Event ของ widget แต่ละตัวเท่านั้นที่เราแค่เขียนเพิ่มเข้าไป

ลองไปลองมา WxWidgets ก็ไม่เลวเหมือนกันนะ คิดเหมือนผมมั้ยครับ

วันพฤหัสบดีที่ 29 ตุลาคม พ.ศ. 2552

เทคนิกเล็กๆน้อยๆกับสตริง

ต้องคอยเตือนตัวเองไว้เสมอว่าเกือบทุกอย่างใน Ruby นั้นเป็นอ็อบเจกต์แทบทั้งหมด
สตริงก็เหมือนกันครับ เป็นอ็อบเจกต์
เมื่อมันเป็นอ็อบเจกต์ คุณก็น่าจะคาดได้ว่าเราสามารถเรียกใช้เมธอดของคลาสตริงจากอ็อบเจกต์ได้ด้วย เมธอดส่วนใหญ่เป็น build-in เมธอดที่ถูกกำหนดอยู่ในคลาส String ซึ่งเป็นคลาสมาตรฐานใน Ruby อยู่แล้ว เช่น
puts "string".reverse
puts "string".upcase

ผลลัพธ์ที่ออกมาก็เป็นไปตามคาด

gnirts
STRING

ที่น่าสนใจน่ะอันนี้ต่างหาก
ถ้าผมทำอย่างนี้ล่ะ พอจะเดากันออกมั้ยครับว่าจะได้ผลลัพธ์ยังไง
puts "string"[0]
puts "string"[1]
puts "string"[2]
puts "string"[3]
puts "string"[4]
puts "string"[5]
puts "string"[6]

ตอนแรกผมเดาว่าน่าจะได้ผลออกมาเป็น charecter แต่ละตัวของคำๆนี้เช่น "s", "t", ....
แต่ไม่ใช่
สิ่งที่ได้คือ ASCII โค้ดของตัวอักษรแต่ละตัวต่างหาก ซึ่งถ้าเราใส่ index ที่ไม่มีตัวอักษรในตำแหน่งนั้นเราก็จะได้ค่าเป็น nil ออกมาแทน(ตามตัวอย่างคือตำแหน่งที่ 6)

115
116
114
105
110
103
nil

พอเป็นอย่างนี้ผมก็สามารถใช้เทกนิคนี้มาใช้แปลงลำดับของตัวอักษรที่ผมอยากทำให้มันเป็น index ได้ง่ายๆยกตัวอย่างเช่น
ถ้าผมอยากแปลงตำแหน่งของระบบ row, column อย่างที่ใช้ใน Excel มาเป็นระบบคู่ลำดับ x,y เช่นตำแหน่ง A1 ก็คือ (1,1) หรือ H6 ก็คือ (8, 6) ผมก็สามารถเขียน function แปลงได้ดังนี้
def convert(cell)
  str = cell.downcase
  x = str[0] - 'a'[0]
  y = str[1] - '0'[0]
  
  return x, y
end

pos = convert("J5")
puts pos

พอจะได้ idea นะครับ
อย่างไรก็ตาม function นี้ยังมีจุดที่ต้องระวังเวลานำไปใช้ คือมันจะ work ก็ต่อเมื่อค่าของ row และ column นั้นเป็นตัวอักษรตัวเดียวเท่านั้น ถ้าเราใส่เป็น AB3 หรือ A16 อย่างงี้มันจะแสดงผลไม่ถูกต้องครับ ลองคิดต่อเล่นๆละกันครับว่าถ้าอย่างให้มันแสดงค่าได้ถูกต้องนั้นต้องทำยังไง


วันนี้พอแค่นี้ ไปก่อนล่ะครับ หุหุ



วันจันทร์ที่ 14 กันยายน พ.ศ. 2552

โค้ดสวยๆ ด้วย Syntax highlight

ผมคิดว่าหลายๆคนคงสงสัยสินะครับว่าโค้ดสีสวยที่เห็นผมเขียนลงในบล็อกนั้นมันทำยังไง
การทำโค้ดให้เป็นสีๆแบบนี้เค้าเรียกว่า Syntax highlight

ซึ่งเราสามารถหาเครื่องมือเพื่อช่วยทำ syntax highlight ได้ทั่วไปในเน็ทครับ
ตัวช่วยทำไฮไลท์ยอดนิยมที่เราน่าจะคุ้นๆ เห็นอยู่บ่อยๆก็มาจาก site นี้เลยครับ http://code.google.com/p/syntaxhighlighter/ ลองตามไปอ่านดูนะครับ tool ตัวนี้เป็น javascript

แต่

syntax highlight ที่ทุกท่านเห็นผมใช้ในบล็อกนี้นั้นไม่ได้ใช้ tool จาก link ข้างบนหรอกครับ
ผมใช้ tool อีกตัวที่เขียนด้วยภาษา Ruby ต่างหาก
เอาน่า ไหนๆบล็อกนี้ก็เขียนเกี่ยวกับ Ruby แล้ว Syntax Highlight จะทำจากภาษา Ruby ก็ไม่แปลกใช้ไหนครับ อิอิ

Tool ที่ผมใช้ทำไฮไลท์นั้นเป็นการนำไลบรารี่ gem ที่ชื่อ CodeRay มาใช้ครับ(เราเรียกไลบรารี่ที่ใช้ในภาษา Ruby ว่า gem ครับ) สำหรับ CodeRay เวอร์ชั่นล่าสุด(0.8.3)นั้นก็สามารถรองรับการทำ Syntax highlight ได้หลายภาษาทีเดียว แต่ก็ยังถือว่าไม่สมบูรณ์นักเพราะยังไม่รองรับภาษาที่ฮิตๆอย่าง C++, C#, PHP หรือ Perl ได้ แต่สำหรับการทำ highlight ให้กับโค้ดภาษาเช่น Ruby, Java, JavaScript, HTML หรือ CSS นั้นถือว่าสอบผ่านครับ
ไปดูวิธีเลยดีกว่าว่ามันใช้ยังไง

ติดตั้ง CodeRay ก่อน

เริ่มจากการดาวน์โหลดไลบรารี่ก่อนโดยเรียก Windows command prompt ขึ้นมา แล้วพิมพ์คำสั่งตามนี้
C:\> gem install coderay
เพื่อให้ RubyGem ทำการ download ไลบรารี่ CodeRay ให้โดยอัตโนมัติ เมื่อดาวน์โหลดแล้วก็จะได้ผลลัพธ์ประมาณนี้



พร้อมแล้วก็ Generate โค้ดเลย

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

1
2
3
4
5
6
7
8
9
10
require 'rubygems'
require 'coderay'
 
my_code = <<-'CODE'
puts "Colorful your code with CodeRay"
3.times { puts "I'm hungry" }

CODE

tokens = CodeRay.scan(my_code, :ruby)
 
puts tokens.div(:line_numbers => :table, :css=> :class)


จากโค้ดตัวอย่าง บรรทัดที่ 1-2 เป็นการเรียกไลบรารีที่จำเป็นเข้ามาใช้ซึ่งในที่นี้คือไลบรารีที่ชื่อ rubygems และ coderay
ในขณะที่โค้ดในบรรทัดที่ 5-6 คือโค้ดต้นแบบที่เราต้องการ ซึ่งในตัวอย่างนี้โค้ดของเราเขียนอยู่ในรูปของ Here document(ครอบอยู่ในคีย์เวิร์ด 'CODE')และถูกนำไปเก็บไว้ในตัวแปร my_code อีกทีหนึ่ง
จากนั้นเราจะทำการแปรโค้ดตาม syntax ของภาษาต่างๆด้วยคลาสเมธอด scan ในบรรทัดที่ 8 ซึ่งผลของการแปลโค้ดทำให้เราได้อ็อบเจกต์ของ coderay ซึ่งจะถูกเก็บอยู่ในตัวแปร token

สุดท้ายเราจะนำเจ้าอ็อบเจกต์ token นี้มาแสดงผลลัพธ์ให้ออกมาเป็นโค้ด HTML โดยการเรียกใช้เมธอด div ในบรรทัดที่ 10
ซึ่งผลลัพธ์ที่ได้ก็จะเป็นโค้ดที่ทำ highlight เรียบร้อยสวยงามแต่มันจะอยู่ในรูปของ HTML โค้ดดังนี้
<table class="CodeRay"><tr><br /> <td class="line_numbers" title="click to toggle" onclick="with (this.firstChild.style) { display = (display == '') ? 'none' : '' }"><pre>1<tt><br /></tt>2<tt><br /></tt>3<tt><br /></tt>4<tt><br /></tt>5<tt><br /></tt>6<tt><br /></tt>7<tt><br /></tt>8<tt><br /></tt>9<tt><br /></tt><strong>10</strong><tt><br /></tt></pre></td><br /> <td class="code"><pre ondblclick="with (this.style) { overflow = (overflow == 'auto' || overflow == '') ? 'visible' : 'auto' }">require <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">'</span><span style="">rubygems</span><span style="color:#710">'</span></span><tt><br /></tt>require <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">'</span><span style="">coderay</span><span style="color:#710">'</span></span><tt><br /></tt><tt><br /></tt>my_code = <span style="background-color:#fff0f0;color:#D20"><span style="color:#710">&lt;&lt;-'CODE'</span></span><span style="background-color:#fff0f0;color:#D20"><span style=""><tt><br /></tt> puts &quot;Colorful your code with CodeRay&quot;<tt><br /></tt> 3.times { puts &quot;I'm hungry&quot; }</span><span style="color:#710"><tt><br /></tt> CODE</span></span><tt><br /></tt>tokens = <span style="color:#036;font-weight:bold">CodeRay</span>.scan(my_code, <span style="color:#A60">:ruby</span>)<tt><br /></tt><tt><br /></tt>puts tokens.div(<span style="color:#A60">:line_numbers</span> =&gt; <span style="color:#A60">:table</span>, <span style="color:#A60">:css</span>=&gt; <span style="color:#A60">:class</span>)</pre></td><br /></tr></table>


ทดลองนำไปใช้กันดูนะครับ ไม่ยากเกินไปและสามารถนำไปใช้ประโยชน์ได้แน่นอน
CodeRay นั้นรองรับ

ทำ GUI เอาไว้ใช้เอง

แถมอีกนิดอ่ะครับ เนื่องจากผมเล็งเห็นแล้วว่าต้องใช้ CodeRay เพื่อทำ highlight ให้กับโค้ดแล้วนำลงบล็อกบ่อยแน่เลย
ดังนั้นผมจึงสร้าง GUI ง่ายๆ เพื่อใช้สำหรับ convert โค้ดต้นแบบให้เป็นโค้ด HTML ที่ทำผ่านการทำ highlight เรียบร้อยแล้วและพร้อมจะนำไปแปะบนบล็อกได้เลย ในที่นี้ผมจะใช้ไลบรารี gem อีกตัวหนึ่งคือ wxruby มาช่วยในการสร้าง GUI ซึ่งผมขออนุญาตข้ามในส่วนของรายละเอียดในการสร้าง GUI ด้วย wxruby ไปก่อนนะครับ ไว้จะมาเล่าให้ฟังในคราวต่อๆไป

เมื่อลงโค้ดจนได้ที่แล้ว เจ้า GUI ที่ใช้ทำ syntax highlight ของผมก็เลยมีหน้าตาแบบนี้ครับ



วิธีใช้ก็แค่นำโค้ดต้นแบบมาแปะลงใน text box ด้านซ้ายแล้วกดปุ่ม convert เราก็จะได้โค้ดที่ทำ highlight แล้วที่อยู่ในรูปของ HTML ครับ ซึ่งผมทำ option ให้เลือกได้ด้วยว่าจะทำ highlight เป็นภาษาอะไร

วันศุกร์ที่ 28 สิงหาคม พ.ศ. 2552

Constructor และคุณสมบัติภายในคลาส

ลังจากที่เราทำความเข้าใจกับหลักการเบื้องต้นของ OOP เกี่ยวกับคลาสและอ็อบเจกต์แล้ว
วันนี้เราจะมาว่าถึงรายละเอียดที่อยู่ภายในคลาสกัน ซึ่งได้แก่ constrcutor, ตัวแปร instant, properties ของคลาส, ตัวแปรคลาส ครับ

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

note: เมธอด Constrcutor ของภาษา Java หรือ C# คือเมธอดที่มีชื่อเดียวกับคลาสครับแต่หลักการทำงานนั้นเหมือนกันคือจะถูกเรียกใช้ตอนสร้างอ็อบเจกต์เสมอ)

เรายังจำกันได้นะครับว่าเวลาสร้างอ็อบเจกต์เราจะต้องเรียกใช้เมธอด "new" เช่นเขียนว่า iphone = Phone.new ก็คือการสร้างอ็อบเจกต์จากคลาส Phone โดยอ็อบเจกต์นั้นจะเก็บอยู่ในตัวแปร iphone ซึ่งเจ้าเมธอด new นี่แหละจะมีความสัมพันธ์โดยตรงกับเมธอด intiailize ของเรา นั่นคือหลังจากที่เราเรียกเมธอด new ตัวแปลภาษา Ruby จะเข้าไปหาว่าภายในคลาสนั้นมีการกำหนดเมธอดชื่อ initialize เอาไว้หรือไม่ ถ้ามี Ruby ก็จะเรียกเมธอด initialize ขึ้นมาใช้ทันทีครับ

ดังนั้นทุกๆครั้งที่เราสร้างอ็อบเจกต์ขึ้นมาจากคลาส เมธอด initialize ก็จะถูกเรียกใช้ก่อนเสมอ ซึ่งเมธอด initialize จะทำหน้าที่กำหนดสถานะเบื้องต้นของอ็อบเจกต์ ว่า

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

เราลองว่าดูการทำงานของเมธอด initialize และ ตัวแปร instant ไปพร้อมๆกันดีกว่าครับ
สมมติว่าผมมีคลาสที่ชื่อว่า Phone ดังตัวอย่างต่อไปนี้
class Phone
  def initialize(mfg, model, color)
    @mfg, @model, @color = mfg, model, color
  end

  def info
    "MFG: #{@mfg}\tMODEL:#{@model}\tCOLOR:#{@color}"
  end

  def call(number)
    puts "Calling #{number}"
  end
end
 
# create phone objects
phone_1 = Phone.new("Nokia", "3310", "Dark Blue")
phone_2 = Phone.new("Nokia", "N70", "White")
phone_3 = Phone.new("HTC", "Touch", "Black")
 
# call method info for each phones
puts phone_1.info
puts phone_2.info
puts phone_3.info

คลาส Phone ของเรามีเมธอดอยู่ 3 เมธอดครับคือ initialize, info และ call อย่างที่อธิบายไปแล้วว่าเมธอด initialize จะถูกเรียกใช้เสมอเมื่อมีการสร้างอ็อบเจกต์ด้วย new

สังเกตุนิดนึงนะครับว่าเมธอด initialize ในบรรทัดที่ 2 นั้นมีการกำหนดให้ต้องใส่พารามิเตอร์ลงไปด้วยกัน 3 ตัว ซึ่งค่าของพารามิเตอร์แต่ละตัวก็จะถูกกำหนดลงไปในตัวแปร instant ที่ชื่อ @mfg, @model และ @color ตามลำดับ เมื่อเมธอด initialize ถูกกำหนดให้ต้องใส่ค่าพารามิเตอร์ดังนั้นเวลาเราเรียกเมธอด “new” เราก็ต้องใส่พารามิเตอร์แบบเดียวกับที่กำหนดไว้ในเมธอด initialize ให้มันด้วย เพราะเมธอดทั้งสองต่างก็มีความสัมพันธ์ต่อกัน โดยค่าของพารามิเตอร์ที่ใส่เข้าไปในเมธอด initialize จะเป็นตัวกำหนดคุณลักษณะของอ็อบเจกต์แต่ละตัวด้วย

เมื่อสร้างคลาสเสร็จเรียบร้อยแล้วเราก็จะนำคลาสมาสร้างอ็อบเจกต์ดังแสดงในบรรทัดที่ 13-15 ซึ่งเป็นการสร้างอ็อบเจกต์ขึ้นมา 3 อ็อบเจกต์ด้วยการเรียกเมธอด new
อ็อบเจกต์แต่ละตัวจะมีคุณลักษณะที่แตกต่างกันไปตามค่าของพารามิเตอร์เราใส่ลงไปในเมธอด new เช่น ยี่ห้อ(@mfg), รุ่น(@model) และ สี(@color) ของอ็อบเจกต์โทรศัพธ์มือถือ ซึ่งค่าเหล่านี้จะถูกนำไปใช้สำหรับกำหนดค่าของตัวแปร instant ในเมธอด initialize อีกทีหนึ่งครับ ดังนั้นอ็อบเจกต์ที่เกิดจากคลาสเดียวกันก็อาจจะมีหน้าตาที่ต่างกันได้


Properties ของอ็อบเจกต์มือถือแต่ละเครื่อง (@mfg, @model, @color) ทำให้อ็อบเจกต์ที่มาจากคลาสมือถือเดียวกันมีหน้าตาต่างกัน แต่ความสามารถในการทำงาน (method) ยังเหมือนกันทุกประการซึ่งในที่นี้อ็อบเจกต์ทั้งสามทำได้แค่โทรออกและแสดงข้อมูลของเครื่องเท่านั้น (เมธอด call และ เมธอด info)

เมื่อเรารันซอร์ซโค้ด phone.rb เราจะได้ข้อความที่แสดงข้อมูลของอ็อบเจกต์โทรศัพท์มือถือแต่ละเครื่องดังนั้น ซึ่งเป็นผลลัพธ์มาจากโค้ดในบรรทัดที่ 17-19 ครับ
MFG: Nokia MODEL:3310 COLOR:Dark Blue
MFG: Nokia MODEL:N70 COLOR:White
MFG: HTC MODEL:Touch COLOR:Black

ถึงแม้ว่าอ็อบเจกต์ที่เกิดจากคลาสเดียวกันจะสามารถมีลักษณะที่ต่างกันไป แต่มันยังคงความสามารถในการทำงานได้เหมือนกันตามที่กำหนดไว้ในคลาส หรือพูดอีกอย่างก็คืออ็อบเจกต์พวกนี้มี properties ต่างกันได้แต่เมธอดที่กำหนดการกระทำนั้นยังคงเหมือนกัน ในที่นี้ทั้งอ็อบเจกต์ของ HTC touch, N70 และ 3310 ต่างก็ทำได้แค่โทรออก(เมธอด call) และเรียกดูรายละเอียด(เมธอด info) เหมือนกัน เพราะในคลาส Phone กำหนดเมธอดไว้แค่นั้น

ค่าทั่วไปของอ็อบเจกต์
คราวนี้ลองมาดูกันว่าถ้าผมเรียกเมธอด new แล้วไม่ใส่พารามิเตอร์จะเกิดอะไรขึ้น
สมมติว่าผมสร้างอ็อบเจกต์ phone_4 เพิ่มเข้าไปแบบนี้
class Phone #คลาสเหมือนเดิมจากตัวอย่างที่แล้ว # … end phone_4 = Phone.new puts phone_4.info
เมื่อลองรันซอร์ซโค้ดแล้วปรากฏว่าเกิดข้อผิดพลาดดังนี้ครับ

phone_2.rb:10:in `initialize': wrong number of arguments (0 for 3) (ArgumentError)
from phone_2.rb:10:in `new'
from phone_2.rb:10

อ่า... มันฟ้องว่าเราต้องใส่พารามิเตอร์ 3 ตัวให้กับเมธอด new ซึ่งก็เป็นอย่างที่เราคาดเอาไว้ครับ
อย่างไรก็ตามหากเราจะสร้างอ็อบเจกต์ออกมาใช่ เราไม่จำเป็นต้องมานั่งใส่ค่าพารามิเตอร์ให้ครบทุกครั้งเสมอไป เพราะเราสามารถบอกคลาสให้ตั้งค่าทั่วไป(Default) ให้กับอ็อบเจกต์ที่เราสร้างขึ้นมาได้เลย หมายความว่าถ้าเราไม่ใส่ค่าพารามิเตอร์เลยตอนที่เรา new อ็อบเจกต์ ค่าของพารามิเตอร์เหล่านั้นจะถูกกำหนดให้เป็นค่า default โดยอัตโนมัติครับ
class Phone
  def initialize(mfg="xxx", model="xxx", color="xxx")
    @mfg, @model, @color = mfg, model, color
  End
   # …
end
 
phone_1 = Phone.new
phone_2 = Phone.new("Nokia")
phone_3 = Phone.new("HTC", "Touch", "Black")
 
puts phone_1.info
puts phone_2.info
puts phone_3.info

phone_3.rb คือโค้ดที่ทำการปรับปรุงคลาส Phone ใหม่ โดยจะเห็นว่ามีการกำหนดค่า default ให้กับพารามิเตอร์แต่ละตัวในบรรทัดที่ 2
เมื่อเรา new อ็อบเจกต์โดยไม่ใส่พารามิเตอร์ ค่าพารามิเตอร์ของเมธอด initialize (mfg, model และ color) จะถูกกำหนดให้เป็นค่า default ซึ่งก็คือ “xxx” โดยอัตโนมัติ
มีข้อสังเกตุเล็กน้อยแต่ถ้าสมมติว่าเราใส่ค่าพารามิเตอร์เพียงตัวเดียวลงไป ค่าของตัวแปร instant ในเมธอด initialize ก็จะถูกกำหนดเพียงค่าเดียว(@mfg) ในขณะที่ตัวแปร instant ตัวอื่นก็จะถูกกำหนดให้เป็นค่า default เหมือนเดิม ดังแสดงในบรรทัดที่ 10-12 ครับ

ดังนั้นผลลัพธ์ของการรันซอร์ซโค้ด phone_3.rb จึงมีหน้าตาดังนี้
MFG: xxx MODEL:xxx COLOR:xxx
MFG: Nokia MODEL:xxx COLOR:xxx
MFG: HTC MODEL:Touch COLOR:Black

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

โดยทั่วไปแล้วการใช้ประโยชน์จาก properties มีอยู่ด้วยกัน 2 ลักษณะคือ 1) อ่านค่าของ properties ตัวนั้นแล้วนำไปใช้ 2) กำหนดค่าเก็บไว้ใน properties เพื่อนำไปใช้ประโยชน์ในคราวต่อไป ซึ่งทั้งสองลักษณะนี้ก็คือการอ่านและเขียนค่าลงใน properties นั้นเอง
การทำให้ properties ของอ็อบเจกต์สามารถ “อ่าน” และ “เขียน” ได้นั้นก็ไม่ยากครับ เราเพียงแค่สร้างเมธอดที่มีชื่อเหมือนกับ properties ขึ้นมาเพื่อใช้เป็นช่องทางในการเข้าถึงค่าของ properties นั้น
class Phone
  def initialize(mfg="Nokia", model="5310", color="Black-Red")
    @mfg, @model, @color = mfg, model, color
  end
  def info
    "MFG: #{@mfg}\tMODEL:#{@model}\tCOLOR:#{@color}"
  end
  def color
    @color
  end
  def color=(new_color)
    @color = new_color
  end
end

phone_1 = Phone.new
puts "Default color is " + phone_1.color

phone_1.color = "White-Blue"
puts "Now, the color change to " + phone_1.color
puts phone_1.info

จากคลาส Phone ในโค้ด phone_4.rb นั้น ผมกำหนดให้เราสามารถตรวจสอบสีของอ็อบเจกต์มือถือ และเปลี่ยนสีของอ็อบเจกต์มือถือได้ตามต้องการ

จะพบว่ามีเมธอดชื่อ color และ color= เพิ่มเข้ามา โดยเมธอด color นั้นจะทำหน้าที่คืนค่าของสีที่อยู่ในตัวแปร @color ออกมาซึ่งก็คือการ “อ่าน” ค่าสีออกมาจาก properties ที่ชื่อ color ในขณะที่เมธอด color= จะทำหน้าที่กำหนดค่าสีใหม่ให้กับตัวแปร @color ซึ่งก็เปรียบเสมือนการ “เขียน” ค่าสีลงไปใน properties ที่ชื่อ color ด้วยเช่นกัน เรามักจะเรียกเมธอดที่ทำหน้าที่ “อ่าน” ค่าของ properties อย่างเมธอด color ว่าเป็นเมธอด “getter” และเรียกเมธอดที่ทำหน้าที่ “เขียน” เหมือนอย่างเมธอด color= ว่าเมธอด “setter” ครับ

เมื่อเรารันรันโค้ด phone_4.rb ก็จะได้ผลลัพธ์ดังนี้ ซึ่งจะเห็นว่าการอ่านและเขียนค่าของ properties color ในบรรทัดที่ 17 และ19 นั้นจะทำผ่านเมธอดอย่าง color และ color= ทั้งสิ้น
Default color is Black-Red
Now, the color change to White-Blue
MFG: Nokia MODEL:5310 COLOR:White-Blue


เราสามารถนำเทคนิคการสร้างเมธอด setter และ getter ไปใช้กับ properties ตัวอื่นๆของคลาสได้ด้วยครับ ซึ่งผมสามารถปรับปรับคลาส Phone ออกมาใหม่ได้ดังโค้ด phone_5.rb
class Phone
  def initialize(mfg="Nokia", model="5310", color="Black-Red")
    @mfg, @model, @color = mfg, model, color
  end
  def info
    "MFG: #{@mfg}\tMODEL:#{@model}\tCOLOR:#{@color}"
  end
  def color
    @color
  end
  def color=(new_color)
    @color = new_color
  end
  def mfg
    @mfg
  end
  def mfg=(new_mfg)
    @mfg = new_mfg
  end
  def model
    @model
  end
  def model=(new_model)
    @model = new_model
  end
end
 
phone_1 = Phone.new
puts phone_1.info
 
phone_1.mfg = "Samsung"
phone_1.model = "Omnia"
phone_1.color = "Black"
phone_1.info
puts phone_1.info

จากโค้ด phone_5.rb properties ทุกตัวทั้ง mfg, model และ color นั้นต่างก็สามารถอ่านและเขียนผ่านทางเมธอดได้แล้ว ซึ่งหลังจากการทดลองรันโค้ด phone_5.rb จะพบว่าค่าของ properties ทุกตัวนั้นจะเปลี่ยนไปจากค่า default ในตอนแรกเมื่อเรากำหนดค่าของ properties แต่ละตัวใหม่ในบรรทัดที่ 31-33 ดังนั้นเมื่อเรียกดูสถานะของ properties ด้วยเมธอด info ก็จะได้ผลลัพธ์ดังนี้

MFG: Nokia MODEL:5310 COLOR:Black-Red
MFG: Samsung MODEL:Omnia COLOR:Black

การเขียนเมธอด setter และ getter ในโค้ด phone_5.rb นั้นออกจะยาวเกินไปครับสำหรับค่า properties เพียงแค่ 3 ตัวถ้ามี properties ซัก 10 ตัว คงต้องเขียนกันถึง 20 เมธอด คงดูไม่ดีแน่ ดังนั้น Ruby จึงมีวิธีประกาศเมธอด setter และ getter แบบสั้นๆครับ ซึ่งเราเรียกว่าการใช้ attribute accessor

Note: ในที่นี้ Properties และ Attributes คือสิ่งเดียวกันซึ่งหมายถึงคุณลักษณะของอ็อบเจกต์นะครับ แต่บางทีอาจจะเรียกต่างกันไปบ้างเท่านั้น

ดังนั้นผมจึงสามารถเขียนนำโค้ด phone_5.rb มาเขียนใหม่โดยใช้ attribute accessor เพื่อลดจำนวนโค้ดได้ดังนี้
class Phone
  attr_accessor :mfg, :model, :color
  def initialize(mfg="Nokia", model="5310", color="Black-Red")
    @mfg, @model, @color = mfg, model, color
  end
  def info
    "MFG: #{@mfg}\tMODEL:#{@model}\tCOLOR:#{@color}"
  end
end
 
phone_1 = Phone.new
puts phone_1.info
 
phone_1.mfg = "Samsung"
phone_1.model = "Omnia"
phone_1.color = "Black"
phone_1.info
puts phone_1.info

ในบรรทัดที่ 2 ของโค้ด phone_6.rb นั้นมีการประกาศ attribute accessor ให้กับ properties โดยใช้คีย์เวิร์ด attr_accessor ทำให้ properties ทั้งสามตัวของคลาสนี้มีคุณสมบัติเป็นเมธอด setter และ getter โดยอัตโนมัติ ซึ่งจะเห็นได้ว่าการใช้ attribute accessor สามารถลดจำนวนของโค้ดที่ต้องเขียนได้มากทีเดียว
ดังนั้นผลลัพธ์ของการรันโค้ด phone_6.rb จึงเหมือนกับผลลัพธ์ของการรันโค้ด phone_5.rb ทุกประการ

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

วันศุกร์ที่ 21 สิงหาคม พ.ศ. 2552

Irb (Interactive Ruby) เครื่องมือที่คนเขียน Ruby ทุกคนต้องทำความรู้จัก

Irb หรือ Interactive Ruby เป็นโปรแกรมที่มีมาให้พร้อมกับตัวติดตั้งภาษา Ruby ตั้งแต่แรก เราสามารถเขียนโค้ดภาษา Ruby ด้วย irb ซึ่งมันจะแปลโค้ดภาษา Ruby และแสดงผลลัพธ์พร้อมค่าที่ return ออกมาจากการแปลโค้ดให้ทุกครั้ง ซึ่งการที่มันแสดงค่า return ของการแปลโค้ดแต่ละครั้งนั้น มีประโยชน์มากในการ debug เพราะมันช่วยให้เราเข้าใจที่มาที่ไปและการทำงานของโค้ดโดยละเอียด

สำหรับท่านที่ใช้ Ruby บน Window นั้นสามารถเรียกใช้ irb ได้โดยการพิมพ์ “irb” ลงไปที่ Windows command prompt ดังตัวอย่างด้านล่าง



จากรูปจะเห็นว่า Windows Command prompt จะเปลี่ยนเป็น irb prompt แสดงว่า irb พร้อมที่จะทำงานแล้ว
เมื่อผมลองพิมพ์โค้ดโดยใช้คำสั่ง puts ลงไป สิ่งที่ได้ก็คือข้อความที่แสดงออกมาบนหน้าจอ และค่าผลลัพธ์ที่ return ออกมาหลังจากที่ Ruby แปลโค้ดบรรทัดนี้เสร็จ โดยจะใช้เครื่องหมาย “=>” เพื่อแสดงว่ามันคือค่า return จากตัวอย่างนี้ค่า return ก็คือ nil หรือค่าว่างเปล่านั้นเองครับ

ลองดูตัวอย่างการใช้ irb และค่าที่มัน return ออกมาในแต่ละครั้งกันดีกว่าครับ จะได้ get idea
C:\>irb
irb(main):001:0> 1+1
=> 2
irb(main):002:0> "one" + "two"
=> "onetwo"
irb(main):003:0> 3.times { puts "Help! " }
Help!
Help!
Help!
=> 3
irb(main):004:0> x = "Arsenal"
=> "Arsenal"
irb(main):005:0> x
=> "Arsenal"
irb(main):006:0> x.upcase
=> "ARSENAL"
irb(main):007:0> x
=> "Arsenal"
irb(main):008:0> x.reverse
=> "lanesrA"
irb(main):009:0>


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

วันอังคารที่ 11 สิงหาคม พ.ศ. 2552

[ภาษา Ruby] OOP ตอนที่ 1 Class และ Object

Object Oriented Programming

การเขียนโปรแกรมแบบ OOP(Object Oriented Programming) หรือที่เรียกกันว่าการเขียนโปรแกรมเชิงวัตถุนั้น คือ ”รูปแบบ” ของการเขียนโปรแกรมอย่างหนึ่งที่บังคับ(แบบอ้อมๆ)ให้เราในฐานะผู้เขียนโปรแกรมต้องออกความคิดให้รอบคอบก่อนลงมือเขียน คือต้องคิดก่อนว่าโปรแกรมที่เราจะสร้างขึ้นมานั้นจะประกอบด้วยอะไร และจะเอามันไปใช้ทำอะไรบ้าง ซึ่งในขั้นตอนนี้ให้เราจินตาการว่ามัน(เจ้าโปรแกรมที่เราจะสร้างขึ้น)มีลักษณะเหมือนกับวัตถุทั่วๆที่มีอยู่บนโลกนี้ เช่น รูปร่างหน้าตาของมันควรเป็นยังไง มันสามารถทำอะไรได้บ้าง ฯลฯ เมื่อคิดได้แล้วว่าวัตถุ(โปรแกรม)ของเรานั้นน่าจะคุณลักษณะอย่างไร ก็ให้จะนำคุณลักษณะเหล่านั้นมาร่างเป็นคลาส(class) ซึ่งคลาสเนี่ย ก็คือโค้ดที่ใช้อธิบายถึงกลุ่มของวัตถุที่มีคุณสมบัติเหมือนๆกัน โดยมีหน้าที่สำหรับใช้สร้างวัตถุขึ้นออกมาใช้งาน โดยวัตถุที่ถูกสร้างออกมาจากคลาสจะเรียกว่าอ็อบเจกต์(Object)

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

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

แต่ข่าวดีก็คือ เราไม่จำเป็นต้องเสียเวลามานั่งสร้างคลาสขึ้นมาเองเสมอไป ถ้าอ็อบเจกต์ที่เราอยากได้นั้นมีความสามารถและคุณสมบัติเหมือนกับคลาสที่คนอื่นเขาสร้างเอาไว้เรียบร้อยแล้ว เพราะเราสามารถนำคลาสดังกล่าวมาสร้างอ็อบเจกต์แล้วนำอมันไปใช้ทำในสิ่งที่เราต้องการได้เลย ซึ่งบังเอิญเหลือเกินว่ามากกว่า 50% ของเนื้อหาในการเขียนโปรแกรมทั่วไปๆเราสามารถนำคลาสที่มีอยู่แล้วมาใช้สร้างอ็อบเจกต์ได้เลย เช่น เราต้องการใช้อ็อบเจกต์ที่มีความสามารถ”ปริ้น” งานออกมาจาก printer ก็สามารถสร้างอ็อบเจกต์ออกมาจากคลาส Printer(สมมตินะ) ที่มีอยู่แล้วได้เลย(มีอยู่แน่ๆ คลาส Printer เนี่ย) หรือถ้าเราต้องการอ็อบเจกต์ที่มีความสามารถในการควบคุมการทำงานของ Joystick ผ่านทางพอร์ต USB ก็เราก็สามารถหาคลาส Joystick ทีมีคนสร้างไว้แล้วมาใช้ได้เหมือนกัน หรืออีกตัวอย่างคือ อ็อบเจกต์ที่มีความสามารถ convert ไฟล์นามสกุลแบบ csv ให้กลายเป็นไหล์ xls เป็นต้น

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


Class และ Object

เราสามารถสร้างคลาสขึ้นมาด้วยการใช้คีย์เวิร์ด class ตามด้วยชื่อคลาส และปิดท้ายคลาสด้วย end ดังตัวอย่างต่อไปนี้ครับ

cell_phone1.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class CellPhone
def initialize(id, mfg, model)
@id, @mfg, @model = id, mfg, model
end

def call(number)
"Calling to #{number}"
end

def answer
"talking..."
end

def to_s
"number: #@id\nmfg: #@mfg\nmodel: #@model\n"
end
end

เรามาดูรายละเอียดของคลาสนี้ไปพร้อมๆกันนะครับ

คลาสนี้มีชื่อว่าคลาส CellPhone ซึ่งแน่นอนว่าเป็นคลาสของโทรศัพท์มือถือ อ็อบเจกต์ที่จะถูกสร้างออกมาจากคลาสนี้ต้องเป็น “วัตถุ” ที่มีคุณสมบัติเหมือนมือถือแน่นอน ภายในตัวคลาสมีการระบุคุณสมบัติและความสามารถไว้เอาไว้แล้วครับ โดยคลาสนี้กำหนดให้อ็อบเจกต์ของมัน (ก็มือถือนั่นแหละ) มีคุณสมบัติ 3 ประการครับคือ หมายเลขเครื่อง, ยี่ห้อ และ รุ่น ซึ่งโดยทั่วไปแล้วคุณสมบัติของคลาสนั้นจะสามารถแทนได้ด้วยตัวแปร instance ซึ่งในที่นี้ได้แก่ตัวแปร @id, @mfg, @model

ส่วนความสามารถที่ถูกกำหนดเอาไว้ในคลาสนี้คือ ความสามารถในการโทรออก, รับสายและแสดงคุณสมบัติของเครื่องมือถือ ซึ่งจะแทนด้วยเมธอด(method) call, answer และ to_s ครับ จริงๆแล้วเราจะมองว่าเมธอดนั้นคือฟังชั่น(function) ที่ถูกเรียกใช้โดยอ็อบเจกต์ก็ไม่ถึงกับผิดเสียทีเดียวครับ เราจะเห็นว่าการทำงานของเมธอดทั้งสองก็ง่ายๆครับ คือมันจะคืนค่าออกมาเป็นข้อความเมื่อมันถูกเรียกใช้

การเขียนเมธอดทำได้โดยใช้คีย์เวิร์ด def แล้วตามด้วยชื่อของเมธอด จากนั้นปิดท้ายเนื้อหาของโค้ดภายในเมธอดด้วยคีย์เวิร์ด end ครับ สำหรับโค้ดภายในตัวเมธอดนั้น ถ้าไม่มีการกับหนดคีย์เวิร์ด return เอาไว้เพื่อบอกว่าจะมีการคืนค่าอะไรออกมาจากเมธอด ภาษา Ruby จะถือว่าผลลัพธ์ของการแปลโค้ดบรรทัดสุดท้ายในเมธอดคือค่าที่จะคืนออกมาจากเมธอดเองโดยอัตโนมัติ นั่นหมายความว่าทุกๆเมธอดจะต้องมีค่าที่ถูกคืนออกมาทั้งนั้น ถ้าหากค่าที่คืนออกมาจากเมธอดมีค่าเป็น ค่าว่างเปล่า(null) เราจะถือว่าเมธอดนั้นไม่มีการคืนค่าก็ได้ครับ ผมว่าวิธีนี้ก็เข้าท่าดีเพราะไม่ต้องมากำหนด void ให้กับเมธอดเพื่อบอกว่ามันเป็นเมธอดที่ไม่คืนค่าเหมือนกับในภาษา Java หรือ C#

หลังจากเขียนคลาสเสร็จแล้วผมจะนำมันมาสร้างเป็นอ็อบเจกต์โดยให้ชื่อว่า my_phone ดังโค้ดตัวอย่างต่อไปนี้ จากนั้นเราจะลองรันซอร์ดโค้ดของโปรแกรมนี้ดูครับ (ถ้าใช้ SciTE ก็ให้ save ไฟล์ source code เป็นไฟล์ชื่อ cell_phone.rb ก่อนแล้วกด F5 เพื่อรันโปรแกรมได้เลย)

cell_phone2.rb
1
2
3
4
5
6
7
8
9
10
class CellPhone
# ไส้ในเหมือนกับโค้ดตัวอย่างข้างบนครับ
# …
End

my_phone = CellPhone.new("0891134103", "Nokia", "3310")
puts my_phone.to_s
puts my_phone.call(1175)
puts my_phone.answer


ผลลัพธ์ของการรันไฟล์ซอร์ซโค้ด cell_phone2.rb จะแสดงเป็นข้อความออกมาทางหน้าจอดังนี้
number: 0891134103
mfg: Nokia
model: 3310
Calling to 1175
talking...

ผมขอสรุปใจความสำคัญของโค้ด cell_phone2.rb ออกมาเป็นข้อๆดังนี้ครับ
1) การสร้างอ็อบเจกต์ออกมาจากคลาสใดๆทำได้โดยการเขียนชื่อคลาสนั้นๆแล้วตามด้วย “.new” ซึ่งหมายถึงการเรียกเมธอดที่ชื่อ new ซึ่งเป็นเมธอดพิเศษออกมาทำงาน ซึ่งค่าที่คืนออกมาจากเมธอด new ก็คืออ็อบเจกต์ของคลาสนั้นๆ ในที่นี้อ็อบเจกต์ที่ถูกสร้างออกมาจาก คลาส CellPhone จะถูกนำไปเก็บอยู่ในตัวแปร my_phone เพื่อใช้อ้างอิงในโอกาสต่อๆไป
2) ในบรรทัดที่ 6-8 จะแสดงการ “เรียกใช้เมธอด(calling method)”ต่างๆของอ็อบเจกต์ my_phone ออกมาใช้งาน ซึ่งการเรียกใช้เมธอดนั่นทำได้โดยการเขียนเครื่องหมายจุดแล้วตามด้วยชื่อของเมธอดเอาไว้ที่ด้านหลังของอ็อบเจกต์ my_phone จากตัวอย่างนั้นเราเรียกใช้เมธอด call, answer และ to_s ออกมาใช้ทั้งหมด
3) การทำงานของเมธอดทั้งสามนั้นทำเหมือนกันหมดครับ คือคืนค่าที่เป็นข้อความออกมาหลังจากที่เมธอดถูกเรียกใช้ จะต่างกันก็เพียงเนื้อหาของข้อความเท่านั้น ดังนั้นในบรรทัดที่ 6-8 เราจึงสั่งให้แสดงผลข้อความนั้นๆออกมาทางหน้าจอด้วยคำสั่ง puts
4) ตอนที่เราจะสร้างอ็อบเจกต์ด้วยการเรียกเมธอด new นั้นเราสามารถกำหนดคุณลักษณะ(properties) ให้กับอ็อบเจกต์ของเราได้โดยการใส่ค่า arguments เข้าไป ซึ่งจากตัวอย่าง เราใส่ค่าเข้าไปสามค่าคือ เบอร์โทร, ยี่ห้อโทรศัพท์, รุ่นของโทรศัพท์ ค่าเหล่านี้จะถูกส่งไปเป็น input ของเมธอด initialize อีกทีหนึ่ง ดังนั้นเราจะเห็นว่าเมธอด initialize และ เมธอด new นั้นมีความสัมพันธ์กันโดยตรงครับ สรุปว่าเมธอด new เมื่อถูกเรียกจะทำหน้าที่สร้างอ็อบเจกต์ แล้วก็จะทำหน้าที่รับค่าจากภายนอกด้วยถ้าเกิดมีการเขียนรายละเอียดเอาไว้ในเมธอด initialize ของคลาส ซึ่งเมื่อเมธอด new รับค่าเข้ามามันก็จะโยนค่าดังกล่าวไปให้เมธอด initialize ทันทีเพื่อให้เมธอด initialize นำไปประมวลผลต่อไป

โอเคนะครับ ถึงตรงนี้เราก็พอจะร็แล้วว่าคลาส, อ็อบเจกต์, เมธอดและตัวแปรของเรามีความสัมพันธ์กันอยู่ เรานำคลาสมาสร้างอ็อบเจกต์ ซึ่งภายในคลาสนั้นจะมีโค้ดที่เขียนไว้เพื่อกำหนด method และ properties สำหรับอ็อบเจกต์ของคลาสนั้นๆ เมื่อเราสร้างอ็อบเจกต์ออกมาจากคลาสด้วยการ “นิว(.new)” แล้ว เวลาเราจะเอาอ็อบเจกต์นั้นไปใช้เราก็แค่เรียกเมธอดของมันออกมาด้วยการใส่เครื่องหมายจุดแล้วตามด้วยชื่อเมธอดครับ

การสืบทอดคุณสมบัติของคลาส(Class Inheritance)

ทีนี้ผมขอย้อนกลับไปที่ผมเคยบอกว่า เราอาจไม่จำเป็นต้องมานั่งสร้างคลาสขึ้นมาใหม่ตั้งแต่ต้น เพราะอ็อบเจกต์ส่วนใหญ่ที่มีความสามารถแบบเดียวกับที่เราต้องการใช้นั้นมันมีคนไปสร้างคลาสเอาไว้เรียบร้อยแล้ว ซึ่งเราก็สามารถนำคลาสของเขามาใช้ได้เลย ไม่ต้องสร้างคลาสเองตั้งแต่ต้น
แต่มันอาจจะไม่ง่ายอย่างนั้นเสมอไปครับ
เพราะในความเป็นจริงนั้นไม่มีความต้องการของใครที่มันจะเหมือนกันไปหมดทุกอย่างครับ คลาสที่คนอื่นสร้างขึ้นมาอาจมีความสามารถที่เราต้องการอยู่ 8 อย่างแต่กลับขาดความสามารถอื่นไป 2 อย่างก็ได้ แล้วเราจะทำอย่างไร? จะสร้างใหม่ก็ใช่ทีเพราะเสียเวลามาก จะเอาของเขาเดิมๆมาใช้เลยก็ไม่ได้เพราะทำงานอย่างที่ต้องการได้ไม่ครบแน่ๆ แล้วจะทำไงดีอะ
ปัญหานี้มีทางออกครับ เพราะการเขียนโปรแกรมแบบ OOP มีคุณสมบัติที่เรียกว่า การสืบทอดคลาส(Class Inheritance) คุณสมบัตินี้จะทำให้เราสามารถนำคุณสมบัติจากคลาสหนึ่งมาใช้ในอีกคลาสหนึ่งได้ด้วยการถ่ายทอดคุณสมบัติให้แก่กัน

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

เพื่อความเข้าใจมากขึ้น ลองมาดูพิจารณาคลาสโทรศัพท์มือถือของเรากันอีกครั้งครับ
สมมติว่าเราต้องการสร้างอ็อบเจกต์มือถือที่มีความสามารถใช้งานบริการข้อมูลแบบ 3G ด้วยเราจะไม่มาสร้างคลาสมือถือใหม่ตั้งแต่แรก เพราะเรารู้แล้วว่าเราสามารถประหยัดเวลาด้วยการสืบทอดคุณสมบัติได้
ดังนั้นเราก็แค่สร้างคลาสโทรศัพท์มือถือใหม่ขึ้นมา โดยสมมติว่าชื่อคลาส CellPhone3G จากนั้นเราก็จะสืบทอดคุณสมบัติของความเป็นมือถือธรรมดาๆจากคลาส CellPhone โดยคุณสมบัติใหม่ที่เราจะเพิ่มเข้าไปในคลาส CellPhone3G ก็คือความสามารถในการส่ง MMS และความสามารถในการใช้ wifi ซึ่งก็คือการเขียนเมธอด mms และ wifi เพิ่มเข้าไปนั่นเอง

เราจะเรียกคลาส CellPhone ว่าคลาสแม่ (เป็นแม่แบบให้เขามาสืบทอดคุณสมบัติ) และเรียกคลาส CellPhone3G ว่าคลาสลูก (ไปรับถ่ายทอดคุณสมบัติมาจากคลาสแม่)

คลาส CellPhone3G จึงมีหน้าตาดังนี้ครับ

cell_phone_3g.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
class CellPhone3G < CellPhone
def mms
"Send MMS"
end
def wifi
"Connect to Wifi network"
end
end

phone_3g = CellPhone3G.new("0814445555", "Apple", "iPhone")
puts phone_3g.to_s
puts phone_3g.mms
puts phone_3g.wifi

ซึ่งเราจะได้ผลลัพธ์จากการรันโค้ด cell_phone_3g.rb ดังนี้
number: 0814445555
mfg: Apple
model: iPhone
Send MMS
Connect to Wifi network

จากตัวอย่างในโค้ด cell_phone_3g.rb จะเห็นว่าในบรรทัดที่ 1 เราใช้เครื่องหมาย “<” แทนความหมายของการสืบทอดคุณสมบัติของคลาส โดยจะเขียนเครื่องหมาย “<” ไว้หลังคลาสลูกแล้วตามด้วยชื่อของคลาสแม่ที่ไปสืบทอดมา

เมื่อคลาสลูกสืบทอดคุณสมบัติมาจากคลาสแม่เรียบร้อยแล้ว อ็อบเจกต์ที่เกิดจากคลาสลูกนั้นก็จะสามารถเรียกใช้เมธอดหรือ properties ที่อยู่ในคลาสแม่ได้ด้วย ดังนั้นอ็อบเจกต์ cellphone_3g จึงสามารถเรียกใช้เมธอด to_s ได้เพราะมีเมธอดนี้อยู่ในคลาสแม่แล้ว

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

วันพฤหัสบดีที่ 6 สิงหาคม พ.ศ. 2552

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

6) Garbage Collector ใน Ruby
ตัวแปลภาษา Ruby มีฟังก์ชั่นที่เรียกว่า Garbage Collector ซึ่งทำหน้าที่บริหารจัดการหน่วยความจำโดยอัตโนมัติ เพื่อให้โปรแกรมสามารถใช้พื้นที่ในหน่วยความจำได้อย่างมีประสิทธิภาพมากที่สุด ซึ่ง Garbage Collector นั้นก็เป็นฟังก์ชั่นที่ใช้ในภาษา Java และ C# เช่นกัน

7) ไม่ต้องมีตัวจบบรรทัด
เนื่องจากภาษา Ruby แปลโค้ดแบบ interpreter แปลไปทีละบรรทัด เราเลยไม่ต้องใส่ตัวจบบรรทัดให้มัน ภาษา Ruby จะแยกแยะโค้ดแต่ละบรรทัดได้เองจากการขึ้นบรรทัดใหม่
อย่างไรก็ภาษา Ruby ก็ยังอนุญาตให้ใช้เครื่องหมาย ";"(comma) แทนการขึ้นบรรทัดใหม่ได้ในกรณีที่เราต้องการเขียนโค้ดหลายๆชุดไว้ในบรรทัดเดียว ดังตัวอย่างต่อไปนี้ครับ


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


ซึ่งจะมีความหมายเดียวกับการที่เราเขียนแยกบรรทัด ดังตัวอย่างด้านล่างนี้ โดยการรันซอร์ซโค้ดทั้งสองแบบจะได้ผลลัพธ์เหมือนกันคือการพิมพ์ค่าของ sum ออกมาทางหน้าจอ

1
2
3
4
a = 10
b = 15
sum = a + b
puts sum


8) พิมพ์ข้อความออกมาทางหน้าจอ
เรามีวิธีสั่งให้ Ruby พิมพ์ข้อความออกมาทางหน้าจออยู่ 3 วิธีด้วยกันคือ

วิธีผลลัพธ์
ใช้คำสั่ง putsพิมพ์ข้อความออกทางหน้าจอพร้อมกับขึ้นบรรทัดใหม่ให้ด้วย
ใช้คำสั่ง printพิมพ์ข้อความออกทางหน้าจออย่างเดียวโดยไม่ขึ้นบรรทัดใหม่ให้
ใช้คำสั่ง printfพิมพ์ข้อความออกทางหน้าจออย่างเดียวโดยไม่ขึ้นบรรทัดใหม่ให้ และสามารถกำหนดลักษณะของการแสดงผลข้อความได้ด้วย


โค้ดต่อไปนี้จะเป็นตัวอย่างการพิมพ์ข้อความออกทางหน้าจอของทั้งสามรูปแบบครับ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# พิมพ์ข้อความพร้อมกับขึ้นบรรทัดใหม่ด้วย puts
puts "Arsenal"
 
# พิมพ์ข้อความอย่างเดียวไม่ต้องขึ้นบรรทัดใหม่ด้วย print
print "Manchester United"
 
# แต่ถ้าอยากขึ้นบรรทัดใหม่ด้วย print ก็ใส่สตริง "\n" ที่แทนการขึ้นบรรทัดใหม่เข้าไป
print "Chelsea\n"
 
# แสดงค่าของตัวแปรร่วมกับสตริงด้วย printf
frustrated = "Liverpool"
printf "%s : This is Anfield", frustrated
 
# ถ้าจะแสดงค่าของตัวแปรร่วมกับสตริงด้วย puts ก็ใส่ตัวแปรลงไปในเครื่องหมาย "#{ }"
puts "#{frustrated} : This is Anfield"


ซึ่งจะได้ผลลัพธ์ดังนี้
Arsenal
Manchester UnitedChelsea
Liverpool : This is AnfieldLiverpool : This is Anfield

หมายเหตุ: จริงๆแล้ว puts, print และ printf ไม่ใช้ "คำสั่ง" ครับแต่พวกมันเป็นเมธอด !? ซึ่งเมธอดที่เรียกใช้ได้โดยที่เราไม่ต้องสร้างอ็อบเจกต์ขึ้นมาเหล่าจะสังกัดอยู่ในคลาส NilClass ครับ ดังนั้นหากเราจะเรียกมันว่า "คำสั่ง" ก็คงไม่ผิดในแง่ของการนำมาใช้งานนัก

9) Parallel Assignment
ภาษา Ruby สามารถกำหนดค่าให้กับตัวแปรในแบบที่เรียกว่า parallel asignment ได้ครับ คือใช้เครื่องหมายเท่ากับแค่อันเดียวในการกำหนดค่าให้ตัวแปรหลายๆตัว ซึ่งการกำหนดค่าแบบนี้จะพบเห็นได้บ่อย ในภาษา Ruby ครับ

ลองมาดูกันสักหน่อยว่า parrelle asignment นั้นหน้าตาเป็นยังไง
สมมติว่าเราอยากจะกำหนดค่าให้กับตัวแปร 4 ตัว ตามปกติแล้วเราอาจจะทำได้ด้วยการเขียนโค้ด 4 บรรทัดแบบนี้

1
2
3
4
model = "Jazz",
mfg = "Honda"
color = "White"
cost = 640000


แต่ถ้ากำหมดค่าแบบ parallel asignment เราจะใช้แค่บรรทัดเดียว ซึ่งตะได้ผลลัพธ์เหมือนกับโค้ดด้านบน


model, mfg, color, cost = "Jazz", "Honda", "White", 640000


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

วันศุกร์ที่ 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 จะแปลโค้ดทีละบรรทัด โดยเริ่มจากบรรทัดแรกไปจนถึงบรรทัดสุดท้าย

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

ติดตั้ง Ruby

[update: 13 Aug 2014]

สำหรับการลงตัวแปลภาษา Ruby ให้เข้าไปที่หน้า Installation ของ Ruby official website
ในหน้านี้แบ่งการติดตั้ง Ruby ออกตามประเภทของ OS ซึ่งแต่ละ OS มีวิธีการติดตั้งให้เลือกต่างกันไป
ผมเคยติดตั้งแค่บน Debian กับ Windows เท่านั้ันซึ่งก็ไม่ยากเย็นอะไร

ในกรณีของ Debain ง่ายมาก แค่พิมพ์คำสั่งข้างล่างลงบน terminal

$ sudo apt-get install ruby

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

$ sudo apt-get install ruby 1.9.3

สำหรับการติดตั้งบน Windows  ทาง Officail site แนะนำให้ติดตั้งโดยการใช้ RubyInstaller หรือที่เรียกว่า 1-click installer ซึ่งเป็นเพ็กเกจลักษณะเดียวกับการติดตั้งโปรแกรมบน Windows ทั่วไป

ในหน้า option ระหว่างติดตั้ง ผมแนะนำให้ติ๊กถูกทั้งสาม option เลย
อันแรกเป็นการลงเพ็กเกจ Tcl/Tk ในคราวเดียวกันเลย option ที่สองตัว installer จะเพิ่ม Ruby path ลงในตัวแปร PATH ของ Windows ให้เราอัตโนมัติ ส่วน option สุดท้ายเป็นการเพิ่ม file นามสกุล .rb และ .rbw ให้ Windows รู้จัก เมื่อไฟล์ .rb หรือ .rbw ถูกเรียกใช้ก็จะถูกเรียกใช้ด้วย Ruby เวอร์ชั่นที่ติดตั้งนี้



จากนั้น คลิ๊ก Next โลด ...

Verification : ตรวจสอบหลังติดตั้ง
หลังจากลง package ติดตั้ง Ruby เรียบร้อยแล้วเราจะมาตรวจสอบกันมามันใช้งานได้หรือไม่
อย่างแรกเลย ดูว่าเราสามารถเรียก Ruby ขึ้นมาทำงานได้หรือไม่ ซึ่ง check ได้จากกาพิมพ์คำสั่ง ruby -v ที่ DOS prompt
ถ้าผลลัพธ์ที่ได้เป็นข้อความแสดง version ของ Ruby แสดงว่าการติดตั้งของเราไม่มีปัญหาครับ
ถ้าลง Ruby แล้วไม่มีปัญหาก็จะได้ผลลัพธ์ประมาณนี้

C:\>ruby -v
ruby 1.9.3p327 (2012-11-10) [i386-mingw32]


จากนั้นเราจะมาลองรันโปรแกรมภาษา Ruby เพื่อทดสอบตัวแปลภาษา โดยเราจะใช้โปรแกรม SciTE ที่ติดมากับ installation package นี่แหละในการรันโปรแกรม
โปรแกรมที่เราจะทดสอบก็คือโปรแกรม hello บ้านๆทั่วๆไปครับ วอธีการก็ง่ายมาก ให้ เปิดโปรแกรม SciTE แล้วเขียนโค้ดด้านล่างนี้ลงไปแล้ว save ให้เป็นไฟล์ชื่อ hello.rb จากนั้นกดปุ่ม F5 เพื่อรันโปรแกรม ซึ่งจะได้ผลลัพธ์ในหน้า output windows ด้านขวามือครับ


ใช้โปรแกรม SciTE เขียนโค้ด Ruby แล้วรันได้เลย

หรือจะรันโปรแกรมที่เขียนด้วยภาษา Ruby ด้วยคำสั่งใน DOS prompt ก็ย่อมทำได้ครับโดยการใช้คำสั่ง ruby แล้วตามด้วยชื่อโปรแกรม(ไฟล์นามสกุล .rb)


เรียกใช้โปรแกรมที่เขียนด้วยภาษา Ruby ผ่านทาง command line ของ DOS prompt

สำหรับคนที่ทำงานบน Windows แล้วลง Ruby ด้วย One Click Installer package นั้นสามารถเรียกใช้โปรแกรมที่เขียนด้วยภาษา Ruby(ไฟล์ .rb) ด้วยการ double click ที่ไฟล์นั้นได้เลย พูดอีกอย่างก็คือไฟล์นามสกุล .rb นั้นจะทำตัวเป็น executable ไฟล์แบบหนึ่งนั่นเอง

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

มาเขียนโปรแกรมกันเถอะ

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

สมัยนั้นภาษาโปรแกรมที่ทำงานบน Windows หรือ DOS ก็มีอยู่ไม่กี่ตัว อย่าง C, Pascal, Cobal ซึ่งการใช้งานของภาษาของแต่ละภาษาก็ไม่ง่ายเลย แถมแหล่งความรู้ที่จะศึกษานี่ก็จำกัดอย่างยิ่ง เพราะหนังสือเกี่ยวกับ computer programming ภาษาไทยที่มีขายก็น้อย(และอ่านยากมากๆๆๆๆ) internet ก็ยังไม่แพร่หลายเหมือนทุกวันนี้ จะซื้อ text มาอ่านก็แพงไปแถมภาษาอังกฤษเรายังออกทะเลอีก

เล่มนี้ล่ะครับคลาสิกมาก รุ่นปู่อยากผมใช้กันทุกคนครับ(อ่านยากมากเช่นกัน)


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

แต่เนื่องจากผมใช้คอมฯ อยู่ตลอดทั้งเล่นเกม เล่นเน็ต ส่งเมล ใช้ excel ทำงาน(งานนี่อันดับสุดท้ายเลย) จึงได้รับรู้ข้อมูล และเห็นวิวัฒนาการของการใช้ภาษา programming ในบ้านเรามาด้วย ซึ่งผมบอกได้เลยว่าตอนนี้หากจะเริ่มเขียนโปรแกรมซักตัวเพื่อเอามาทำอะไรบางอย่างตามที่เราต้องการนั้น มันไม่ได้ยากเย็นขั้นเทพเหมือนเมื่อก่อนอีกแล้ว เพราะ...

1) มีภาษา computer ให้ศึกษากันเยอะมาก ชอบใจภาษาไหนก็มีให้เลือก แต่ละภาษาก็มีจุดเด่นของตัวเองต่างกันไป
2) แหล่งข้อมูลเพื่อศึกษาการเขียนโปรแกรมนั้นมีมากขึ้นทั้ง internet และหนังสือภาษาไทย วางขายกันเกลื่อน แค่อ่านหนังสือก็เขียนโปรแกรมกันได้แล้วแทบจะไม่ต้องไปลง course เรียนให้เสียสตางค์
3) มีเครื่องมือและตัวช่วยในการเขียนโปรแกรมให้ download ทำให้ลดเวลาในการเขียนโปรแกรมลงได้มาก

แล้วทำไมต้องเขียนโปรแกรม
คำถามนี้เป็นคำถามเดียวกับที่ผมเคยถามตัวเองมาแล้ว ว่าทำไมต้องมานั่งเขียนโปรแกรมเองให้เสียเวลา ใช้โปรแกรมสำเร็จรูปไม่ได้เหรอ ให้คนที่ทำหน้าที่เป็น IT เขียนไม่ได้เหรอ ฯลฯ คำตอบที่ได้ก็มาจากประสบการณ์ตรงในการทำงานจริงของผม บวกกับความเห็นส่วนตัวด้วยนะครับ :)

1) ถ้าเราอยากลดเวลาทำงานพวก report ที่มันซ้ำๆ หรือไม่อยากมานั่งประมวลผลข้อมูลเช่นตัดแปะแล้วคำนวณสูตร ซึ่งพวกนี้เป็นงานซ้ำๆที่ต้องทำอยู่เป็นประจำ การเขียนโปรแกรมแล้วให้มันทำงานที่ว่านี้ให้จะช่วยคุณได้มาก และคุณจะมีเวลาไปคิดงานอย่างอื่นที่มีประโยชน์กว่า
2) บ่อยครั้งที่คุณไม่สามารถใช้โปรแกรมสำเร็จรูปทำงานของคุณให้เสร็ขในครั้งเดียว ครับ คุณอาจต้องการโปรแกรมของคุณเอง
3) ถ้าคุณฝันอยากจะสร้างเกมของคุณเอง การเขียนโปรแกรมก็เป็นหนังในสิ่งที่คุณควรศึกษา
4) ความสามารถในการเขียนโปรแกรมได้จะเพิ่มคุณค่าของคุณในการหางานครั้งต่อไป นั้นหมายถึงคุณมีโอกาสได้รับค่าตอบแทนมากขึ้น
5) การเขียนโปรแกรม มันส์ดี ครับ
สำหรับคุณผู้อ่านแล้วอาจมีเหตุผลอื่นๆอีกมากนอกเหนือจาก 5 ข้อนี้

ก็นั่นหละครับ มีแต่ข้อดีไม่มีข้อเสีย ดังนั้นเรามาเริ่มเขียนโปรแกรมกันดีกว่าครับ !


แล้วจะใช้ภาษาอะไรดี ?
โอวว ถามง่าย แต่ตอบไม่ง่ายนะครับ
จะใช้ภาษาไหนนั้นขึ้นอยู่กับความเหมาะสมและลักษณะของโปรแกรมที่จะสร้างไปใช้งาน ยกตัวอย่างเช่น ผมอยากเขียนโปรแกรมเกมที่สามารถนำไป run บนมือถือได้ อย่างนี้ควรเลือกศึกษา พวก Java เอาไว้ แต่หากจะเขียนเกมเพื่อเล่นบนเครื่องคอนโซลอย่าง Xbox360 ก็ต้องไปทางภาษา ตระกูล .NET XNA เป็นต้น
อย่างไรก็ตามภาษาที่ได้รับความนิยมและใช้งานกันอย่างแพร่หลายใน 'ตลาดแรงงานของเมื่องไทย' สมควรจัดเป็นตัวเลือกอันดับต้นๆของมือใหม่ที่อยากศึกษาการเขียนโปรแกรมครับ เพราะเมื่อเขียนเป็นแล้วส่วนใหญ่สามารถหางานได้ง่ายกว่า(แต่ก็ไม่เสมอไป :P) เมื่อมีคนใช้งานภาษาเหล่านั้นเยอะเราจึงสามารถค้นหาแหล่งความรู้สำหรับภาษานั้นๆได้ง่ายกว่าด้วย ซึ่งภาษายอดนิยมก็ได้แก่ C++, Java, ภาษาตระกูล .NET(VB, ASP, C#), PHP เป็นต้น


แต่........


ภาษาที่ผมอยากแนะนำให้ทุกท่านได้รู้จักผ่านทาง Blog นี้ไม่ได้มีชื่ออยู่ในทำเนียบภาษายอดนิยมของเมืองไทย แต่กลับเป็นที่รู้จักกันมาสักพีกใหญ่แล้วในเมืองนอก อีกทั้งความนิยมของภาษานี้ก็เพิ่มสูงขึ้นอย่างต่อเนื่อง แม้แต่ผมที่เคยสักกัดภาษา Java และ PHP ก็ยังแทใจเปลี่ยนมาใช้ภาษานี้เหมือนกัน ไม่ใช้เพราะตามกระแสหรอกครับ แต่เพราะของเขาดีจริงๆ

ภาษาที่ผมพูดถึงคือภาษา Ruby ครับ


ตอนหน้าเราจะมาดูกันครับว่า Ruby มันมีดียังไง