ตัวแปรและขอบเขตของตัวแปร

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

  • Ruby local variables
  • Blocks and methods
  • Ruby global variables
  • Ruby instance variables
  • Ruby class variables
  • Ruby predefined constant variables
  • Ruby predefined global variables

Ruby local variables

ตัวแปร local เป็นตัวแปรที่สามารถใช้ได้ในขอบเขตที่ถูกสร้างขึ้น หรือเรียกว่า local scope ในภาษา Ruby นั้นขอบเขตใหม่จะถูกสร้างขึ้นเมื่อสร้างเมธอดหรือบล็อค นั่นหมายความว่า ตัวแปรที่ประกาศภายในเมธอดจะสามารถใช้ได้ในเมธอดนั้นเท่านั้น นอกจากนี้ Ruby ยังมีส่วนของโปรแกรมหลัก (Main execution area) หรือ Ruby toplevel ซึ่งเป็นขอบเขตสูงสุดของโปรแกรม มาดูตัวอย่าง

local_variable1.rb
#  Main execution area
x = 10

def method1
    x = 20
    puts "x in method1 #{x}"
end

puts "x in main #{x}"
method1

ในตัวอย่าง แสดงให้เห็นถึงขอบเขต local สองขอบเขตที่ถูกสร้างขึ้น ส่วนแรกอยู่ในส่วนของโปรแกรมหลัก เราได้ประกาศตัวแปร x และกำหนดค่าเป็น 10 และขอบเขตที่สองอยู่ภายในเมธอด method1 ที่มีการประกาศตัวแปร x เช่นเดียวกัน แต่มีค่าเป็น 20 และสิ่งที่เกิดขึ้นคือตัวแปรทั้งสองนั้นอยู่คนละขอบเขตกัน และนั่นหมายความว่ามันเป็นคนละตัวแปรกัน

x in main 10
x in method1 20

และนี่เป็นผลลัพธ์จากการรันไฟล์โปรแกรม local_variable1.rb จะเห็นว่าค่าของตัวแปรทั้งสองจะเป็นคนละค่ากัน มาดูตัวอย่างถัดไป

local_variable2.rb
# Main execution area
x = 10

def method1
    puts "x in method1 #{x}" # Error
end

puts "x in main #{x}"
method1

ในตัวอย่างนี้ เราได้ประกาศตัวแปร x ในส่วนหลักของโปรแกรมเช่นเดิม แต่ไม่มีการประกในเมธอด method1 แล้ว และเนื่องจากภายในเมธอดเราได้เข้าถึงตัวแปร x ที่ไม่ได้ประกาศในเมธอด โปรแกรมจะเกิดข้อผิดพลาดขึ้นเมื่อเรารัน

x in main 10
Traceback (most recent call last):
        1: from local_variable2.rb:9:in `<main>'
local_variable2.rb:5:in `method1': undefined local variable or method `x' for main:Object (NameError)

และนี่เป็นผลลัพธ์ของการรันโปรแกรม local_variable2.rb ซึ่งจะเกิดข้อผิดพลาดโดยแจ้งว่าตัวแปร x ไม่ได้ถูกประกาศในเมธอด

ตัวอย่างต่อไป มาดูตัวอย่างของขอบเขตตัวแปรที่ซับซ้อนขึ้นเล็กน้อย

a = 1
b = 2

def method1
    c = 3
    d = 4

    puts "Local variables in method1"
    puts local_variables.inspect

    def method2
        e = -8
        f = -9

        puts "Local variables in method2"
        puts local_variables.inspect
    end

    method2

end

def method3
    a = 10
    b = 20

    puts "Local variables in method3"
    puts local_variables.inspect
end

puts "Local variables in main"
puts local_variables.inspect

method1
method3

