เนื่องจากส่วน book ของ codenone จะปิดตัวลง คุณ mk จึงแนะนำให้ผมย้ายหนังสือไปที่ blognone library ซึ่งเป็นหมวดหนังสือใครเครือ *none :)
ผมย้ายหนังสือเล่มนี้ไปอยู่ที่หน้า Ruby for fun เรียบร้อยแล้วครับ

ผมเจอ Ruby จากการแนะนำของเพื่อน และจากการดู Video สาธิตการพัฒนาโปรแกรมบน Rails framework บอกตามตรงว่าตอนแรกไม่ได้สนใจ Ruby เลยเพราะมัวแต่ไปสนใจ Rails แต่พอได้ลงมาดูตัว Ruby จริงๆ ผมพบว่ามันเป็นภาษาที่ชัดเจนมากๆ ภาษาอ่านง่าย ทำให้เรียนรู้ได้เร็ว ไม่รู้ว่า เพราะคนคิดค้นภาษานี้เป็นชาวญี่ปุ่น ทำให้ออกแบบภาษา ruby ได้แตกต่างภาษาอื่นก็เป็นได้
ตัวภาษา ruby มีสิ่งที่เราคุ้นเคยอยู่ครบ ทั้ง String, Integer, floating-point, array, boolean รวมไปถึงพวก loop ต่างๆ ก็ไม่ต่างจากภาษาอื่นๆ มากนักทำให้การเรียนรู้ภาษา ruby ยิ่งสนุกขึ้นไปอีก
ผมลองแบ่งส่วนที่จะเขียนคร่าวๆ ตามนี้ menu ด้านล่าง
ruby เป็นภาษาที่ใช้ได้หลายระบบปฏิบัติการ แต่ละระบบมีวิธีการติดตั้งไม่เหมือนกัน ใครชอบตัวไหนก็ใช้ตัวนั้นนะครับ วิธีการติดตั้งดูได้ดังนี้
วิธีติดตั้ง ruby บน linux (ubuntu) ให้ทำดังนี้ครับ
สำหรับคนที่ online ได้
1. ตรวจสอบดูไฟล์ /etc/apt/sources.list แล้วเอา comment ที่สองบรรทัดนี้ออก
deb http://us.archive.ubuntu.com/ubuntu gutsy universe
deb-src http://us.archive.ubuntu.com/ubuntu gutsy universe
คำว่า gutsy เป็นชื่อรุ่นของ ubuntu ถ้าใครมีเก่าหน่อย อาจจะเป็น depper หรือรุ่นใหม่กว่านี้ก็ได้
2. update ระบบให้ทันสมัยโดยคำสั่ง
$ sudo apt-get update
3. ติดตั้ง ruby และ irb ด้วยคำสั่ง
$sudo apt-get install ruby ruby1.8 ruby1.8-dev irb
Rails ยังไม่รองรับ version 1.9 ครับ ป้องกันปัญหาในอนาคต ตอนนี้ลง 1.8 ก่อนดีกว่าครับ
จากนั้นติดตั้ง library พิเศษ สำหรับทำ document และเตรียมสำหรับ rails
$sudo apt-get install rdoc libzlib-ruby libopenssl-ruby
4. ถ้าต้องการใช้ mysql ให้ติดตั้ง mysql ด้วย
$sudo apt-get install mysql-server libmysql-ruby
5. ติดตั้ง gem เพราะต่อไปบน ruby เราจะลง library เสริมด้วยคำสั่ง gem
$wget http://rubyforge.org/frs/download.php/35283/rubygems-1.1.1.tgz
$tar zxvf rubygems-1.1.1.tgz
$cd rubygems-1.1.1
$sudo ruby setup.rb
ถ้าไม่แน่ใจว่า download version ล่าสุด เข้าไปดูได้ที่นี่ครับ http://rubyforge.org/projects/rubygems
6. หลังจากติดตั้ง gem เราควร update gem ด้วย
$sudo gem update --system
แถม
7. ถ้าต้องการลง rails ให้พิมพ์คำสั่งนี้ครับ
$sudo gem install rails --include-dependencies
8. ในกรณีที่ต้องการใช้ Mongrel application server ให้พิมพ์คำสั่งนี้ครับ
$sudo gem install mongrel
ตอนนี้เราพร้อมสำหรับ ruby และ rails แล้วครับ
ถ้าใช้ Leopard ก็ไม่ต้องลง ruby และ rails ครับ
ติดตั้ง ruby หรือ rails บน windows มีทางลัดคือ dowload instance rails
http://instantrails.rubyforge.org
หรือค่อยๆ ทำดังนี้
1. download และ install ruby จากที่
http://rubyforge.org/frs/?group_id=167
2. download gem ตัวล่าสุดจาก
http://rubyforge.org/projects/rubygems/
หรือ download version 1.0.1 ได้ที่
http://rubyforge.org/frs/download.php/35283/rubygems-1.1.1.tgz
untar แล้วเข้า cmd.exe สั่ง run setup.rb ใน folder ที่พึ่ง untar ออกมา
c:\rubygems\>ruby setup.rb
3. สั่ง update gem ให้ทันสมัย
c:\> gem update --system
เป็นอันเรียบร้อย สามารถใช้งาน ruby บน windows ได้เลย
irb หรือ Interactive เป็นโปรแกรมเล็กๆ มักลงคู่กับ ruby เสมอ เหมือนการใช้งาน cmd, terminal, shell หรือ dos ที่เราสั่งงานคอมพิวเตอร์ที่ละคำสั่ง irb ก็คล้ายกัน จะต่างก็ตรงที่เป็นชุดคำสั่งภาษา ruby
user$ irb >> 5+3 => 8 >>
ทดลองเปิด console ขึ้นมาครับ ถ้าบน windows ให้เปิด cmd (Start -> run -> cmd.exe) บน mac ให้เปิด terminal (Application -> utilities -> Terminal.app) ส่วนบน Linux ผมคิดว่าทุกคนคงเปิด terminal ได้อยู่แล้ว
ให้ทดลองพิมพ์ irb ครับ สุดยอดเครื่องคิดเลขอยู่ตรงหน้าคุณแล้ว ;)
เราสามารถทดลองคำสั่ง ruby ได้คำสั่งต่อคำสั่งเลยครับ เช่น
>>say = "I love Ruby" >>puts say >>say['love'] = "*love*" >>puts say.upcase >>5.times { puts say }
หรือทดลองใช้แทนเครื่องคิดเลข
>> a = 3 ** 2 >> b = 4 ** 2 >> Math.sqrt(a+b)
ทดลองสร้าง function
>> def h >> puts "Hello World!" >> end => nil >> h Hello World! => nil >> h() Hello World! => nil
คำสั่ง h หรือการสร้าง function h จะ return ค่าว่าง (nil) การเรียก function ใน ruby ไม่จำเป็นต้องใส่วงเล็บ
การใช้ irb ช่วยให้การทดสอบคำสั่งต่างๆ ของ ruby ทำได้อย่างรวดเร็ว ช่วยลดเวลาในการเรียนรู้ ruby ได้มากทีเดียวครับ
ทดลองเขียนโปรแกรมกัน เริ่มด้วยการสร้างไฟล์ hello.rb โดยมีเนื้อความด้านในดังนี้
# hello.rb puts('hello world')
จากนั้นทดลองเรียกใช้โปรแกรมของเรา ผ่าน ruby
ruby hello.rb
จะได้ผลดังนี้
hello world
คำสั่ง puts จะคล้ายกับคำสั่ง println มันจะทำหน้าที่พิมพ์ string ที่เราส่งให้กับมันออกมา ส่วนสิ่งที่อยู่หลัง "#" ภาษา ruby จะถึอว่าเป็น comment มันจะข้ามไปโดยไม่สนใจ เราสามารถใส่ comment ลักษณะนี้ก็ได้
puts 'hello world' # say hello
โดยปกติ ruby จะดูว่าจบแต่ละคำสั่งจาก "end of line" แต่เราจะใช้ ";" เพื่อแสดงว่าจบคำสั่งก็ได้ ในกรณีที่ต้องการให้ในหนึ่งบรรทัดมีสองคำสั่ง
puts('hello world'); puts('hello '); puts('world')
โดยทั่วไปเราจะไม่ค่อยเห็น ";" ในโปรแกรม ruby เพราะเรามักใช้คำสั่งละ 1 บรรทัด
ในกรณีที่หนึ่งคำสั่งไมได้จบในบรรทัดเดียว parser ของ ruby จะดูสัญลักษณ์สุดท้ายเพื่อวิเคราะห์ว่าจบคำสั่งหรือยัง
x = 10 + 20 + 30
หรือเราสามารถใส่ "\" เพื่อแสดงการขึ้นบรรทัดใหม่ โดยไม่จบคำสั่งก็ได้
x = 10 \ + 20 + 30
อีกสิ่งหนึ่งเราเราควรรู้ก่อนเริ่มเขียน ruby คือความแตกต่างของ
puts 'hello world'
และ
puts "hello world"
ทั้ง single quote และ double quote ต่างก็เป็น string แต่การใช้ 'abc\n' จะนับจำนวนตัวอักษรได้ 5 ตัว ส่วน "abc\n" จะนับได้ 4 ตัว นั่นเพราะ ruby แปล \n ที่อยู่ใน "" ว่าเป็นการขึ้นบรรทัดใหม่ ส่วนการใช้ '' ruby จะมองเป็น '\' และ 'n'
การตั้งชื่อตัวแปรในภาษา ruby คล้ายกับการตั้งชื่อตัวแปลในภาษาอื่นๆ เช่น
สังเกตสองบรรทัดแรก “max_length” และ “maxLength” ในภาษา java ธรรมเนียมปฏิบัติในการตั้งตัวแปลของเค้าคือ “maxLength” แต่ธรรมเนียมปฏิบัติของ ruby เราจะใช้ “max_length” โดยให้เหตุผลว่าการใช้ “_” ทำให้เขียนผิดได้ยากกว่า
ในภาษา ruby “maxlength” ไม่เท่ากับ “maxLength” เพราะมันคำนึงถึงตัวใหญ่ตัวเล็กด้วย (case sensitive) ดังนั้นการเขียน max_length จึงผิดได้ง่ายกว่า แต่โดยส่วนตัวผมชอบ max_langth เพราะมันอ่านง่ายกว่าครับ
สำหรับตัวแปล “___” เป็นตัวแปลที่สามารถตั้งได้ แต่ไม่ควรทำอย่างยิ่งครับ
ที่สำคัญอีกอย่างคือ ruby ไม่ต้องประกาศ type ให้กับตัวแปล เราสามารถกำหนดค่าให้มันได้เลย
first_name = "Apirak" last_name = "Panatook" full_name = fitst_name + ' ' + last_name
เมื่อเรากำหนดค่าให้กับ first_name โปรแกรม ruby จะรู้ทันทีว่า first_name ตัวเป็น object String หากเราเปลี่ยนค่าที่เก็บในตัวแปล ชนิดของตัวแปลจะเปลียนตามทันที ทดสอบได้โดยใช้ method class เช่น
iam = "Number" => Number iam.class => String iam = 28 iam.class => Fixnum
ขอให้สนุกกับการเขียน ruby นะครับ
Object สำหรับเลขจำนวนเต็มใน ruby มีสองตัวคือ Fixnums และ Bignum สำหรับตัวแปลที่มีจุดทศนิยมเช่น 7.5, 3.14.159 หรือ 10.0 จะเป็น Float แม้ว่าใน ruby ตัวแปลสามารถเปลี่ยน type ได้ทันที (Dynamic type)
สำหรับการหารจำนวนเต็มสองจำนวน ผลลัพท์ไม่ได้เป็น Float แต่เป็นจำนวนเต็มและไม่ปัดเศษให้ด้วย
6/3 => 2 7/3 => 2 8/3 => 2 9/3 => 3
ตัวแปล Fixnums มีขนาด 31 bits (ลองคำนวนตัวต่ำสุดสูงสุดดูนะครับ) สำหรับ Bignum เราสามารถใส่ค่าได้ไม่จำกัดครับ (ใช้ได้เต็มที่เท่าหน่วยความจำที่ให้ ruby ครับ)
แม้ว่าการหารจำนวนเต็มสองจำนวนจะไม่กลายเป็น float แต่สามารถเปลี่ยน Fixnum -> Bignum หรือ Bignum -> Fixnum ได้
2 => Fixnum 437 => Fixnum 2**437 => Bignum 1234567890 => Bignum 1234567890/1234567890 => is 0, is Fixnum
ใน ruby ไม่มี a++ หรือ a— ให้นะครับ เราจะใช้ +=, -=, *=, /= แทน
a=4 a += 1 # a is now 5 a -= 2 # a is now 3 a *= 4 # a is now 12 a *= 2 # a is now 6 <blockcode lang="ruby"> a += 1 มีค่าเท่ากับ a = a+1
ตัวอย่างของตัวเลขที่จะเป็น float คือ
3.14158 -2.5 6.0 0.00000000111
ถ้าต้องการให้ผลลัพธ์ของการคูณหรือการหารมีค่าเป็น float เราต้องมีค่าที่เป็น float อยู่ในสมการ
2.5+3.5 # => 6.0 is float 0.5*10 # => 5.0 is float 10*0.5 # => 5.0 is float 8.0/3.0 # => 2.66666666 is float .5/10 # => Syntax error
ภาษา Java, C# หรือ C++ มี int, float, boolean เป็นต้น ที่เป็น primitive data type ไม่ได้เป็น Object ทำให้หลายครั้งถ้าเราต้องการ Object Integer เราต้องแปลง primitive ให้เป็น Object ซะก่อน แต่สำหรับ Ruby ขอให้ programmer มั้นใจว่าทุกอย่างในภาษานี้เป็น Object ทั้งหมด ไม่มี primitive data type ให้สับสนดังนั้นเราจึงเรียกใช้งานตัวเลขได้ตรงๆ เหมือนการเรียกใช้ Object ได้เลย
3.7.round # Give us 4.0 3.7.truncate # Give us 3 -123.abs # Give absolute value, 123 1.succ # Successor, or next number, 2
เราสามารถตรวจสอบได้ว่า ตัวแปลของเราเป็น class อะไร
7.class # Give us Fixnum 88888888888888.class # Give us Bignum 3.14159.class # Give us Pi :P Sorry. It give us Float
ทุก Object ต่างสืบทอดมาจาก class Object และได้ความสามารถพื้นฐานของ Object มาด้วย
'hello'.nil? # false 44.to_s # Return a two-character string '44' 'hello'.to_s # Return 'hello'
Object สามารถบอกได้ว่าตัวเองเป็นค่าว่างหรือไม่ และสามารถ Override method ของแม่ได้เหมือน toString ใน java
ถ้าทุกอย่างใน ruby เป็น Object หมด จะเกิดอะไรขึ้นถ้าบางตัวแปลไม่มี Object จริงๆ
black = nilทดลองหา class ของตัวแปล black
black = nil black.class # => NilClass
แม้แต่ nil ก็เป็น Object ครับ :) แต่น่าเสียดายว่าเราไม่สามารถสร้าง instances ของ NilClass ได้ มันมีได้แค่ instance เดียวคือ nil ครับ
ภาษาruby มีการใช้ boolean เหมือนภาษาอื่นๆ
1 == 1 # true 1 == 2 # false 'russ' == 'smart' # false (1 < 2) #true (4 > 6) # false a = 1 b = 10000 (a > b) # false (4 >=4) # true (1 <=2) # true
true กับ false ก็เหมือนกับ nil ไม่ได้เป็น primitive แต่เป็น instance ของ TrueClass และ flase ก็เป็น instance ของ FalseClass
and และ or ในภาษา ruby สามารถใช้สัญลักษ์แทนได้
(1 == 1) and (2 == 2) #true (1 == 1) and (2 == 3) #false
เราสามารถใช้ && แทนได้
(1 == 1) && (2 == 2) #true (1 == 1) && (2 == 3) #false
ส่วนคำสั่ง or เราสามารถใช้ || แทนได้
(1 == 1) or (2 == 2) #true (1 == 2) || (2 == 3) #false
สำหรับ “not” เราจะใช้ ! แทน
not (1 ==2) #true ! (1 == 1) # false not false #ture
ทุกตัวแปล (Object) สามารถตีความเป็นเป็น boolean ได้ โดยจำง่ายๆ ว่า false และ nil ถือว่าเป็นเท็จ นอกนั้นเป็นจริงหมด
true and 'fred' #true
เพราะว่า ‘fred’ ไม่ใช่ nil จึงถือว่าเป็นจริง
'fred' && 44 # true
เพราะ ‘fred’ และ 44 ไม่ใช่ nil จึงเป็นจริงทั้งคู่
nil || false #false
เพราะ false และ nil เป็นเท็จทั้งคู่
ที่น่าจะจำนอกจากนั้นก็มี
if 0 puts('zero is true!') end
ไม่เหมือน c หรือ c++คำสั่งข้างต้นจะพิมพ์คำว่า ‘zero is true!’ เพราะใน ruby ค่า “0” เป็นจริง
if "" puts('empty string is true!') end
คำสั่งข้างต้นพิมพ์คำว่า ‘empty string is true!’ เพราะ empty string ไม่ใช่ nil ถ้าต้องการตรวจสอบว่า string empty หรือเปล่าให้ใช้
s = "" if s.empty? puts('s is empty!') end
บทที่แล้วเขียนเรื่อง จริง, เท็จ และ ค่าว่าง (true, false, and nil) ไหนๆ เรารู้เรื่อง boolean แล้ว ได้เวลาเอามาใช้
การใช้ if ในกรณีที่มี else
money = 10 if (money >= 10) puts 'You can buy Ray' else puts 'You can't buy Ray' end
ถ้าต้องการมีเงื่อนไขมากกว่า 1 อย่าง สามารถใช้ elsif ได้
if (money < 10) puts 'You can't buy Ray' elsif (money < 20) puts 'You can buy Ray' elsif (money < 30) puts 'You can buy Big Ray' else puts 'You can buy more than one Ray' end
สังเกตุว่า elsif มีแค่ 5 ตัวอักษร ไม่ใช่ elseif และมีความหมายไม่เหมือนกับ else if
หลายคนอาจจะคุ้นเคยกับการใส่วงเล็บหลัง if แต่ใน ruby เราไม่ต้องใส่ก็ได้
if money < 10 puts 'You can't buy Ray' elsif money < 20 puts 'You can buy Ray' elsif money < 30 puts 'You can buy Big Ray" else puts 'You can buy more than one Ray' end
นอกจากใช้ if แบบที่เราคุ้นเคยแล้ว เราสามารถใช้ if ในแบบที่เราคุ้นเคยมากกว่าได้
puts ('Give me more Ray') if money >= 100
เขียนแบบนี้ใกล้เคียงภาษาคนมากขึ้น และเพื่อให้ใกล้ขึ้นไปอีก ใน ruby มีคำสั่ง if not มาให้เราใช้
unless monty >= 10 puts 'Don't give him a Ray' end
unless จะทำงานเมื่อเงื่อนไขเป็น false และแน่นอนว่าเราเขียนแบบที่เราคุ้นเคยได้
puts ('Don't give him a Ray') unless money < 100
ในภาษา ruby มี loop ให้ใช้สองแบบครับ
แบบแรกคือ loop มาตฐาน while และ for
i = 0 while i < 4 puts("i = #{i}") i = i + 1 end
เมื่อเป็น “จริง” ถึงทำ ผลลัพธ์ที่ได้คือ
i = 0 i = 1 i = 2 i = 3
สำหรับ while เราสามารถเปลียนคำว่า while เป็นคำว่า until ได้ในกรณีที่ต้องการให้เป็น “เท็จ” ถึงทำ
i = 0 until i >= 4 puts("i = #{i}") i = i + 1 end
ผลลัพธ์ที่ได้เหมือนกันครับ
สำหรับ loop for เราไม่ค่อยได้ใช้เท่าไหร จะข้ามไปครับ :p
แบบที่สองคือการ loop โดยดึงค่ามาจาก Array โดยใช้คำสั่ง each
array = ['first', 'second', 'third'] array.each do |x| puts(x) end
ผลลัพธ์ที่ได้คือ
first second third
การใช้ each จะคล้ายกับการใช้ iterator ใน java
ArrayList list = new ArrayList(); list.add("first"); list.add("second"); list.add("third"); for( Iterator i = list.iterator(); i.hasNext();) { System.out.println(i.next()); }
สำหรับคนที่อยากได้ for i = 1 to 10 { } ใน ruby เราใช้
(1..10).each do |i| puts i end
คำสั่งมาตรฐานสำหรับ Loop อย่าง break และ next
ตัวอย่าง break
name = ['george', 'mike', 'gary', 'diana'] name.each do |name| if name == 'gary' puts('Break!') break end puts(name) end
ผลที่ได้จะหยุด loop ที่ gary
george mike Break!
ตัวอย่าง next
name = ['george', 'mike', 'gary', 'diana'] name.each do |name| if name = 'gary' puts('Next!') next end puts(name) end
ผลที่ได้จะข้าม gary ไป
george mike Next! diana
string ใน ruby สามารถเขียนได้ทั้ง ” และ “” ดูความแต่กต่าง
first = 'Mary had' second = ' a little lamb'
เราสามารถต่อ string ได้ด้วย +
poem = first + second
ค่าของ poem จะเท่ากับ
Mary had a little lamb
นอกจากนี้คำสั่งที่ใช้บ่อยๆ ของ string เช่น
คำสั่ง length เพื่อวัดความยาวของ string
first.length #=> 8
คำสั่ง upcase, downcase
poem.upcase #=> MARY HAD A LITTLE LAMB
porm.downcase #=> mary had a little lamb
คำสั่ง gsub และ split
first.gsub(/ry/,'re') #=> mare had
first.split(" ") #=> ["mare", "had"]
first.split("( )") #=> ["mare", " ", "had"]
คำสั่ง <=> เพื่อเปรียบเทียบ string
first <=> second #=> false
เรามอง string คล้ายกับ array เราสามารถเปลี่ยนตัวอักษรใน string ได้แบบนี้
poem[0] = 'G' puts(poem) #=> Gary had a little lamb puts(poem[1]) #=> 97, the code for 'a'
ในการสร้าง String หลายบรรทัด การใช้ ” และ “” อาจดูไม่ดีนัก เราสามารถใช้ %Q{ } แทนได้
multiline_string = %Q{ first line second line }
แต่เดิม String ในภาษา C, C++ เป็น mutable พอมาใน Java และ C# กลายเป็น immutable พอมาถึง Ruby มันกลับมาเป็น mutable อีกครั้ง ถ้าจะเถียงกันเรื่อง mutable ดีหรือ immutable ดีกว่าคงเถียงกันไม่จบ ใน Ruby เลยมี String เป็น mutable และ Symbols เป็น immutable
เวลาประกาศ Symbol เราจะขึ้นต้นด้วย colon
:a_symbol :an_other_symbol :first_name
ชาว Ruby มักใช้มันตอนที่ต้องการแสดงความเป็นตัวชี้ (identifiers) เพราะแน่ใจว่า string ตัวนี้มีอยู่ใน memory แค่ที่เดียว ตัวอย่างการใช้งานเช่น
Food.eat(:all, :by => "hand", :on => "table")
หรือใช้ในคำสั่ง find ของ rails
Employee.find(:all, :conditions => 'name LIKE "Paul%"')
คุณ pphetra อธิบายเกี่ยวกับ Symbol ว่า
Symbol ถือเป็น object พิเศษแบบหนึ่ง นั่นคือ มันจะมีแค่ตัวเดียวเสมอ (ถ้าชื่อเหมือนกัน)
สมมติเราอ้างถึง :a ไป 10 ครั้งใน program
ก็จะมี object symbol :a เกิดขึ้นใน memory แค่ตัวเดียว
แต่ถ้าเราใช้ string ‘a’ 10 ครั้งใน program
ก็จะมี object string ‘a’ เกิดขึ้น 10 ตัวใน memory
ตัวอย่างการใช้งาน array
x = [] #=> สร้าง arry ว่างๆ y = Array.new #=> สร้าง array ว่างๆ อีกอัน a = ['neo', 'trinity', 'tank'] #=> สร้าง array ขนาดเท่ากับ 3
Array เริ่มต้นที่ 0
a[0] #=> neo a[1] #=> trinity
ดูขนาดของ Array จากคำสั่ง length และ size (คือคำสั่งเดียวกัน)
puts(a.length) #=> 3 puts(a.size) #=> 3
เราสามารถเพิ่มข้อมูลใน array ได้ตลอด
a[3] = 'morphieus'
ถ้าเติมค่าให้ array เลยตำแหน่งสุดท้าย ค่าของ size จะขยายออกไปทันที
a[6] = 'keymaker' a.size #=> 7
ได้ค่าของ array a คือ
a = ['neo', 'trinity', 'tank', 'morphieus', nil, nil, 'keymaker']
อีกวิธีในการเพิ่มค่าให้ array คือใช้เครื่องหมาย <<
a << ‘mouse’
ค่า mouse จะไปต่อท้าย a ไม่ว่าตัวแปลสุดท้ายของ a จะเป็น nil ก็ตาม
array ใน ruby มีคำสั่ง sort และ reverse ในตัวเอง
a = [77, 10,120, 3] a.sort # return => [3, 10, 77, 120] a = [1, 2, 3] a.reverse # return => [3, 2, 1]
สำคัญว่าคำสั่งทั้งสองไม่ได้ sort หรือ reverse ค่าใน array a เลย ทำแค่ return array ใหม่เท่านั้น ถ้าต้องการให้ a เปลี่ยนค่าไปจริงๆ ต้องเรียก method ชื่อ sort! และ reverse!
a = [77, 10,120, 3] a.sort! # a => [3, 10, 77, 120] a = [1, 2, 3] a.reverse! # a => [3, 2, 1]
เครื่องหมาย “!” เป็นที่รู้กันในคนเขียน ruby ว่าหมายถึงการเตือนว่า method นั้นจะเปลี่ยนค่า object นั้นไปด้วย เหมือนเครื่องหมาย “?” ที่หมายถึงการถามเพื่อให้ method นั้นตอบเป็น true/false
(คนเขียน java คงคุ้นกับ “is” เราใช้เหมือน “?” ใน ruby ครับ)
Hashes เป็นการเก็บข้อมูลคล้ายกับ Array ต่างกันที่ Hashes จะใช้ key word ในการอ้างอิงค่า แทนที่จะนับตามลำดับ
family = { :dad => "Sumon", :mom => "Viraporn", :sister => "Sasivimon"}
เราสามารถใช้ :dad หรือ “dad” ก็ได้ เพราะเป็น string เหมือนกัน แต่ที่เรานิยมใช้ :dad เพื่อแสดงความเป็น keyword ให้กับคนที่มาอ่าน code ของเรา (ดูความแตกต่างของ : และ “” ได้ในบท string)
วิธีการเรียกใช้ข้อมูลใน hashes
puts family[:dad] #=> "Sumon"
แสดงข้อมูลที่อยู่ใน hashes
family.keys #=> [:dad, :mom, :sister] family.value #=> ["Sumon", "Viraporn", "Sasivimon"]
เราสามารถเพิ่มข้อมูลลงใน hashes ได้เรื่อยๆ
family[:dog] = "Bobby" family.keys #=> [:dad, :mom, :sister, :dog]
ผมพบว่าการส่งค่าให้กับ function ด้วย hashes เป็นวิธีที่มีปรสิทธิภาพมาก ลองเทียบแบบนี้
แบบแรกไม่ใช้ hashes
def love(hunter , victim) puts "#{hunter} love #{victim}" end # มี code กั้น 2000 บรรทัด หรืออยู่คนละไฟล์ love("Somsri","Somchai")
ปัญหาเกิดขึ้นตอนที่เรากลับมาอ่านโปรแกรมของเราแล้วไม่รู้ว่าจริงๆ ตัวแปลที่เราส่งไปอะไรมาก่อนมาหลัง ลองเทียบกับ
def love(option) puts "#option[:hunter] love #option[:victim]" end ... 2000 line of code ... love(:hunter =>"Somsri", :victim =>"Somchai")
แบบนี้เราจะอ่านรู้เรื่องว่าส่งตัวแปลอะไรไป
อย่างหนึ่งที่ควรรู้คือใน ruby ถ้าตัวแปลตัวสุดท้ายเป็น hashes เราไม่ต้องใส่ { } ดังนั้น การเรียนคำสั่ง love เราสามารถเรียกแบบนี้ได้
love :hunter => "Somsri", :victim => "Somchai"
หรือ
has_many :people, :class_name => "Person", :conditions => "deleted = 0", :order => "name"
เป็นต้น
ruby มีเครื่องหมาย =~ เอาไว้เทียบ regular expression กับ String
/old/ =~ 'this old house' # return 5
เป็นตำแหน่งของ old ใน ‘this old house’
/Russ|Russell/ =~ 'Fred' # return nil
เพราะใน Fred ไม่มี Russ หรือ Russell
/.*/ =~ 'any old string' # return 0
เพราะ .* หมายถึง string ใดๆ
เราสามารถเอา =~ ไปใช้ในประโยคเงื่อนไข
if /Russ|Russell/ =~ s puts "Russ or Russell" end
หรือใช้เพื่อกำหนดค่าให้ตัวแปล
x = /old/ =~ 'this old house'สำหรับการใช้สุดยอดเครื่องมืออย่าง regular expression ลองอ่านใน wiki pedia หรือลองเล่มนี้ครับ Mastering Regular Experession
Class เป็นเหมือนแบบแปลนของ Object มาลองสร้าง Class แรกกันดู
Class BankAccount def initialize(account_owner) @owner = account_owner @balance = 0 end def deposit(amount) @balance = @balance + amount end def withdraw(amount) @balance = @balance - amount end end
เราลองไล่โปรแกรมที่ละชุดนะครับ
Class BankAccount ... end
เป็นการกำหนดชื่อของ Class เพื่อเรียกใช้ในภายหลัง เรานิยมสร้างชื่อของ Class ให้ตัวหน้าเป็นตัวใหญ่และคำที่ตามมาไม่มี “_” แต่ใช้ตัวอักษรใหญ่เพื่อแยกระหว่างคำ ไม่เหมือนการตั้งชื่อตัวแปร
...
def initialize(account_owner)
@owner = account_owner
@balance = 0
end
...ใน ruby เราสร้าง method ด้วยคำสั่ง def ตามด้วยชื่อของ method นั้นๆ ตามด้วย argument ที่ต้องการส่งให้กับ method นั้นๆ สิ่งที่พิเศษสำหรับ method นี้คือ initialize ซึ่งทำให้ method นี้เป็น method แรกที่ถูกเรียกใช้เมื่อเราแปลง Class เป็น object
@owner = account_owner
ตัวแปรที่ขึ้นต้นด้วย @ จะหมายถึง instance variable ซึ่งทำเราสามารถเรียกใช้ตัวแปรที่ชื่อ owner ได้จากที่ใดก็ได้ใน class ในบรรทัดนี้เรากำหนดให้ owner มีค่าเท่ากับ account_owner ซึ่งเป็นตัวแปรที่มีคนส่งมาให้ผ่าน argument ของ method
เมื่อเรานำ @owner และ @balance มาไว้ที่ initialize จะเป็นเหมือนเรากำหนดค่าเริ่มต้นให้กับตัวแปรทั้งสองตัวนี้ หากเราไม่เขียน initialize ภาษา ruby จะสร้าง Object โดยมองว่า method initialize ไม่มีอะไรอยู่ข้างใน
การสร้าง Object จาก class เราสามารถทำได้ดังนี้
my_account = BankAccount.new('Russ')
เราจะได้ Object BankAccont ที่มีตัวแปร @owner เท่ากับ ‘Russ’ และ @balance มีค่าเริ่มต้นเท่ากับ 0
...
def deposit(amount)
@balance = @balance + amount
end
def withdraw(amount)
@balance = @balance - amount
end
...deposit และ withdraw ใช้สำหรับการกำหนดค่าให้กับ @balance จากนั้น method จะ return ค่าสุดท้้ายให้กับคนที่เรียกมัน จะเห็นว่าสิ่งสุดท้ายที่ method นี้ทำคือกำหนดค่าให้ @balance ดังนั้นสิ่งที่ return คือ @balance
my_balance = my_account.deposit(100,000)
ค่าของ my_balance จะเท่ากับ 100,000 แต่ถ้าเราต้องการค่าของ balance โดยไม่ต้อง deposit หรือ withdraw ล่ะ
หลังจากที่เราได้ Class BankAccount จากบทที่แล้ว เรามาลองเรียก balance ออกมาจาก method Bank account ดู
my_account = BankAccount.new('Russ') puts(my_account.balance)
เราจะเจอ error ว่า
account.rb:8: undefined method 'balance' ... (NoMethodError)
แปลว่าแทนที่มันจะไปหาตัวแปล balance มันกลับไปตามหา method balance แทน … ไม่ต้องทดลอง my_account.@balance นะครับ ตัวแปลใน rails ไม่สามารถเรียกใช้โดยตรงได้จากนอก object
แล้วถ้าต้องการเรียกใช้ตัวแปลจากนอก object สิ่งที่ต้องทำคือกำหนด accessor method ให้กับตัวแปล
def balance @balance end
สิ่งสุดท้ายที่ method balance ทำคือค่า @balance ดังนั้นสิ่งที่ return ออกมาคือ @balance หากทดลองเรียบกใช้แบบเดิม
my_account = BankAccount.new('Russ') puts(my_account.balance)
Ruby จะแสดงค่า @balance ออกมาอย่างถูกต้อง … แล้วถ้าต้องการกำหนดค่าให้กับ @balance ล่ะ?
def set_balance(new_balance) @balance = new_balance end
set_balance สามารถทำงานได้ดีเยี่ยมเหมือน method balance
my_account.set_balance(100)
ปัญหาคือมันไม่สวยและดูไม่เป็นธรรมชาติ มันจะดีกว่านี้ถ้าเป็น
my_account.balance = 100
ทำอย่างไรเราถึงจะสามารถสร้าง method ให้เป็นธรรมชาติและ ได้คุณสมบัติ encapsulation ไปพร้อมๆ กัน
def balance=(new_balance) @balance = new_balance end
เปลี่ยนชื่อ method โดยใส่ “=” ลงไป
my_account.balance=(100)
หรือ
my_account.balance= 100
หรือ
my_account.balance = 100
ถ้าไม่ต้องการจ้างคนมาเขียน set, get จำนวนมาก Ruby มีคำสั่ง attr_accessor ซึ่งจะช่วยสร้าง set และ get ในแบบที่เราพูดถึงข้างต้น
attr_accessor :balance
วิธีนี้ช่วยลด method ได้หลายบรรทัดทีเดียว หรือจะใช้ทีละหลายๆ ตัวแปลก็ได้
attr_accessor :balance, :grace, :agility
และในกรณีที่ต้องการให้อ่านอย่างเดียวให้ใช้ attr_reader
attr_reader :name
ทีนี้ลองเดาว่า attr_writer ใช้ทำอะไร
Ruby ทำได้แค่ single inheritance ทุก class จะมีแม่เพียง class เดียว ถ้าไม่มีการระบุทุก class จะเป็นลูกของ class Object
วิธีการกำหนด superclass
class InterestBearingAccount < BankAccount def initialize(owner, rate) @owner = owner @balance = 0 @rate = rate end def deposit_interest @balance += @rate * @balance end end
InterestBearingAccount เป็น class ลูกของ BankAccount ดังนั้นจึงได้คุณสมบัติ (method) ทั้งหมดของ BankAccount มาใช้
ถ้าเราต้องการยกเรื่องการกำหนด @balance และ @owner ให้กับ class แม่ เราสามารถกำหนดได้ดังนี้
def initialize(owner, rate) super(owner) @rate = rate end
คำสั่ง super จะ ค้นหา method ที่มีชื่อเหมือนกันใน class แม่ แล้วเรียก method นั้น ในตัวอย่างคำสั่ง super(owner) จะค้นหา method ที่ชื่อ initialize ใน class แม่ พร้อมเรียกและส่งตัวแปร owner ไปให้ ในกรณีที่ไม่เจอ ruby จะดูต่อไปที่ class แม่ของแม่ต่อๆ ไป ถ้าไม่เจอจึงแสดง error ออกมา
สิ่งนี้ต่างจากภาษาอื่นๆ เราต้องระวังไว้ว่า class ลูกจะไม่เคยเรียก initialize ของ class แม่เอง ถ้าเราไม่เรียกให้
ตัวอย่างการใช้ Argument ใน ruby
def create_car( model, convertible=false) ... end
เราสามารถเรียกใช้ create_car โดยส่งตัวแปรให้เพียงตัวเดียวหรือสองตัวก็ได้
create_car('sedan') create_car('sports car', true) create_car('minivan', false)
กรณีที่ method ถูกเรียกโดยส่งค่าให้เพียงตัวแปรเดียว method จะกำหนดค่า false ให้ convertible โดยอัตโนมัติครับ
argument ที่มีค่ากำหนดไว้แล้วต้องเรียงอยู่หลังสุดนะครับ
method ของ ruby ทำให้เรากำหนด argument ใด้ flexible มากๆ เช่น
def add_students(*names) for student in names puts("adding student #{student}") end end add_students( "Fred Smith", "Bob Tanner")
เมื่อเราใส่ * ไว้ท้ายสุด ruby จะแปลงค่าที่อยู่ต่อท้ายเป็น array อย่างในตัวอย่าง ruby จะทำให้
names = ["Fred Smith", "Bob Tanner"]
ลองดูอีกตัวอย่างที่เราใสทั้ง regular arguments และ arguments array ไว้ด้วยกัน
def describe_hero(name, *super_powers) puts("Name: #{name}") for power in super_powers puts("Super power: #{student}") end end
ตัวอย่างการเรียกใช้ method hero
describe_hero("Batman") describe_hery("Flash", "Speed") describe_hero("Supreman", "can fly", "x-ray vision", "invulnerable")
ภาษาส่วนใหญ่ มักจัดการกับปัญหาที่คาดไม่ถึง ด้วย exception เมื่อโปรแกรมมีปัญหาแทนที่จะออกจากโปรแกรมไปเลย ถ้านักพัฒนาเขียน exception ไว้โปรแกรมจะก็จะมาเรียก exception แทนที่จะออกจากโปรแกรมไป
ruby ไม่มี exception :p
เราจะใช้ begin/rescue ในการ catch exceptions แทน
begin quotient = 1/0 #Boom! rescue puts('Something bad happen') end
ในกรณีข้างต้น แทนที่โปรแกรมจะแสดง error
ZeroDivisionError: divided by 0 from (irb):1:in `/' from (irb):1
โปรแกรมจะแสดงคำว่า “Something bad happen”
ถ้าเราต้องการแสดงผลให้ตรงกับปัญหาที่เกิดขึ้นเราสามารถ กำหนดได้ว่าปัญหาแบบไหนที่ให้ rescue
begin quotient = 1/0 #Boom! rescue ZeroDivisionError puts('You tried to divide by zero') end
ในตัวอย่างข้างต้นถ้าปัญหาที่เกิดไม่ได้เป็นการหารด้วยศูนย์ โปรแกรมจะแสดง error ตามปกติโดยไม่เข้า rescue
ถ้าเรารู้อยู่แล้วว่า method ของเรามีโอกาศ error แทนที่จะรอให้เกิด error ก่อน เราสามารถตรวจสอบแล้วส่งไป rescue ได้เลย
if denominator == 0 raise ZeroDivisionError end return numerator/denominator
ในกรณีที่เราต้องการสร้าง exception ของตนเอง ruby มีทางลัดให้
>>> raise 'You did it wrong' RuntimeError: You did it wrong
ruby จะสร้าง Object RuntimeException ขึ้นมาแล้วใช้ ‘You did it wrong’ เป็นข้อความแนบไปกับ exception ที่เกิดขึ้น ในกรณีนี้เราไม่ต้องใช้ rescue เลย