บทความก่อนหน้า
Iterator
คือฟังก์ชันที่ส่งค่าชุดหนึ่งออกมาที่ละส่วนเมื่อมีการเรียกซึ่งสามารถเอาไปใช้ในการวนรอบการทำงานของโปรแกรมได้ แบ่งออกเป็น stateless และ stateful แบบแรกนั้นคือฟังก์ชันที่ไม่มีตัวเก็บสถานะการวนรอบไว้เช่นฟังก์ชัน next(), ipairs(), pairs() ที่เคยกล่าวถึงในตอนก่อนๆ
Stateless Iterator
ตัวอย่างฟังก์ชัน next()
local a = {5,6,7,8}
print(next(a)) -- 1 5
print(next(a,nil)) -- 1 5
print(next(a,3)) -- 4 8
print(next(a,1)) -- 2 6
print(next(a)) -- 1 5
print(next(a,4) -- nil
-- เรียกใช้ในลูป
for i,v in next,a,nil do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
เราสามารถจำลองการทำงานของฟังก์ชัน ipairs() ได้ดังนี้
function itor(tbl,control_index)
local index = control_index+1
local value = tbl[index]
if value then
return index, value
end
return nil
end
function ipairs_less(tbl)
return itor, tbl, 0
end
for i,v in ipairs_less(a) do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
-- ได้ผลเหมือนกับ
for i,v in itor,a,0 do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
-- เรียกใช้ฟังก์ชันโดยตรง
print(itor(a,0)) -- 1 5
print(itor(a,2)) -- 3 7
print(itor(a,0)) -- 1 5
เห็นได้ว่าไม่ว่าเราจะเรียก next() หรือ itor() กี่ครั้งด้วยอาร์กิวเมนท์เดิมก็จะได้ค่าเดิมออกมาค่าจะเปลี่ยนตามตัวแปรควบคุมที่ส่งเข้าไปไม่ได้มีการเก็บสถานะไว้ในฟังก์ชัน
Stateful Iterator
จะมีการเก็บสถานะลำดับข้อมูลไว้เราสามารถสร้าง iterator แบบ stateful ได้ด้วยวิธี closure และ coroutine ดังนี้
อย่างที่เรารู้กันในบทก่อนๆ ว่า closure จะเก็บค่าตัวแปร local ในบล๊อกนอกฟังก์ชันเป็น upvalue ซึ่งฟังก์ชันสามารถเข้าถึงได้เราจึงเก็บสถานะของ iterator ไว้ใน upvalue เมื่อมีการเรียกใช้ฟังก์ชันแต่ละครั้งค่าสถานะก็จะเปลี่ยนไปเรื่อยๆ โดยเราไม่ต้องส่งค่าตัวควบคุมไปให้ฟังก์ชัน
-- แบบ Closure
function ipairs_ful1(tbl)
local index = 0 -- เป็น upvalue ของฟังก์ชันที่บรรทัดถัดไป
return function()
index = index + 1
local value = tbl[index]
if value then
return index, value
end
return nil
end
end
for i,v in ipairs_ful1(a) do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
-- เรียกใช้ฟังก์ชันโดยตรง
local itor_cl = ipairs_ful1(a)
print(itor_cl()) -- 1 5
print(itor_cl()) -- 2 6
print(itor_cl()) -- 3 7
-- หรือสามารถเรียกใช้งานแบบนี้ก็ได้ผลเหมือนกัน
itor_cl = ipairs_ful1(a)
for i,v in itor_cl do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
ในตอนที่แล้วเรารู้จักกับ coroutine ซึ่งสามารถหยุดการทำงานชั่วคราวและเริ่มทำงานต่อจากที่จุดที่หยุดได้ซึ่งเราสามารถมาประยุกต์ใช้กับ iterator ได้โดยฟังก์ชันจะเก็บสถานะตำแหน่งที่หยุดการทำงานไว้และเริ่มทำงานต่อเมื่อเรียกใช้ฟังก์ชันครั้งต่อไปโดยไม่ต้องอาศัยตัวควบคุมการวนรอบจากภายนอก
--แบบ Coroutine
function ipairs_ful2(tbl)
return coroutine.wrap(function()
for index=1,#tbl do
coroutine.yield(index, tbl[index])
end
end)
end
for i,v in ipairs_ful2(a) do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
-- เรียกใช้ฟังก์ชันโดยตรง
local itor_co = ipairs_ful2(a)
print(itor_co()) -- 1 5
print(itor_co()) -- 2 6
print(itor_co()) -- 3 7
-- หรือสามารถเรียกใช้งานแบบนี้ก็ได้ผลเหมือนกัน
itor_co = ipairs_ful2(a)
for i,v in itor_co do
print(i,v)
end
-- ผลลัพธ์
--[[
1 5
2 6
3 7
4 8
]]
การใช้งาน Iterator
--ตัวอย่าง Iterator แบบต่างๆ
--หาตัวอักษรจากข้อความ
function char_iterator(str)
local i = 0
return function()
i = i + 1
if i <= #str then
return str:sub(i,i)
end
end
end
for char in char_iterator "Hello" do
print(char)
end
-- ผลลัพธ์
--[[
H
e
l
l
o
]]
--หาเลขจำนวนเต็มในช่วงที่กำหนด
function range(start_num,stop_num)
local start = stop_num and start_num or 1
local stop = stop_num or start_num
if start>stop then
start,stop = stop,start
end
return function()
if start<=stop then
local num = start
start = start + 1
return num
end
end
end
for i in range(5,3) do
for j in range(2,4) do
print(i,j)
end
end
-- ผลลัพธ์
--[[
3 2
3 3
3 4
4 2
4 3
4 4
5 2
5 3
5 4
]]
for i in range(6) do
print(i)
end
-- ผลลัพธ์
--[[
1
2
3
4
5
6
]]
--หารายการในลิสต์
function fruits()
local t = {"apple","banana", "orange"}
local i = 0
return function()
i = i + 1
return t[i]
end
end
for fruit in fruits() do
print(fruit)
end
-- ผลลัพธ์
--[[
apple
banana
orange
]]
ตอนต่อไปมารู้จัก OOP ในแบบ Lua กันครับ
บทความถัดไป
ความคิดเห็น
แสดงความคิดเห็น