ในตัวอย่าง ในส่วนของโปรแกรมหลัก เราได้ประกาศตัวแปรสองตัวคือ a และ b และสองเมธอดคือ method1 และเมธอด method3 โดยแต่ละเมธอดนั้นมีการประกาศตัวแปร local เป็นของตัวเอง

def method1
    c = 3
    d = 4

    puts "Local variables in method1"
    puts local_variables.inspect

    def method2
        e = -8
        f = -9

        puts "Local variables in method2"
        puts local_variables.inspect
    end

    method2

end

ในเมธอด method1 นอกจากจะมีการประกาศตัวแปรแล้ว ยังมีการประกาศเมธอดอยุ่ข้างในอีกด้วย เมธอด method2 นั้นเป็นเมธอดที่มีขอบเขตภายในเมธอด method1 ซึ่งในภาษา Ruby นั้น คุณเมธอดสามารถที่จะประกาศซ้อนกันได้ แต่ในการที่จะเรียกใช้งานเมธอด method2 คุณต้องเรียกในขอบเขตที่มันถูกประกาศขึ้นมาเท่านั้น นั่นก็คือภายในเมธอด method1 นั่นเอง

puts local_variables.inspect

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

Local variables in main
[:a, :b]
Local variables in method1
[:c, :d]
Local variables in method2
[:e, :f]
Local variables in method3
[:a, :b]

นี่เป็นผลลัพธ์ของการรันโปรแกรม จะเห็นว่าตัวแปรที่เราได้ประกาศนั้นจะอยู่ในขอบเขตที่มันถูกสร้างเท่านั้น

Blocks and methods

บล็อค (Blocks) นเป็นไวยากรณ์ทางภาษาที่ใช้สำหรับรันส่วนของโปรแกรมในขอบเขตของมันเอง ในภาษา Ruby คุณสามารถสร้างบล็อคด้วยเครื่องหมาย {...} หรือคำสั่ง do และ end อย่างไรก็ตาม บล็อคนั้นไม่สามารถสร้างขึ้นมาอย่างโดดเดี่ยวได้ แต่มันจะถูกสร้างเมื่อต้องการส่งเป็นพารามิเตอร์ของเมธอด หรือใช้ร่วมกับคำสั่งในภาษา Ruby

มาดูตัวอย่างการสร้างบล็อคในภาษา Ruby

blocks.rb
BEGIN {
    puts 'Program started'
}

END {
    puts 'Program stopped'
}

[1, 2, 3].each do |x|
    puts x
end

ในตัวอย่าง เราได้สร้างบล็อคมาสามบล็อค โดยสองบล็อคแรกนั้นใช้เครื่องหมาย {...} ในการสร้าง ส่วนบล็อคสุดท้ายใช้คำสั่ง do ... end ในการสร้าง ซึ่งทั้งสองวิธีนี้ได้ผลลัพธ์ที่เหมือนกัน

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

และสุดท้ายใน main โปรแกรม เราใช้เมธอด each ของอาเรย์ โดยเมธอดนี้จะส่งแต่ละค่าในอาเรย์เข้าไปรันในบล็อคผ่านทางตัวแปร x ซึ่งเป็นพารามิเตอร์ของบล็อค โดยที่เครื่องหมาย | (pip) นั้นใช้สำหรับกำหนดพารามิเตอร์ในบล็อค

Program started
1
2
3
Program stopped

นี่เป็นผลลัพธ์การทำงานของโปรแกรม สังเกตุว่าลำดับการรันของบล็อคนั้นไม่ได้รันตามลำดับที่มันประกาศ อันเนื่องมาจากการงานของคำสั่ง BEGIN และ END นั่นเอง

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

local_scope_block1.rb
x = 10
y = 20

4.times { |x|
    puts "x inside the block: #{x}"
    puts "y inside the block: #{y}"
}

puts "x outside the block: #{x}" 

