Câu trả lời:
Trong Lua 5.2, cách giải quyết tốt nhất là sử dụng goto:
-- prints odd numbers in [|1,10|]
for i=1,10 do
if i % 2 == 0 then goto continue end
print(i)
::continue::
end
Điều này được hỗ trợ trong LuaJIT kể từ phiên bản 2.0.1
continue
ngày thực tế . Sự goto
thay thế trông không đẹp lắm và cần nhiều đường hơn. Ngoài ra, điều đó sẽ không gây rắc rối nếu bạn có nhiều hơn một vòng lặp thực hiện điều này trong một chức năng, cả hai đều có ::continue::
? Tạo một tên trên mỗi vòng lặp có vẻ không phải là một việc nên làm.
Cách mà ngôn ngữ quản lý phạm vi từ vựng tạo ra các vấn đề bao gồm cả goto
và continue
. Ví dụ,
local a=0
repeat
if f() then
a=1 --change outer a
end
local a=f() -- inner a
until a==0 -- test inner a
Khai báo local a
bên trong thân vòng lặp che dấu biến bên ngoài được đặt tên a
và phạm vi của địa phương đó mở rộng qua điều kiện của until
câu lệnh để điều kiện này được kiểm tra trong cùng a
.
Nếu continue
tồn tại, nó sẽ phải bị hạn chế về mặt ngữ nghĩa để chỉ có giá trị sau khi tất cả các biến được sử dụng trong điều kiện đã đi vào phạm vi. Đây là một điều kiện khó để ghi lại cho người dùng và thực thi trong trình biên dịch. Đề xuất khác nhau xung quanh vấn đề này đã được thảo luận, trong đó có câu trả lời đơn giản không cho phép continue
với repeat ... until
phong cách của vòng lặp. Cho đến nay, không ai có trường hợp sử dụng đủ hấp dẫn để đưa chúng vào ngôn ngữ.
Công việc xung quanh nói chung là đảo ngược điều kiện sẽ khiến cho một continue
lệnh được thực thi và thu thập phần còn lại của thân vòng lặp trong điều kiện đó. Vì vậy, vòng lặp sau đây
-- 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
có thể được viết
-- 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
Nó là đủ rõ ràng, và thường không phải là một gánh nặng trừ khi bạn có một loạt các culls phức tạp kiểm soát hoạt động vòng lặp.
until...
.
goto
vào Lua 5.2. Đương nhiên, goto
có cùng một vấn đề. Cuối cùng họ đã quyết định rằng bất cứ chi phí nào trong thời gian chạy và / hoặc mã để bảo vệ chống lại nó đều có giá trị lợi ích của việc có một linh hoạt goto
có thể được sử dụng để mô phỏng cả hai continue
và đa cấp break
. Bạn sẽ phải tìm kiếm tài liệu lưu trữ danh sách Lua cho các chủ đề liên quan để có được thông tin chi tiết. Vì họ đã giới thiệu goto
, rõ ràng nó không thể vượt qua.
local
là chỉ thị của trình biên dịch - không có vấn đề gì về thời gian chạy giữa local
và sử dụng biến - bạn không cần thay đổi bất cứ điều gì trong trình biên dịch để duy trì hành vi phạm vi tương tự. Vâng, điều này có thể không quá rõ ràng và cần một số tài liệu bổ sung, nhưng, để nhắc lại một lần nữa, nó yêu cầu thay đổi ZERO trong trình biên dịch. repeat do break end until true
ví dụ trong câu trả lời của tôi đã tạo ra chính xác cùng mã byte mà trình biên dịch sẽ tiếp tục, sự khác biệt duy nhất là với continue
bạn sẽ không cần thêm cú pháp xấu xí để sử dụng nó.
do{int i=0;}while (i == 0);
fail hoặc trong C ++: do int i=0;while (i==0);
cũng fail ("không được khai báo trong phạm vi này"). Quá muộn để thay đổi điều đó bây giờ ở Lua, thật không may.
Bạn có thể bọc thân vòng lặp bổ sung repeat until true
và sau đó sử dụng do break end
bên trong để tiếp tục. Đương nhiên, bạn sẽ cần thiết lập các cờ bổ sung nếu bạn cũng có ý định thực sự break
thoát khỏi vòng lặp.
Điều này sẽ lặp 5 lần, in 1, 2 và 3 mỗi lần.
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
Cấu trúc này thậm chí còn chuyển thành một opcode theo nghĩa đen JMP
trong 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
đầu ra trên SO! Có một upvote xứng đáng :)
Trực tiếp từ chính nhà thiết kế của Lua :
Mối quan tâm chính của chúng tôi với "tiếp tục" là có một số cấu trúc điều khiển khác (theo quan điểm của chúng tôi) ít nhiều quan trọng như "tiếp tục" và thậm chí có thể thay thế nó. (Ví dụ: ngắt với các nhãn [như trong Java] hoặc thậm chí là một goto chung hơn.) "Tiếp tục" dường như không đặc biệt hơn các cơ chế cấu trúc điều khiển khác, ngoại trừ việc nó có trong nhiều ngôn ngữ. (Perl thực sự có hai câu lệnh "tiếp tục", "tiếp theo" và "làm lại". Cả hai đều hữu ích.)
continue
vào Lua, xin lỗi."
Phần thứ nhất được trả lời trong câu hỏi thường gặp khi bị giết ra nhọn.
Đối với một cách giải quyết, bạn có thể bọc phần thân của vòng lặp trong một hàm và return
từ đó, ví dụ:
-- 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
Hoặc nếu bạn muốn cả hai break
và continue
chức năng, hãy để chức năng cục bộ thực hiện kiểm tra, ví dụ:
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")
ngay cả sau 100 lần thử đơn giản của bạn và sau đó chúng tôi sẽ nói chuyện. Tối ưu hóa "sớm" như vậy đã lưu một dự án tải trọng cao từ việc khởi động lại mỗi phút vào tuần trước.
Tôi chưa bao giờ sử dụng Lua trước đây, nhưng tôi đã lấy nó và nghĩ ra điều này:
Kiểm tra câu hỏi 1.26 .
Đây là một khiếu nại phổ biến. Các tác giả Lua cảm thấy rằng tiếp tục chỉ là một trong một số cơ chế dòng điều khiển mới có thể (thực tế là nó không thể hoạt động với các quy tắc phạm vi lặp lại / cho đến khi là một yếu tố phụ.)
Trong Lua 5.2, có một câu lệnh goto có thể dễ dàng sử dụng để thực hiện cùng một công việc.
Chúng tôi đã gặp kịch bản này nhiều lần và chúng tôi chỉ cần sử dụng một lá cờ để mô phỏng tiếp tục. Chúng tôi cũng cố gắng tránh sử dụng các câu lệnh goto.
Ví dụ: Mã dự định in các câu lệnh từ i = 1 đến i = 10 ngoại trừ i = 3. Ngoài ra, nó cũng in "vòng lặp bắt đầu", kết thúc vòng lặp "," nếu bắt đầu "và" nếu kết thúc "để mô phỏng các câu lệnh lồng nhau khác tồn tại trong mã của bạn.
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
đạt được bằng cách kèm theo tất cả các câu lệnh còn lại cho đến phạm vi kết thúc của vòng lặp với cờ kiểm tra.
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
Tôi không nói rằng đây là cách tiếp cận tốt nhất nhưng nó hoạt động hoàn hảo với chúng tôi.
Một lần nữa với đảo ngược, bạn chỉ cần sử dụng mã sau đây:
for k,v in pairs(t) do
if not isstring(k) then
-- do something to t[k] when k is not a string
end
Lua là ngôn ngữ kịch bản nhẹ mà muốn nhỏ hơn có thể. Ví dụ: nhiều thao tác đơn phương như gia tăng trước / sau không khả dụng
Thay vì tiếp tục, bạn có thể sử dụng goto như
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
Bởi vì nó không cần thiết¹. Có rất ít tình huống mà một nhà phát triển sẽ cần nó.
A) Khi bạn có một vòng lặp rất đơn giản, hãy nói 1 hoặc 2 lớp, sau đó bạn có thể chỉ cần xoay điều kiện vòng lặp và nó vẫn có thể đọc được.
B) Khi bạn viết mã thủ tục đơn giản (hay còn gọi là cách chúng tôi viết mã trong thế kỷ trước), bạn cũng nên áp dụng lập trình có cấu trúc (hay còn gọi là cách chúng tôi viết mã tốt hơn trong thế kỷ trước)
C) Nếu bạn đang viết mã hướng đối tượng, thân vòng lặp của bạn sẽ bao gồm không quá một hoặc hai lệnh gọi phương thức trừ khi nó có thể được biểu thị trong một hoặc hai lớp (trong trường hợp này, xem A)
D) Nếu bạn đang viết mã chức năng, chỉ cần trả về một cuộc gọi đuôi đơn giản cho lần lặp tiếp theo.
Trường hợp duy nhất khi bạn muốn sử dụng một continue
từ khóa là nếu bạn muốn mã Lua giống như nó là python, mà nó không phải là .²
Trừ khi A) áp dụng, trong trường hợp không cần bất kỳ cách giải quyết nào, bạn nên thực hiện lập trình Cấu trúc, Hướng đối tượng hoặc Chức năng. Đó là những mô hình mà Lua được xây dựng cho, vì vậy bạn sẽ chiến đấu chống lại ngôn ngữ nếu bạn tránh đường để tránh những khuôn mẫu của chúng .³
Một số làm rõ:
Lua là một ngôn ngữ rất tối giản. Nó cố gắng có ít tính năng nhất có thể, và một continue
tuyên bố không phải là một tính năng thiết yếu theo nghĩa đó.
Tôi nghĩ rằng triết lý tối giản này được nắm bắt tốt bởi Roberto Ierusalimschy trong cuộc phỏng vấn năm 2019 này :
thêm điều đó và điều đó và điều đó, đưa nó ra, và cuối cùng chúng tôi hiểu kết luận cuối cùng sẽ không làm hài lòng hầu hết mọi người và chúng tôi sẽ không đưa ra tất cả các tùy chọn mà mọi người muốn, vì vậy chúng tôi không đưa ra bất cứ điều gì. Cuối cùng, chế độ nghiêm ngặt là một sự thỏa hiệp hợp lý.
² Dường như có rất nhiều lập trình viên đến Lua từ các ngôn ngữ khác bởi vì bất kỳ chương trình nào họ đang cố gắng viết kịch bản để sử dụng nó, và nhiều người trong số họ muốn dường như không muốn viết bất cứ điều gì ngoài ngôn ngữ của họ sự lựa chọn, dẫn đến nhiều câu hỏi như "Tại sao Lua không có tính năng X?"
Matz đã mô tả một tình huống tương tự với Ruby trong một cuộc phỏng vấn gần đây :
Câu hỏi phổ biến nhất là: "Tôi đến từ cộng đồng ngôn ngữ X, bạn không thể giới thiệu một tính năng từ ngôn ngữ X sang Ruby?", Hoặc đại loại như thế. Và câu trả lời thông thường của tôi cho những yêu cầu này là "không, tôi sẽ không làm điều đó", bởi vì chúng tôi có thiết kế ngôn ngữ khác nhau và các chính sách phát triển ngôn ngữ khác nhau.
Có một số cách để hack theo cách này; Một số người dùng đã đề xuất sử dụng goto
, đó là một sự ước lượng đủ tốt trong hầu hết các trường hợp, nhưng lại rất xấu xí rất nhanh và phá vỡ hoàn toàn với các vòng lặp lồng nhau. Việc sử dụng goto
cũng khiến bạn gặp nguy hiểm khi có một bản sao SICP ném vào bạn bất cứ khi nào bạn hiển thị mã của mình cho bất kỳ ai khác.
continue
có thể là một tính năng tiện lợi, nhưng điều đó không cần thiết . Rất nhiều người sử dụng Lua tốt mà không có nó, vì vậy thực sự không có trường hợp nào khác ngoài tính năng gọn gàng không cần thiết cho bất kỳ Ngôn ngữ lập trình nào.
goto
tuyên bố có thể được sử dụng để tiếp tục thực hiện. Xem câu trả lời dưới đây.