ฉันติดต่อกับ Lua เป็นอย่างมากในช่วงไม่กี่เดือนที่ผ่านมาและฉันชอบคุณสมบัติส่วนใหญ่ แต่ฉันก็ยังขาดอะไรบางอย่างอยู่:
- ทำไมถึงไม่มี
continue
? - มีวิธีแก้ปัญหาอะไรบ้าง
ฉันติดต่อกับ Lua เป็นอย่างมากในช่วงไม่กี่เดือนที่ผ่านมาและฉันชอบคุณสมบัติส่วนใหญ่ แต่ฉันก็ยังขาดอะไรบางอย่างอยู่:
continue
?คำตอบ:
ใน Lua 5.2 วิธีแก้ปัญหาที่ดีที่สุดคือใช้ goto:
-- prints odd numbers in [|1,10|]
for i=1,10 do
if i % 2 == 0 then goto continue end
print(i)
::continue::
end
สิ่งนี้ได้รับการสนับสนุนใน LuaJIT ตั้งแต่รุ่น 2.0.1
continue
หนึ่งวัน การgoto
แทนที่ไม่ได้ดูดีมากและต้องการบรรทัดเพิ่มเติม และนั่นจะไม่สร้างปัญหาถ้าคุณมีวงวนมากกว่าหนึ่งอันที่ทำสิ่งนี้ในฟังก์ชั่นเดียวทั้งคู่ด้วย::continue::
? การสร้างชื่อต่อวงไม่ได้ฟังดูเหมือนเป็นเรื่องที่ควรทำ
วิธีการที่ภาษาจัดการขอบเขตคำศัพท์สร้างปัญหากับทั้งและgoto
continue
ตัวอย่างเช่น,
local a=0
repeat
if f() then
a=1 --change outer a
end
local a=f() -- inner a
until a==0 -- test inner a
การประกาศlocal a
ภายในหน้ากากร่างกายห่วงตัวแปรนอกชื่อa
และขอบเขตของท้องถิ่นที่ทอดตัวข้ามเงื่อนไขของคำสั่งเพื่อให้อยู่ในสภาพที่มีการทดสอบด้านในสุดuntil
a
หากcontinue
มีอยู่มันจะต้องถูก จำกัด ความหมายเพื่อให้ใช้ได้เฉพาะหลังจากตัวแปรทั้งหมดที่ใช้ในเงื่อนไขนั้นเข้ามาในขอบเขต นี่เป็นเงื่อนไขที่ยากในการจัดทำเอกสารให้กับผู้ใช้และบังคับใช้ในคอมไพเลอร์ มีการหารือข้อเสนอต่าง ๆ เกี่ยวกับปัญหานี้รวมถึงคำตอบง่ายๆที่ไม่อนุญาตให้continue
ใช้ในrepeat ... until
รูปแบบของการวนซ้ำ จนถึงขณะนี้ยังไม่มีกรณีการใช้ที่น่าสนใจเพียงพอที่จะรวมไว้ในภาษา
โดยทั่วไปแล้วการหลีกcontinue
เลี่ยงเงื่อนไขที่จะทำให้ a ถูกดำเนินการและรวบรวมส่วนที่เหลือของลูปบอดี้ภายใต้เงื่อนไขนั้น ดังนั้นการวนซ้ำต่อไปนี้
-- not valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
if isstring(k) then continue end
-- do something to t[k] when k is not a string
end
สามารถเขียนได้
-- valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
end
มีความชัดเจนเพียงพอและมักจะไม่เป็นภาระเว้นแต่คุณจะมีชุดของเครื่องมือที่ซับซ้อนซึ่งควบคุมการทำงานของลูป
until...
คือผมคาดว่าจะมีข้อผิดพลาดที่ไม่ถูกผูกตัวแปรท้องถิ่นในกรณีของการเข้าถึง
goto
เข้าสู่ Lua 5.2 ธรรมชาติgoto
มีปัญหาเดียวกัน ในที่สุดพวกเขาตัดสินใจว่าสิ่งที่รันไทม์และ / หรือค่าใช้จ่ายในการสร้างรหัสเพื่อป้องกันมันมีมูลค่าประโยชน์ของการมีความยืดหยุ่นgoto
ที่สามารถนำมาใช้เพื่อเลียนแบบทั้งสองและหลายระดับcontinue
break
คุณจะต้องค้นหารายการใน Lua เพื่อดูรายการที่เกี่ยวข้องเพื่อรับรายละเอียด เนื่องจากพวกเขาแนะนำตัวgoto
มันจึงไม่สามารถเอาชนะได้อย่างแน่นอน
local
คอมไพเลอร์เป็นเพียงคำสั่งเท่านั้น - ไม่สำคัญว่าการทำลายรันไทม์ระหว่างlocal
และการใช้ตัวแปรคืออะไรคุณไม่จำเป็นต้องเปลี่ยนแปลงอะไรในคอมไพเลอร์เพื่อรักษาพฤติกรรมการกำหนดขอบเขตเดียวกัน ใช่สิ่งนี้อาจไม่ชัดเจนและต้องการเอกสารเพิ่มเติมบางส่วน แต่หากต้องการย้ำอีกครั้งจำเป็นต้องมีการเปลี่ยนแปลงค่าศูนย์ในคอมไพเลอร์ repeat do break end until true
ตัวอย่างเช่นในคำตอบของฉันแล้วสร้างตรง bytecode เดียวกันกับที่คอมไพเลอร์จะมีการดำเนินการต่อความแตกต่างเพียงอย่างเดียวคือว่าcontinue
คุณจะไม่จำเป็นต้องไวยากรณ์พิเศษที่น่าเกลียดที่จะใช้มัน
do{int i=0;}while (i == 0);
ล้มเหลวหรือใน C ++: do int i=0;while (i==0);
ยังล้มเหลว ("ไม่ได้ประกาศในขอบเขตนี้") สายเกินไปที่จะเปลี่ยนว่าตอนนี้ใน Lua น่าเสียดาย
คุณสามารถล้อมร่างกายห่วงเพิ่มเติมrepeat until true
แล้วใช้do break end
ภายในเพื่อผลของการดำเนินการต่อ โดยปกติคุณจะต้องตั้งค่าสถานะเพิ่มเติมหากคุณตั้งใจจะbreak
วนซ้ำเช่นกัน
ซึ่งจะวนซ้ำ 5 ครั้งพิมพ์ 1, 2 และ 3 ในแต่ละครั้ง
for idx = 1, 5 do
repeat
print(1)
print(2)
print(3)
do break end -- goes to next iteration of for
print(4)
print(5)
until true
end
สิ่งปลูกสร้างนี้แปลเป็น opcode JMP
ใน Lua bytecode อย่างแท้จริง!
$ luac -l continue.lua
main <continue.lua:0,0> (22 instructions, 88 bytes at 0x23c9530)
0+ params, 6 slots, 0 upvalues, 4 locals, 6 constants, 0 functions
1 [1] LOADK 0 -1 ; 1
2 [1] LOADK 1 -2 ; 3
3 [1] LOADK 2 -1 ; 1
4 [1] FORPREP 0 16 ; to 21
5 [3] GETGLOBAL 4 -3 ; print
6 [3] LOADK 5 -1 ; 1
7 [3] CALL 4 2 1
8 [4] GETGLOBAL 4 -3 ; print
9 [4] LOADK 5 -4 ; 2
10 [4] CALL 4 2 1
11 [5] GETGLOBAL 4 -3 ; print
12 [5] LOADK 5 -2 ; 3
13 [5] CALL 4 2 1
14 [6] JMP 6 ; to 21 -- Here it is! If you remove do break end from code, result will only differ by this single line.
15 [7] GETGLOBAL 4 -3 ; print
16 [7] LOADK 5 -5 ; 4
17 [7] CALL 4 2 1
18 [8] GETGLOBAL 4 -3 ; print
19 [8] LOADK 5 -6 ; 5
20 [8] CALL 4 2 1
21 [1] FORLOOP 0 -17 ; to 5
22 [10] RETURN 0 1
luac
การส่งออกในดังนั้น!
ความกังวลหลักของเรากับ "ดำเนินการต่อ" คือมีโครงสร้างการควบคุมอื่น ๆ หลายอย่างที่ (ในมุมมองของเรา) มีความสำคัญไม่มากไปกว่า "ดำเนินการต่อ" และอาจแทนที่ได้ (เช่นแตกกับเลเบล [ใน Java] หรือแม้แต่ goto ทั่วไป) "ดำเนินการต่อ" ดูเหมือนจะไม่พิเศษกว่ากลไกโครงสร้างการควบคุมอื่น ๆ ยกเว้นว่ามีอยู่ในภาษาอื่น ๆ (Perl จริง ๆ มีสองข้อความ "ดำเนินการต่อ", "ถัดไป" และ "ทำซ้ำ" ทั้งสองมีประโยชน์)
continue
Lua ขอโทษ"
ส่วนแรกจะได้รับการตอบในคำถามที่พบบ่อยตามที่สังหารชี้ให้เห็น
สำหรับวิธีแก้ปัญหาคุณสามารถพันร่างของลูปในฟังก์ชันและreturn
ก่อนหน้านั้นได้เช่น
-- Print the odd numbers from 1 to 99
for a = 1, 99 do
(function()
if a % 2 == 0 then
return
end
print(a)
end)()
end
หรือถ้าคุณต้องการทั้งฟังก์ชั่นbreak
และcontinue
ฟังก์ชั่นให้ฟังก์ชั่นท้องถิ่นทำการทดสอบเช่น
local a = 1
while (function()
if a > 99 then
return false; -- break
end
if a % 2 == 0 then
return true; -- continue
end
print(a)
return true; -- continue
end)() do
a = a + 1
end
collectgarbage("count")
แม้หลังจากพยายาม 100 ครั้งง่ายๆแล้วเราจะคุยกัน การเพิ่มประสิทธิภาพ "ก่อนกำหนด" ดังกล่าวช่วยให้โครงการไฮโหลดหนึ่งโครงการไม่สามารถรีบูตได้ทุกนาทีในสัปดาห์ที่แล้ว
ฉันไม่เคยใช้ Lua มาก่อน แต่ฉัน Googled และมากับสิ่งนี้:
ตรวจสอบคำถาม 1.26
นี่คือการร้องเรียนทั่วไป ผู้เขียน Lua รู้สึกว่าการดำเนินการต่อเป็นเพียงหนึ่งในกลไกการควบคุมการไหลที่เป็นไปได้ใหม่จำนวนหนึ่ง (ความจริงที่ว่ามันไม่สามารถทำงานกับกฎขอบเขตการทำซ้ำ / จนกระทั่งเป็นปัจจัยรอง)
ใน Lua 5.2 มีคำสั่ง goto ซึ่งสามารถใช้ในการทำงานเดียวกันได้อย่างง่ายดาย
เราสามารถทำได้ดังนี้ด้านล่างมันจะข้ามตัวเลข
local len = 5
for i = 1, len do
repeat
if i%2 == 0 then break end
print(" i = "..i)
break
until true
end
O / P:
i = 1
i = 3
i = 5
เราพบสถานการณ์นี้หลายครั้งและเราก็ใช้การตั้งค่าสถานะเพื่อจำลองการดำเนินการต่อ เราพยายามหลีกเลี่ยงการใช้คำสั่ง goto เช่นกัน
ตัวอย่าง: รหัสตั้งใจพิมพ์คำสั่งจาก i = 1 ถึง i = 10 ยกเว้น i = 3 นอกจากนี้ยังพิมพ์ "loop start", loop end "," if start "และ" if end "เพื่อจำลองคำสั่งที่ซ้อนกันอื่น ๆ ที่มีอยู่ในรหัสของคุณ
size = 10
for i=1, size do
print("loop start")
if whatever then
print("if start")
if (i == 3) then
print("i is 3")
--continue
end
print(j)
print("if end")
end
print("loop end")
end
ทำได้โดยการแนบคำแถลงที่เหลือทั้งหมดจนกระทั่งขอบเขตสิ้นสุดของลูปด้วยแฟล็กทดสอบ
size = 10
for i=1, size do
print("loop start")
local continue = false; -- initialize flag at the start of the loop
if whatever then
print("if start")
if (i == 3) then
print("i is 3")
continue = true
end
if continue==false then -- test flag
print(j)
print("if end")
end
end
if (continue==false) then -- test flag
print("loop end")
end
end
ฉันไม่ได้บอกว่านี่เป็นวิธีการที่ดีที่สุด แต่ใช้ได้ดีกับเรา
Lua เป็นภาษาสคริปต์ที่มีน้ำหนักเบาซึ่งต้องการเล็กลงที่สุด ตัวอย่างเช่นการดำเนินการ unary จำนวนมากเช่นการเพิ่มก่อน / หลังไม่พร้อมใช้งาน
แทนที่จะดำเนินการต่อคุณสามารถใช้ข้ามเช่น
arr = {1,2,3,45,6,7,8}
for key,val in ipairs(arr) do
if val > 6 then
goto skip_to_next
end
# perform some calculation
::skip_to_next::
end
คุณสามารถใช้รหัสต่อไปนี้อีกครั้งด้วยการสลับกลับ:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
เพราะมันไม่จำเป็น¹ มีสถานการณ์น้อยมากที่ผู้พัฒนาต้องการมัน
A) เมื่อคุณมีวงวนที่ง่ายมากให้พูด 1 หรือ 2 ซับจากนั้นคุณก็สามารถหมุนสภาพลูปไปรอบ ๆ และมันยังคงอ่านได้มากมาย
B) เมื่อคุณกำลังเขียนโค้ดขั้นตอนง่าย ๆ (aka วิธีที่เราเขียนโค้ดในศตวรรษที่ผ่านมา) คุณควรใช้การเขียนโปรแกรมแบบมีโครงสร้าง (aka. วิธีที่เราเขียนโค้ดที่ดีกว่าในศตวรรษที่ผ่านมา)
C) ถ้าคุณกำลังเขียนโค้ดเชิงวัตถุร่างกายลูปของคุณควรประกอบด้วยการเรียกใช้วิธีไม่เกินหนึ่งหรือสองวิธีเว้นแต่ว่ามันจะสามารถแสดงในหนึ่งหรือสองซับ (ในกรณีนี้ดู A)
D) หากคุณกำลังเขียนโค้ดที่ใช้งานได้ให้ส่งค่าโทรกลับแบบธรรมดาสำหรับการทำซ้ำครั้งถัดไป
กรณีเดียวเมื่อคุณต้องการใช้continue
คำหลักคือถ้าคุณต้องการให้รหัส Lua เหมือนกับเป็นงูหลามซึ่งเป็นเพียงไม่ได้²
ยกเว้น A) ใช้ซึ่งในกรณีนี้ไม่จำเป็นต้องแก้ไขปัญหาใด ๆ คุณควรทำการเขียนโปรแกรมแบบโครงสร้างเชิงวัตถุหรือเชิงฟังก์ชัน นี่คือกระบวนทัศน์ที่ Lua สร้างขึ้นดังนั้นคุณจะต้องต่อสู้กับภาษาถ้าคุณพยายามหลีกเลี่ยงรูปแบบของพวกเขา”
ชี้แจงบางส่วน:
¹ Lua เป็นภาษาที่เรียบง่ายมาก มันพยายามที่จะมีคุณสมบัติน้อยที่สุดเท่าที่จะทำได้และcontinue
แถลงการณ์ไม่ใช่คุณสมบัติที่สำคัญในแง่นั้น
ฉันคิดว่าปรัชญาของความเรียบง่ายนี้ถูกจับได้เป็นอย่างดีโดยRoberto Ierusalimschyในการสัมภาษณ์ปี 2019นี้:
เพิ่มนั้นและนั่นและที่นำออกและในท้ายที่สุดเราเข้าใจข้อสรุปสุดท้ายจะไม่ตอบสนองคนส่วนใหญ่และเราจะไม่ใส่ตัวเลือกทั้งหมดที่ทุกคนต้องการดังนั้นเราจึงไม่ใส่อะไรเลย ในที่สุดโหมดที่เข้มงวดคือการประนีประนอมที่เหมาะสม
²ดูเหมือนว่ามีโปรแกรมเมอร์จำนวนมากที่มาที่ Lua จากภาษาอื่น ๆ เพราะโปรแกรมใดก็ตามที่พวกเขากำลังพยายามที่จะเขียนสคริปต์เพื่อใช้งานและหลายคนต้องการดูเหมือนจะไม่ต้องการเขียนอะไรนอกจากภาษาของพวกเขา ตัวเลือกซึ่งนำไปสู่คำถามมากมายเช่น "ทำไม Lua จึงไม่มีฟีเจอร์ X"
Matzอธิบายสถานการณ์ที่คล้ายกันกับ Ruby ในการสัมภาษณ์เมื่อเร็ว ๆ นี้ :
คำถามที่ได้รับความนิยมมากที่สุดคือ: "ฉันมาจากชุมชนภาษา X คุณไม่สามารถแนะนำคุณสมบัติจากภาษา X ถึง Ruby ได้หรือไม่" หรืออะไรทำนองนั้น และคำตอบปกติของฉันสำหรับคำขอเหล่านี้คือ ... "ไม่ฉันจะไม่ทำอย่างนั้น" เพราะเรามีการออกแบบภาษาที่แตกต่างกันและนโยบายการพัฒนาภาษาที่แตกต่างกัน
³มีหลายวิธีในการแฮ็กข้อมูลของคุณ ผู้ใช้บางคนแนะนำให้ใช้goto
ซึ่งเป็นสิ่งที่ดีพอในกรณีส่วนใหญ่ แต่ก็น่าเกลียดมากอย่างรวดเร็วและแยกออกเป็นสองกลุ่มด้วยลูปซ้อนกัน การใช้goto
s ยังทำให้คุณตกอยู่ในอันตรายจากการมีสำเนาของ SICP ที่ส่งถึงคุณเมื่อใดก็ตามที่คุณแสดงรหัสของคุณให้กับบุคคลอื่น
continue
อาจจะเป็นคุณลักษณะที่สะดวก แต่นั่นก็ไม่ได้ทำให้มันจำเป็น ผู้คนจำนวนมากใช้ Lua ไม่เป็นไรดังนั้นจึงไม่มีกรณีใดที่จะเป็นสิ่งอื่นนอกเหนือจากคุณสมบัติที่เป็นระเบียบซึ่งไม่จำเป็นสำหรับการเขียนโปรแกรมภาษาใด ๆ
goto
คำสั่งที่สามารถนำไปใช้เพื่อดำเนินการต่อได้ ดูคำตอบด้านล่าง