ในตัวอย่าง เราได้ประกาศตัวแปร x และ y ที่ส่วนหลักของโปรแกรม หลังจากนั้นเราสร้างบล็อคที่มีการประกาศตัวแปร x ด้วย นั่นทำให้เมื่อคุณเข้าถึงและแก้ไขตัวแปร x ภายในบล็อค นั่นจะส่งผลกับค่าของตัวแปรภายในบล็อคเท่านั้น

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

x inside the block: 0
y inside the block: 20
x inside the block: 1
y inside the block: 20
x inside the block: 2
y inside the block: 20
x inside the block: 3
y inside the block: 20
x outside the block: 10

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม ค่าของตัวแปร x นั้นจะเป็นค่าในขอบเขตที่มันถูกสร้างขึ้น ในขณะที่ตัวแปร y นั้นในบล็อคจะใช้ค่าจากส่วนของโปรแกรมหลัก

Ruby global variables

ตัวแปร global เป็นตัวแปรที่สามารถเข้าถึงได้จากทุกส่วนของโปรแกรมหลังจากที่มันถูกประกาศ ซึ่งจะตรงกันข้ามกับตัวแปร local ที่จะสามารถเข้าถึงได้ภายในขอบเขตที่มันถูกสร้างขึ้นเท่านั้น ในภาษา Ruby ตัวแปร global จะขึ้นต้นด้วยเครื่องหมาย $

มาดูตัวอย่างการประกาศและใช้งานตัวแปร global ในภาษา Ruby

global_variable1.rb
$gb = 1

module M
    puts "Inside module #{$gb}"
end

class O
    def initialize
        puts "Inside class's method #{$gb}"
    end
end

def hello
    puts "Inside module #{$gb}"
end

puts "In main #{$gb}"
hello
ob = O.new

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

Inside module 1
In main 1
Inside module 1
Inside class's method 1

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

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

global_variable2.rb
$expressions = []
$last_result = nil

def add(a, b)
    result = a + b
    exp = "#{a} + #{b} = #{result}"
    $expressions.push(exp)
    $last_result = result
    return result
end

def sub(a, b)
    result = a - b
    exp = "#{a} - #{b} = #{result}"
    $expressions.push(exp)
    $last_result = result
    return result
end

def multiply(a, b)
    result = a * b
    exp = "#{a} * #{b} = #{result}"
    $expressions.push(exp)
    $last_result = result
    return result
end

def divide(a, b)
    result = a / b
    exp = "#{a} / #{b} = #{result}"
    $expressions.push(exp)
    $last_result = result
    return result
end

puts add(1, 2)
puts sub(3, 5)
puts divide(8, 7)
puts multiply(4, 9)
puts 'Performed expressions:'
p $expressions
puts "Last result: #{$last_result}"

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

$expressions = []
$last_result = nil

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

def add(a, b)
    result = a + b
    exp = "#{a} + #{b} = #{result}"
    $expressions.push(exp)
    $last_result = result
    return result
end

ในแต่ละเมธอด เราได้ทำการคำนวณหาผลลัพธ์และเก็บไว้ในตัวแปร result และสร้างนิพจน์ไว้ในตัวแปร exp และเนื่องจากตัวแปร $expressions นั้นเป็นอาเรย์ เราได้ใช้เมธอด push สำหรับเก็บข้อมูลเข้าอาเรย์ และเก็บผลลัพธ์จากการคำนวณของเมธอดไว้ในตัวแปร $last_result

3
-2
1
36
Performed expressions:
["1 + 2 = 3", "3 - 5 = -2", "8 / 7 = 1", "4 * 9 = 36"]
Last result: 36

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม

Ruby instance variables

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

มาดูตัวอย่างการประกาศและใช้งานตัวแปร Instance

instance_variable.rb
class Box

    attr_accessor(:width, :height)

    def initialize(width, height)
        @width = width
        @height = height
    end

    def area()
        return @width * @height
    end

end

b1 = Box.new(2, 3)
b2 = Box.new(4, 6)

