มารั่วกันต่อ#4

Tables

มาถึงสุดยอดประเภทข้อมูลสารพัดประโยชน์ซึ่ง table สามารถเป็นได้หลายอย่างทั้ง array หรือ object มาดูพื้นฐานของ table กัน

การประกาศและกำหนดค่า

a={}
-- เป็นการประกาศ table เปล่า


a={1,"hello", true, f(x)}
-- เป็นการสร้าง table โดยกำหนด index เป็นเลขลำดับอัตโนมัติโดยลำดับแรกเป็น 1
for index,value in ipairs(a) do print(index,value) end
--[[ ผลลัพธ์

1     1
2     hello
3     true
4     20
--]]

b={a=1, 2, ["b"]=3, [4]=4, 5}
-- เป็นการสร้าง table โดยกำหนด key และ value ในรูปแบบ key=value
-- ถ้าไม่กำหนด key ก็จะเป็นเลขลำดับอัตโนมัติให้
for key,value in pairs(b) do print(key,value) end
--[[ ผลลัพธ์
1     2
2     5
a     1
b     3
4     4
--]]

c={}
c[2]="A"
c["s"]="B"    -- สำหรับ key เป็น string
c.x="D"       -- สำหรับ key เป็น string
-- เป็นการกำหนดค่า table ทีละค่า
for key,value in pairs(c) do print(key,value) end
--[[ ผลลัพธ์
s     B
2     A
x     D
--]]

การเรียกดูค่าใน table

--เหมือนการกำหนดทีละค่า
print(c["x"])      -----> D
d= c.s
print(d)           -----> B
Array หลายมิติก็คือ table ซ้อน table เช่น
a={[1]={[1]='a', [2]='b', [3]='c'}, [2]={[1]='d', [2]='e', [3]='f'}}
print(a[1][3])     -----> c
a[3][1]='g'
a[3][2]="h"
a[3][3]='i'
-- เพิ่ม {[3]={'g','h',"i"}} เข้าไปใน a

