การเรียกซ้ำใน Lua
สร้างอัลกอริทึมแบบเรียกซ้ำและทำความเข้าใจการเพิ่มประสิทธิภาพการเรียกท้ายฟังก์ชันของ Lua
การเรียกซ้ำใน Lua เป็นบทเรียน Lua Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Lua Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน
การเรียกซ้ำคืออะไร
การเรียกซ้ำคือการที่ฟังก์ชันเรียกตัวเองเพื่อแก้ปัญหาขนาดเล็กลงในลักษณะเดียวกัน ฟังก์ชันที่เรียกซ้ำทุกฟังก์ชันต้องมีกรณีฐาน (จุดที่หยุด) และกรณีเรียกซ้ำ (จุดที่ลดขนาดปัญหา) หากไม่มีกกรณีฐาน การเรียกซ้ำจะทำให้สแตกโอเวอร์โฟลว์
local function countdown(n)
if n <= 0 then
print("Blast off!")
return
end
print(n)
countdown(n - 1)
end
countdown(5)
-- 5, 4, 3, 2, 1, Blast off!แฟกทอเรียล
แฟกทอเรียลเป็นตัวอย่างคลาสสิกของการเรียกซ้ำ: n! = n × (n-1)! โดยมีกรณีฐานเป็น 0! = 1 การเรียกแต่ละครั้งจะลดค่า n ลง 1 จนถึงกรณีฐาน จากนั้นผลลัพธ์จะ "คลายกลับ" ขึ้นมาตามสแตกการเรียก โดยคูณค่าไปด้วย
local function factorial(n)
if n <= 1 then return 1 end
return n * factorial(n - 1)
end
print(factorial(1)) -- 1
print(factorial(5)) -- 120
print(factorial(10)) -- 3628800ฟีโบนัชชี
ลำดับฟีโบนัชชีเป็นอีกตัวอย่างคลาสสิก: fib(n) = fib(n-1) + fib(n-2) การเรียกซ้ำแบบพื้นฐานมีเวลาเพิ่มขึ้นแบบเอ็กซ์โพเนนเชียล เพราะคำนวณค่าเดิมซ้ำหลายครั้ง เราจะเห็นการจดจำผลลัพธ์เป็นวิธีแก้ แต่ขอเริ่มจากรูปแบบพื้นฐานก่อน
local function fib(n)
if n <= 1 then return n end
return fib(n - 1) + fib(n - 2)
end
for i = 0, 10 do
io.write(fib(i) .. " ")
end
print()
-- 0 1 1 2 3 5 8 13 21 34 55การเรียกซ้ำแบบจดจำผลลัพธ์
การจดจำผลลัพธ์จะเก็บผลลัพธ์ที่คำนวณแล้วไว้ในตาราง ในการเรียกแต่ละครั้ง ให้ตรวจสอบแคชก่อน หากมีค่าอยู่ ให้คืนค่าทันที มิฉะนั้นให้คำนวณและเก็บค่าไว้ วิธีนี้เปลี่ยนการคำนวณฟีโบนัชชีจากเวลาแบบเอ็กซ์โพเนนเชียลเป็นเวลาเชิงเส้น
local memo = {}
local function fib(n)
if memo[n] then return memo[n] end
if n <= 1 then return n end
local result = fib(n-1) + fib(n-2)
memo[n] = result
return result
end
print(fib(40)) -- 102334155 (fast!)การเพิ่มประสิทธิภาพการเรียกแบบหาง
Lua ทำการเพิ่มประสิทธิภาพการเรียกแบบหาง (TCO): เมื่อการกระทำสุดท้ายของฟังก์ชันคือการเรียกฟังก์ชันอื่น Lua จะนำเฟรมสแตกปัจจุบันกลับมาใช้ใหม่ ทำให้ฟังก์ชันที่เรียกซ้ำแบบหางใช้พื้นที่สแตก O(1) การเรียกซ้ำทุกแบบไม่ใช่การเรียกซ้ำแบบหาง — การเรียกนั้นต้องเป็นสิ่งสุดท้ายในฟังก์ชัน
-- Tail-recursive factorial using accumulator
local function factTail(n, acc)
acc = acc or 1
if n <= 1 then return acc end
return factTail(n - 1, n * acc) -- tail call
end
print(factTail(10)) -- 3628800
-- Can handle very large n without stack overflowการท่องผ่านต้นไม้
การเรียกซ้ำเหมาะกับการแสดงการท่องผ่านต้นไม้ ต้นไม้คือตารางที่มีฟิลด์ value, left และ right การท่องแบบพรีออร์เดอร์ อินออร์เดอร์ และโพสต์ออร์เดอร์ต่างกันเพียงจังหวะที่ประมวลผลค่าเมื่อเทียบกับการเรียกซ้ำ
local function inorder(node)
if node == nil then return end
inorder(node.left)
io.write(node.value .. " ")
inorder(node.right)
end
local tree = {
value=4,
left={value=2, left={value=1}, right={value=3}},
right={value=6, left={value=5}, right={value=7}}
}
inorder(tree) -- 1 2 3 4 5 6 7การเรียกซ้ำร่วมกัน
ฟังก์ชันสองฟังก์ชันสามารถเรียกหากันได้ (การเรียกซ้ำร่วมกัน) ใน Lua คุณต้องประกาศล่วงหน้า: ประกาศตัวแปรเฉพาะที่ก่อน แล้วจึงกำหนดฟังก์ชัน วิธีนี้ทำให้แต่ละฟังก์ชันอ้างอิงตัวแปรของอีกฟังก์ชันได้ เพราะตัวแปรนั้นอยู่ในขอบเขตแล้ว
local isEven, isOdd
isEven = function(n)
if n == 0 then return true end
return isOdd(n - 1)
end
isOdd = function(n)
if n == 0 then return false end
return isEven(n - 1)
end
print(isEven(10)) -- true
print(isOdd(7)) -- trueการคัดลอกเชิงลึกแบบเรียกซ้ำ
การเรียกซ้ำเหมาะอย่างยิ่งกับการทำงานกับโครงสร้างซ้อนกัน การคัดลอกตารางแบบเชิงลึกหมายถึงการคัดลอกเนื้อหาและคัดลอกตารางที่ซ้อนกันอยู่แบบเรียกซ้ำด้วย (เพื่อให้การแก้ไขสำเนาไม่ส่งผลต่อต้นฉบับ)
local function deepCopy(orig)
local copy
if type(orig) == "table" then
copy = {}
for k, v in pairs(orig) do
copy[deepCopy(k)] = deepCopy(v)
end
setmetatable(copy, getmetatable(orig))
else
copy = orig
end
return copy
end
local a = {1, {2, 3}}
local b = deepCopy(a)
b[2][1] = 99
print(a[2][1]) -- 2 (unchanged)อัลกอริทึมเติมพื้นที่
การเติมพื้นที่ (ใช้ในโปรแกรมระบายสีและแผนที่เกม) เหมาะกับการเรียกซ้ำโดยธรรมชาติ เริ่มจากช่องหนึ่ง ทำเครื่องหมายช่องนั้น แล้วเติมช่องข้างเคียงที่ยังไม่เคยเยี่ยมชมทีละช่องแบบเรียกซ้ำ การเรียกซ้ำจะสิ้นสุดเมื่อพบขอบเขตหรือช่องที่เยี่ยมชมแล้ว
local grid = {
{0,0,0,1,0},
{0,1,0,1,0},
{0,1,1,1,0},
{0,0,0,0,0},
}
local function fill(g, r, c)
if r<1 or r>#g or c<1 or c>#g[r] then return end
if g[r][c] ~= 0 then return end
g[r][c] = 2 -- mark visited
fill(g,r-1,c); fill(g,r+1,c)
fill(g,r,c-1); fill(g,r,c+1)
end
fill(grid, 1, 1)
print(grid[1][1], grid[2][1]) -- 2 2การตระหนักถึงความลึกของสแตก
สแตกการเรียกเริ่มต้นของ Lua มีขีดจำกัด (โดยทั่วไปประมาณ 200 ระดับสำหรับการเรียกที่ไม่ใช่แบบหาง) การเรียกซ้ำเชิงลึกที่ไม่ใช่แบบหางจะทำให้เกิดข้อผิดพลาด "สแตกโอเวอร์โฟลว์" วิธีแก้คือเปลี่ยนเป็นการเรียกซ้ำแบบหาง ใช้สแตกที่จัดการเอง (ตาราง) หรือใช้โครูทีนสำหรับงานวนซ้ำขนาดใหญ่
-- Simulate deep recursion danger
local function depth(n)
if n == 0 then return "done" end
return depth(n - 1) -- tail call, safe
end
print(depth(100000)) -- done (tail call, no overflow)
-- Non-tail: limited depth
local function countDown(n)
if n == 0 then return 0 end
return 1 + countDown(n - 1) -- NOT a tail call
end
-- countDown(10000) would stack overflowตรวจสอบอย่างรวดเร็ว
ข้อใดต่อไปนี้เป็นการเรียกแบบหางที่ถูกต้องใน Lua
ทบทวน: การเรียกซ้ำ
สรุป:
- ฟังก์ชันที่เรียกซ้ำทุกฟังก์ชันต้องมีกรณีฐานและกรณีเรียกซ้ำ
- การเรียกแบบหาง (การกระทำสุดท้ายคือการเรียก) ได้รับการเพิ่มประสิทธิภาพโดย Lua — ใช้สแตก O(1)
- การจดจำผลลัพธ์เปลี่ยนการเรียกซ้ำแบบเอ็กซ์โพเนนเชียลให้เป็นเชิงเส้น
- การเรียกซ้ำร่วมกันต้องมีการประกาศล่วงหน้า
- การเรียกซ้ำเชิงลึกบนต้นไม้หรือกราฟเป็นเรื่องเหมาะสมตามธรรมชาติ แต่ควรระวังความลึกของสแตกสำหรับการเรียกที่ไม่ใช่แบบหาง
คำถามที่พบบ่อย
บทเรียน “การเรียกซ้ำใน Lua” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การเรียกซ้ำใน Lua” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Lua Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Lua Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การเรียกซ้ำใน Lua”
สร้างอัลกอริทึมแบบเรียกซ้ำและทำความเข้าใจการเพิ่มประสิทธิภาพการเรียกท้ายฟังก์ชันของ Lua คุณปฏิบัติ Lua Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Lua Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Lua Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การเรียกซ้ำใน Lua” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Lua Academy นี้ได้ไหม
ได้ บทเรียน Lua Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การกำหนดและเรียกใช้ฟังก์ชัน
- ค่าที่ส่งคืนหลายค่า
- อาร์กิวเมนต์จำนวนแปรผันและตัวดำเนินการ ...
- การเรียกซ้ำใน Lua