puts "Box1: w = #{b1.width}, h = #{b1.height}, area = #{b1.area()}"
puts "Box2: w = #{b2.width}, h = #{b2.height}, area = #{b2.area()}"

b2.width = 5
b2.height = 8
puts "Box2: w = #{b2.width}, h = #{b2.height}, area = #{b2.area()}"

ในตัวอย่าง เราได้ประกาศคลาส Box ด้วยคำสั่ง class และสิ้นสุดด้วยคำสั่ง end

def initialize(w, h)
    @width = w
    @height = h
end

ในเมธอด initialize เราได้ประกาศตัวแปร @width และ @height ซึ่งโดยทั่วไปแล้วตัวแปร Instance นั้นจะประกาศในเมธอด initialize โดยเมธอดนี้จะทำงานเมื่อออบเจ็คถูกสร้างขึ้นด้วยเมธอด Box.new ซึ่งพารามิเตอร์ในเมธอด initialize นั้นจะรับมาจากตอนที่เราสร้างออบเจ็คด้วยเมธอด Box.new

attr_accessor(:width, :height)

คำสั่ง attr_accessor นั้นใช้สำหรับสร้าง Getter และ Setter ให้กับแอตทริบิวต์ นั่นจำทหให้เราสามารถอ่านและเปลี่ยนแปลงแอตทริบิวต์จากภายนอกคลาสได้

def area()
    return @width * @height
end

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

b1 = Box.new(2, 3)
b2 = Box.new(4, 6)

puts "Box1: w = #{b1.width}, h = #{b1.height}, area = #{b1.area()}"
puts "Box2: w = #{b2.width}, h = #{b2.height}, area = #{b2.area()}"

หลังจากนั้นเราสร้างสองออบเจ็คจากคลาส Box นั่นคือ b1 และ b2 ซึ่งแต่ละออบเจ็คนั้นจะมีค่าความยาวและความสูงเป็นของตัวเอง หลังจากนั้นเป็นการแสดงความยาว ความสูงและพื้นที่ของกล่องออกมา

b2.width = 5
b2.height = 8
puts "Box2: w = #{b2.width}, h = #{b2.height}, area = #{b2.area()}"

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

Box1: w = 2, h = 3, area = 6
Box2: w = 4, h = 6, area = 24
Box2: w = 5, h = 8, area = 40

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม instance_variable.rb

Ruby class variables

ตัวแปรคลาส (Class variable) เป็นตัวแปรที่ใช้สำหรับแชร์ค่าระหว่างออบเจ็คที่สร้างจากคลาสนั้นๆ กล่าวคือ มันไม่ผูกอยู่กับออบเจ็คใดออบเจ็คหนึ่งเหมือนกับตัวแปร Instance ด้วยเหตุนี้ ตัวแปรคลาสจึงมีไว้สำหรับเก็บข้อมูลที่ใช้ร่วมกันสำหรับออบเจ็ค ตัวแปรคลาสจะขึ้นต้นด้วยเครื่องหมาย @@ มาดูตัวอย่างการใช้งานตัวแปรคลาสในภาษา Ruby

class_variable1.rb
class Person

    @@instances = 0

    attr_accessor :name, :age

    def initialize(name, age)
        @@instances += 1
        @name = name
        @age  = age.to_i
    end

    def inspect
      "#{name} (#{age})"
    end

    def self.total
        @@instances
    end

end

p1 = Person.new('Mateo', 10)
p2 = Person.new('Julia', 7)
p3 = Person.new('Luke', 9)
puts "Total object created: #{Person.total}"

puts p1.inspect
puts p2.inspect
puts p3.inspect

ในตัวอย่าง เรามีคลาส Person ซึ่งมีตัวแปร instance สองตัวแปรคือ @name และ @age สำหรับเก็บชื่อและอายุ และนอกจากนี้เรายังประกาศตัวแปรคลาส @@instances และกำหนดค่าเป็น 0โดยตัวแปรนี้จะใช้เพื่อนับว่าออบเจ็คของคลาสนี้ได้ถูกสร้างไปแล้วเท่าไหร่