b={x={1, 2, 3}, y={a=4, b=5, c=6}}
print(b.x[2])      -----> 2
print(b["x"][1])   -----> 1
print(b.y.c)       -----> 6
print(b['y']["a"]) -----> 4
นอกจากนี้เราสามารถใช้ ; เป็นตัวแบ่งข้อมูลแทน , ก็ได้และในข้อมูลลำดับสุดท้ายจะใส่ตัวแบ่งหรือไม่ก็ได้เช่นกัน
a={1; 2; 3, 4, 5; 6,}
การดูจำนวนสมาชิกใน table สามารถใช้เครื่องหมาย # หน้าชื่อตัวแปรโดยที่ต้องเป็น table ชนิดที่มี index เป็นเลขลำดับต่อเนื่องกันเท่านั้น
a={'a','b','c','d','e','f',}
print(#a)          -----> 6

a={'a','b','c',[10]='d','e','f',}
print(#a)          -----> 5
for i,v in ipairs(a) do print(i,v) end
--[[ ผลลัพธ์
1     a
2     b
3     c
4     e
5     f
--]]
for k,v in pairs(a) do print(k,v) end
--[[ ผลลัพธ์
1     a
2     b
3     c
4     e
5     f
10    d
--]]

ในการเขียนโปรแกรมแบบ OOP ภาษาอื่นที่เป็น class based มักจะเริ่มจากการสร้าง class แต่ Lua ซึ่งเป็น prototype based เหมือนกับ JavaScript ไม่ต้องสร้าง class แต่เริ่มจาก prototype object ซึ่งใน JavaScript จะใช้ function เป็น object แต่ใน Lua เราจะใช้ table นี่แหละครับ (Lua ก็ใช้ function เป็น object ได้เหมือนกันด้วย cloure แต่เราจะไม่กล่าวถึงตอนนี้)
a={name='John', showName=function (self) print('My name is '..self.name) end}
-- สร้าง object ชื่อ a มี property คือ name และมี method คือ showName()

a.showName(a)      -----> My name is John
-- เรียกใช้ function

a:showName()       -----> My name is John
-- เรียกแบบ method ใน object

a.age=18
a.surName=''
-- สามารถเพิ่ม แก้ไข property ได้โดยตรง

function a:showName() print('My name is '..self.name..' Smith') end
--[[
   เพิ่มหรือแก้ไข method โดยการสร้าง function ชื่อของ object + : + ชื่อของ method
   ถ้าใช้ ชื่อ object + . + ชื่อ method จะต้องใส่ self ด้วยเพื่อให้อ้างถึง property ของ object ได้
   เช่น function a.showName(self) ...
--]]

a:showName()       -----> My name is John Smith

a.showName=function (self, surName)
             self.surName=surName or self.surName
             print('My name is '..self.name..' '..self.surName)
           end
-- เพิ่มแก้ไข method โดยสร้าง anonymous function ใส่ใน table

a:showName('Wu')   -----> My name is John Wu

function fn(self) print("I'm "..self.age..' years old') end
a.showAge=fn
-- เพิ่มหรือแก้ไข method โดยกำหนดค่า function ที่มีอยู่แล้วให้กับ table

a:showAge()        -----> I'm 18 years old
จากตัวอย่างข้างต้น object a ยังไม่สมบูรณ์เรายังไม่สามารถสืบทอดได้ ถ้าเราใช้ b=a จะเป็นการให้ b ชี้ไปที่เดียวกับ a ถ้ามีการเปลี่ยนแปลงค่าของ property หรือเรียกใช้ method ใน b ก็จะส่งผลถึง a ด้วย ดังนั้นเราจะต้องสร้าง constructor เพื่อให้สามารถสร้าง object ใหม่จากตัวต้นแบบได้
function a:new(...)                      -- เครื่องหมาย ... หมายถึงไม่จำกัดจำนวน argument
  newObject={}
  args={...}                             -- สร้าง table ที่เก็บ argument แต่ละตัว
  newObject.name = args[1] or "John"     -- รับ arguments และกำหนดค่า defult
  newObject.surName = args[2] or "Smith"
  self.age = args[3] or 18               -- age จะเป็น property ของ a เมื่อเปลี่ยนค่านี้
                                         -- จะส่งผลถึง instance อื่นๆของ a ด้วย
  self.__index = self                    -- สร้างการอ้างอิงถึงสมาชิกในตัว table a
  return setmetatable(newObject,self)    -- คืนค่า object ใหม่ที่เชื่อมโยงกับ object a
end

b=a:new('Jane','Doe',20)
-- สร้าง object ใหม่

b:showName()       -----> My name is Jane Doe
b:showAge()        -----> I'm 20 years old
a:showName()       -----> My name is John Wu
a:showAge()        -----> I'm 20 years old
c=a:new('Jim')
c:showName()       -----> My name is Jim Smith
c:showAge()        -----> I'm 18 years old
a:showName()       -----> My name is John Wu
a:showAge()        -----> I'm 18 years old


การใช้ metatable

a1={a=1, b=2}
a2={c=3, d=4}
setmetatable(a1,{__index=a2})
print(a1.a,a1.b,a1.c,a1.d)      -----> 1    2    3    4
for k,v in pairs(a1) do print(k,v) end
--[[ ผลลัพธ์
a      1
b      2
--]]
   **หมายเหตุ self ที่อยู่ใน constructor จะอ้างอิงถึงตัวแม่แบบคือ a เช่น
c = a:new("Jim") เท่ากับว่า c = a.new(a,"Jim") 
ส่วน self ที่อยู่ใน function อื่นๆ จะอ้างถึง instance ที่เรียกใช้ function นั้นๆ
c:showName() เท่ากับว่า c.showName(c)
**
นอกจากนี้ยังสามารถประยุกต์ใช้ table เป็นโครงสร้างข้อมูลอื่นๆ เช่น linked list หรือ queue ได้อีกด้วยซึ่งสามารถไปศึกษาเพิ่มเติมกันได้ทั้งจากเว็บทางการ หรือจะค้นจากอากู๋ ก็มีตัวอย่างให้ลองเล่นดูได้ สำหรับ Lua ก็ขอพักยกก่อนช่วงนี้โพสถี่วันเว้นวันผิดคอนเซ็ปต์บล๊อก เดี๋ยวอาจพักยาวหาเรื่องอื่นมาโพสบ้าง ถ้ามีโอกาศค่อยมาต่อ Lua อีกทีครับ

link เพิ่มเติม
  คู่มือล่าสุด Lua 5.3
  wikipedia ภาษา Lua 

ตอนที่ 1 มารั่วกันเถอะ
ตอนที่ 2 มารั่วกันต่อ#2
ตอนที่ 3 มารั่วกันต่อ#3

หัดเขียนโปรแกรมแบบบ้านๆ ตอนที่ 3 พื้นฐานภาษา Lua

ความคิดเห็น

  1. ปล1. เพิ่มเติม index ของ table จะเริ่มต้นที่ 1 ต่างจาก array หรือ list ของภาษาอื่นที่เริ่มที่ 0 ตามภาษา C
    ปล2. แก้ไข : ในฟังก์ชั่นเป็นการซ่อนพารามิเตอร์ตัวแรกที่แทนถึงตัวเอง ดังนั้น c:showName() หมายถึง c.showName(c)
    ปล3. เราสามารถใช้ metamethod "__call" ร่วมกับ metatable เพื่อสร้าง function table เช่นใช้การเรียก a('Julia','Robert',28) แทนที่ a:new('Julia','Robert',28) โดยใช้
    setmetatable(a,{__call=function(self, ...)
    ....... --เหมือนในตัวอย่าง a:new()
    return setmetatable(newObject, self)
    end})

    ตอบลบ

แสดงความคิดเห็น