def initialize(name, age)
     @@instances += 1
     @name = name
     @age  = age.to_i
end

เมื่อออบเจ็คถูกสร้างด้วยเมธอด Person.new นั่นจะทำให้เมธอด initialize ทำงาน ดังนั้นเราทำการเพิ่มค่าของตัวแปร @@instances ในเมธอดดังกล่าว นั่นจะทำให้ค่าของตัวแปรเพิ่มขึ้นทุกครั้งที่มีการสร้างออบเจ็คใหม่

def self.total
    @@instances
end

ในคลาส Person นั้นเราสามารถเข้าถึงตัวแปรคลาสด้วยชื่อตัวแปรได้เลย แต่ถ้าหากเราต้องการเข้าถึงตัวแปรคลาสภายนอกคลาส เราต้องสร้างเมธอดเพื่อเป็น Getter สำหรับตัวแปรคลาสดังกล่าว โดยเมธอด total นั้นถูกสร้างเป็นคลาสเมธอดโดยมีคำสั่ง self. นำหน้า ซึ่งเราสามารถเรียกใช้เมธอดผ่านทางชื่อคลาสตามด้วยชื่อเมธอด Person.total

p1 = Person.new('Mateo', 10)
p2 = Person.new('Julia', 7)
p3 = Person.new('Luke', 9)
puts "Total object created: #{Person.total}"

หลังจากที่เราสร้างคลาสเสร็จเรียบร้อยแล้ว ต่อไปเรานำคลาส Person มาสร้างออบเจ็คทั้งหมด 3 ออบเจ็ค หลังจากนั้นเราเรียกใช้คลาสเมธอด total เพื่อดูว่ามีกี่ออบเจ็คที่ถูกสร้างไปแล้ว

Total object created: 3
Mateo (10)
Julia (7)
Luke (9)

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

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

class_variable2.rb
class Person

    @@last_name_first = false

    def initialize(first_name, last_name)
        @first_name = first_name
        @last_name = last_name
    end

    def full_name
        if @@last_name_first
            return "#{@last_name} #{@first_name}"
        else
            return "#{@first_name} #{@last_name}"
        end
    end

    def self.last_name_first=(value)
        @@last_name_first = value
    end

end

p1 = Person.new('Luke', 'Chavez')
p2 = Person.new('Bernard', 'Green')

# By default, show first name first
puts p1.full_name
puts p2.full_name

# Show last name first
Person.last_name_first = true
puts p1.full_name
puts p2.full_name

ในตัวอย่าง เราได้ประกาศคลาส Person ซึ่งคลาสนี้มีตัวแปรสองตัวแปรคือ @first_name และ @last_name ที่ใช้สำหรับเก็บชื่อและนามสกุลของบุคคลที่จะนำมาสร้างออบเจ็ค

@@last_name_first = false

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

def full_name
    if @@last_name_first
        return "#{@last_name} #{@first_name}"
    else
        return "#{@first_name} #{@last_name}"
    end
end

เมธอด full_name ใช้สำหรับรับค่าชื่อแบบเต็ม โดยจะแสดงชื่อก่อนหรือนามสกุลก่อนนั้นึ้นอยู่กับตัวแปรคลาส @@last_name_first

def self.last_name_first=(value)
    @@last_name_first = value
end

คลาสเมธอด last_name_first= นั้นเป็นเมธอดสำหรับเปลี่ยนค่าตัวแปรคลาส @@last_name_first ซึ่งในภาษา Ruby นั้นคุณสามารถใช้สัญลักษณ์ในการตั้งชื่อเมธอดได้ ในกรณนี้เครื่องหมาย = นั้นเป็นส่วนหนึ่งของชื่อเมธอด

p1 = Person.new('Luke', 'Chavez')
p2 = Person.new('Bernard', 'Green')

# By default, show first name first
puts p1.full_name
puts p2.full_name

# Show last name first
Person.last_name_first = true
puts p1.full_name
puts p2.full_name

หลังจากนั้น เราได้สร้างออบเจ็คสองออบเจ็คจากคลาส Person และเรียกใช้เมธอด full_name สำหรับแสดงชื่อของบุคคล หลังจากนั้นเราเปลี่ยนค่าแปรคลาส @@last_name_first เป็น true ผ่านทางเมธอด last_name_first= และแสดงชื่อของบุคคลอีกครั้ง

Luke Chavez
Bernard Green
Chavez Luke
Green Bernard

และนี่เป็นผลลลัพธ์การทำงานของโปรแกรม สองบรรทัดแรกโปรแกรมจะแสดงชื่อก่อนนามสกุล สองบรรทัดต่อมาโปรแกรมแสดงนามสกุลก่อนชื่อจากผลที่เราเปลี่ยนตัวแปรคลาส @@last_name_first ซึ่งจะส่งผลกับทุกออบเจ็คที่ใช้คลาสนี้นั่นเอง

Ruby predefined constant variables

ตัวแปรค่าคงที่นั้นเป็นตัวแปรที่มีค่าเพียงหนึ่งเดียว โดยตัวแปรเหล่านี้เป็น instance ของคลาสในภาษา Ruby

self นั้นเป็นตัวแปรที่ใช้สำหรับอ้างถึงเมธอดหรือออบเจ็คในบริษทที่ใช้งานปัจจุบัน nil นั้นหมายการไม่มีค่าอะไรอยู่เลย true นั้นหมายถึงค่าทางตรรกศาสร์ที่มีค่าเป็นจริง และ false นั้นหมายถึงค่าทางตรรกศาสตร์ที่มีค่าเป็นเท็จ

p self
p nil
p true
p false

p self.class
p nil.class
p true.class
p false.class

ในตัวอย่างของการใช้งานตัวแปร pseudo เราได้แสดงค่าของตัวแปรด้วยเมธอด p และแสดงคลาสของตัวแปรทั้งหมดออกมา

p self

ในคำสั่งนี้ self จะส่งค่ากลับเป็นบริษทของส่วนหลักของโปรแกรม

main
nil
true
false
Object
NilClass
TrueClass
FalseClass

และนี่เป็นผลลัพธ์การทำงานของโปรแกรมที่แสดงค่าของตัวแปร pseudo ทั้งหมดและคลาสที่ตัวแปรเหล่านั้น

ต่อไปมาดูตัวอย่างการนำใช้ self และ nil ไปใช้ในการเขียนโปรแกรมในภาษา Ruby

predefined_constant_variable.rb
class Fruit

    def initialize(name, color, weight = nil)
        @name = name
        @color = color
        @weight = weight
    end

    def set_color(color)
        @color = color
        self
    end

    def set_weight(weight)
        @weight = weight
        self
    end

    def info
        print "#{@name} has #{@color.downcase} color"
        if @weight
            print " and #{@weight} Gram of weight"
        end
        puts
    end

end

f1 = Fruit.new('Apple', 'Red')
f2 = Fruit.new('Banana', 'Yellow', 80)
f1.info
f2.info

f1.set_color('Green').set_weight(120)
f1.info

ในตัวอย่างนั้น เราได้ประกาศคลาส Fruit ซึ่งเป็นคลาสสำหรับเก็บชื่อ สี และน้ำหนักของผลไม้ สังเกตุว่าในเมธอด initialize ในพารามิเตอร์ weight ของเมธอดเป็นการกำหนดค่า default ให้กับพารามิเตอร์ดังกล่าว นั่นหมายความว่าในตอนสร้างออบเจ็คด้วยเมธอด new เราสามารถละเว้นการส่งค่านี้มาได้ และค่าของตัวแปรนี้จะเป็น nil

def info
    print "#{@name} has #{@color.downcase} color"
    if @weight
        print " and #{@weight} Gram of weight"
    end
    puts
end

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

f1 = Fruit.new('Apple', 'Red')
f2 = Fruit.new('Banana', 'Yellow', 80)
f1.info
f2.info

ต่อมาเราสร้างออบเจ็คมาสองออบเจ็คจากคลาส Fruit ออบเจ็คแรกเราได้ละเว้นพารามิเตอร์ที่สาม นั่นจะส่งผลให้ค่า @weight ของออบเจ็คนี้เป็น nil และสำหรับออบเจ็คที่สองเราส่งพารามิเตอร์ครบทุกตัว และหลังจากนั้นเราแสดงข้อมูลของออบเจ็คผลไม้ด้วยเมธอด info

def set_color(color)
    @color = color
    self
end

def set_weight(weight)
    @weight = weight
    self
end

นอกจากนี้ในคลาส Fruit เรายังได้กำหนด Setter เมธอดสำหรับตัวแปร @color และ @weight สังเกตุว่าภายในเมธอดทั้งสอง หลังจากที่กำหนดค่าใหม่ให้กับตัวแปรแล้ว เราได้ส่งค่าตัวแปร self กลับ ซึ่งหมายถึงออบเจ็คในบริษทปัจจุบันกลับไป

f1.set_color('Green').set_weight(120)
f1.info

และเนื่องจากทั้งเมธอด set_color และ set_weight ส่งค่ากลับมาเป็น self และ self นั้นมีค่าเท่ากับตัวแปร f1 นั่นทำให้เราสามารถเรียกใช้งานเมธอดแบบ chain ได้ ซึ่งเป็นสิ่งที่คุณจะพบได้บ่อยในภาษา Ruby

Apple has red color
Banana has yellow color and 80 Gram of weight
Apple has green color and 120 Gram of weight

และนี่เป็นผลลัพธ์ของการรันโปรแกรมสำหรับตัวอย่างการใช้งาน self และ nil

ต่อไปเป็นตัวอย่างการใช้งานค่า true และ false ซึ่งความจริงแล้วสองค่านี้เป็นค่าของ boolean กล่าวคือมันเป็นข้อมูลประเภท boolean นั้นเอง แต่อย่างไรก็ตามมันถูกสร้างมาจากคนละคลาสนั้นคือคลาส TrueClass และคลาส FalseClass ตามลำดับ

ค่า boolean นั้นเป็นค่าที่มีเพียงสองค่าเท่านั้นนั่นคือ true และ false แต่อย่างไรก็ตาม ในการเขียนโปรแกรมจริงๆ นั้นเราอาจจะใช้ค่าอื่นแทนก็ได้ ยกตัวอย่างเช่น 1 และ 0 สำหรับตอนนี้เราจะมาคุณมาทำความเข้าใจเกี่ยวกับการใช้งาน true และ false ในภาษา Ruby

like = true

if like
    puts "I like Ruby language"
end

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

Ruby predefined variables

ในภาษา Ruby มีตัวแปรที่กำหนดไว้ล่วงหน้า (predefined variable) จำนวนหนึ่ง เพื่ออำนวยความสะดวกให้กับโปรแกรมเมอร์ในการเขียนโปรแกรม ซึ่งตัวแปรเหล่านี้นั้นเป็นตัวแปร global เนื่องจากมันขึ้นต้นด้วยเครื่องหมาย $

สำหรับบทนี้ เราจะแสดงตัวอย่างการใช้งานสำหรับตัวแปรบางตัวเท่านั้น

predefined_variable1.rb
p "apple banana orange".split
$; = "-"
p "apple-banana-orange".split

numbers = ["one", "two", "three", "four"]
p numbers.join
$, = ", "
p numbers.join

ในตัวอย่างเป็นการใช้งานตัวแปร $; เป็นตัวแปรที่ใช้เก็บ String เพื่อใช้เป็นจุดตัดสำหรับเมธอด split และตัวแปร $, เป็นตัวแปรที่ใช้เก็บ String เพื่อเชื่อมอาเรย์ด้วยเมธอด joint

$; = "-"
p "apple-banana-orange".split

ในตอนแรก เราได้เรียกใช้งานเมธอด split เพื่อตัด String ออกจากกันและค่าที่ได้นั้นจะเป็นอาเรย์ของ String โดยเมธอดนี้จะใช้ค่าในตัวแปร $; เป็นตัวแบ่ง เราสามารถเปลี่ยนแปลงค่าในตัวแปรนี้ ถ้าหากเราต้องการใช้ค่าอื่นเป็นตัวแบ่ง

$, = ", "
p numbers.join

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

["apple", "banana", "orange"]
["apple", "banana", "orange"]
"onetwothreefour"
"one, two, three, four"

นี่เป็นผลลัพธ์การทำงานของโปรแกรม

มาดูตัวอย่างอีกสักนิด ซึ่งจะเป็นตัวอย่างของตัวแปร global ที่ใช้งานกับ regular expression

predefined_variable2.rb
s = "Let the peace of Christ rule in your hearts"

matched = s.match(/Christ/)

puts matched.inspect
puts "Pre-matched: #{matched.pre_match}"
puts "Matched text: #{matched}"
puts "Post-matched: #{matched.post_match}"

puts $~.inspect
puts "Pre-matched: #{$`}"
puts "Matched text: #{$&}"
puts "Post-matched: #{$'}"

ในตัวอย่าง เป็นการใช้งานเมธอด match จากตัวแปร String จะทำการค้นหาข้อความที่ตรงกับ pattern /Christ/ ที่ส่งเป็นพารามิเตอร์ของเมธอด โดยเมธอดจะส่งค่ากลับเป็นออบเจ็ค MatchData ที่เก็บรายละเอียดของการ match เอาไว้

puts matched.inspect
puts "Pre-matched: #{matched.pre_match}"
puts "Matched text: #{matched.to_s}"
puts "Post-matched: #{matched.post_match}"

ส่วนแรกของการแสดงผลนั้นเราใช้ข้อมูลจากตัวแปรออบเจ็ค matched ซึ่งจะประกอบไปด้วยข้อมูลของการ match ทั้งหมด เมธอด pre_match จะส่ง string ทางด้านซ้ายของการ match เมธอด post_match จะส่ง string ทางด้านขวาของการ match และเมธอด to_s เป็น string จากการ match

puts $~.inspect
puts "Pre-matched: #{$`}"
puts "Matched text: #{$&}"
puts "Post-matched: #{$'}"

นอกจากเมธอดจะส่งค่ากลับมาเป็นออบเจ็คของ MatchData แล้ว Ruby ยังนำข้อมูลเหล่านั้นเก็บไว้ในตัวแปร global ด้วย ตัวแปร $~ จะเก็บข้อมูลการ match ครั้งสุดท้ายในขอบเขตปัจจุบัน ตัวแปร $` จะเก็บ string ทางด้านซ้ายของการ match ที่สำเร็จครั้งสุดท้าย ตัวแปร $' จะเก็บ String ทางด้านขวาของการ match ที่สำเร็จครั้งสุดท้าย และตัวแปร $& จะเก็บ string ที่ match สำเร็จครั้งสุดท้าย

#<MatchData "Christ">
Pre-matched: Let the peace of
Matched text: Christ
post-matched:  rule in your hearts
#<MatchData "Christ">
Pre-matched: Let the peace of
Matched text: Christ
post-matched:  rule in your hearts

และนี่เป็นผลลัพธ์การทำงานของโปรแกรม

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