2009-06-18 33 views
5

Tôi là người dẫn đầu cho Bitfighter và chúng tôi đang sử dụng Lua làm ngôn ngữ kịch bản để cho phép người chơi lập trình các rô-bốt tùy chỉnh của riêng họ.Khai báo các biến và phạm vi câu hỏi cho Lua

Trong Lua, bạn không cần khai báo biến, và tất cả biến mặc định phạm vi toàn cầu, trừ khi được khai báo khác. Điều này dẫn đến một số vấn đề. Đi đoạn sau, ví dụ:

loc = bot:getLoc() 
items = bot:findItems(ShipType)  -- Find a Ship 

minDist = 999999 
found = false 

for indx, item in ipairs(items) do   
    local d = loc:distSquared(item:getLoc()) 

    if(d < minDist) then 
     closestItem = item 
     minDist = d 
    end 
end 

if(closestItem != nil) then 
    firingAngle = getFiringSolution(closestItem) 
end 

Trong đoạn này, nếu findItems() trả về không có ứng cử viên, sau đó closestItem sẽ vẫn tham khảo bất cứ tàu nó tìm thấy thời gian qua xung quanh, và trong thời gian can thiệp, con tàu có thể đã bị giết. Nếu con tàu bị giết, nó không còn tồn tại nữa, và getFiringSolution() sẽ thất bại.

Bạn có phát hiện sự cố không? Vâng, không phải người dùng của tôi cũng vậy. Đó là tinh tế, nhưng với hiệu ứng ấn tượng.

Một giải pháp sẽ là yêu cầu tất cả các biến được khai báo và cho tất cả các biến được đặt mặc định thành phạm vi cục bộ. Mặc dù sự thay đổi đó sẽ không làm cho các lập trình viên không thể tham chiếu đến các đối tượng không còn tồn tại, nó sẽ làm cho việc vô tình trở nên khó khăn hơn.

Có cách nào để yêu cầu Lua đặt mặc định tất cả các biến thành phạm vi địa phương và/hoặc yêu cầu chúng được khai báo không? Tôi biết một số ngôn ngữ khác (ví dụ: Perl) có tùy chọn này.

Cảm ơn!


Rất nhiều câu trả lời hay tại đây, cảm ơn!

Tôi đã quyết định sử dụng phiên bản sửa đổi nhỏ của mô-đun Lua 'nghiêm ngặt'. Điều này dường như đưa tôi đến nơi tôi muốn đi, và tôi sẽ hack nó một chút để cải thiện các thông điệp và làm cho chúng phù hợp hơn với bối cảnh cụ thể của tôi.

+0

BTW, bạn có bất kỳ lý do chính đáng nào để đặt thêm dấu ngoặc ôm trong câu lệnh if của bạn không? –

+0

Vâng, trước khi bạn đặt ra câu hỏi tôi đã trả lời rằng họ đã được yêu cầu. Nhưng bây giờ tôi phải trả lời rằng bỏ qua các parens chỉ có vẻ sai với tôi! – Watusimoto

+2

Đó là sở thích cá nhân của khóa học. Nhưng điều quan trọng cần nhớ là Lua không phải là C hay Pascal hay gì cả. Lua là Lua và sử dụng nó một cách hiệu quả, bạn phải sử dụng nó như nó là, không phải là nếu nó là một số người nghèo thay thế cho ngôn ngữ lập trình khác. Tôi đã tìm thấy rằng "cú pháp có vẻ sai" những điều giúp đưa Lua vào một phần bộ não của tôi khác biệt với C++ và các ngôn ngữ khác mà tôi biết. Tóm lại, ý kiến ​​của tôi là: nếu nó được viết bằng Lua, hãy viết nó theo cách Lua! :-) –

Trả lời

3

Không có tùy chọn để đặt hành vi này, nhưng có một mô-đun 'nghiêm ngặt' được cung cấp với cài đặt chuẩn, thực hiện chính xác điều đó (bằng cách sửa đổi bảng meta). Cách sử dụng: yêu cầu 'nghiêm ngặt'

Để biết thêm thông tin chuyên sâu và các giải pháp khác: http://lua-users.org/wiki/DetectingUndefinedVariables, nhưng tôi khuyên bạn nên 'nghiêm ngặt'.

+0

Điều này có vẻ là câu trả lời đơn giản nhất và là điều tôi đang tìm kiếm.Tôi muốn hiểu rõ hơn các giải pháp khác được đề xuất ở đây để xem liệu có bất kỳ hạn chế nào khi sử dụng 'nghiêm ngặt' hay không. – Watusimoto

2

Sorta.

Ở Lua, các hình cầu nói chung sống trong bảng toàn cầu _G (thực tế phức tạp hơn một chút, nhưng từ phía Lua không có cách nào để nói với AFAIK). Giống như tất cả các bảng Lua khác, có thể đính kèm một số __newindex có thể metatable vào _G để kiểm soát cách các biến được thêm vào nó. Hãy để trình xử lý __newindex này làm bất cứ điều gì bạn muốn làm khi ai đó tạo ra toàn cầu: ném lỗi, cho phép nhưng in cảnh báo, v.v.

Để can thiệp với _G, đơn giản nhất và sạch nhất để sử dụng setfenv. Xem documentation.

0

Thực ra, biến toàn cục thừa với tham chiếu cũ cho tàu sẽ đủ để giữ cho GC không được loại bỏ đối tượng. Vì vậy, nó có thể được phát hiện tại thời gian chạy bằng cách nhận thấy rằng con tàu bây giờ đã "chết" và từ chối làm bất cứ điều gì với nó. Nó vẫn không phải là con tàu đúng, nhưng ít nhất bạn cũng không sụp đổ.

Một điều bạn có thể làm là giữ tập lệnh người dùng trong một sandbox, có thể là một hộp cát trên mỗi tập lệnh.Với thao tác bên phải của bảng môi trường sandbox hoặc metatable của nó, bạn có thể sắp xếp để loại bỏ tất cả hoặc hầu hết các biến toàn cục khỏi sandbox trước (hoặc ngay sau) gọi mã của người dùng.

Dọn dẹp hộp cát sau khi cuộc gọi sẽ có lợi thế là loại bỏ các tham chiếu bổ sung cho những thứ không được treo xung quanh. Điều này có thể được thực hiện bằng cách giữ một danh sách trắng của các lĩnh vực được phép ở lại trong môi trường, và xóa tất cả các phần còn lại.

Ví dụ: thao tác sau thực hiện cuộc gọi có hộp cát đến chức năng do người dùng cung cấp với môi trường chỉ chứa các tên được liệt kê màu trắng phía sau bảng cào mới được cung cấp cho mỗi cuộc gọi.

 
-- table of globals that will available to user scripts 
local user_G = { 
     print=_G.print, 
     math=_G.math, 
     -- ... 
    } 
-- metatable for user sandbox 
local env_mt = { __index=user_G } 


-- call the function in a sandbox with an environment in which new global 
-- variables can be created and modified but they will be discarded when the 
-- user code completes. 
function doUserCode(user_code, ...) 
    local env = setmetatable({}, env_mt) -- create a fresh user environment with RO globals 
    setfenv(user_code, env)  -- hang it on the user code 
    local results = {pcall(user_code, ...)} 
    setfenv(user_code,{}) 
    return unpack(results) 
end 

Điều này có thể được mở rộng để làm cho bảng toàn cầu chỉ đọc bằng cách đẩy nó trở lại sau một truy cập có thể truy cập khác nếu bạn muốn. Lưu ý rằng một giải pháp sandbox hoàn chỉnh cũng sẽ xem xét việc cần làm về mã người dùng vô tình (hoặc độc hại) thực thi vòng lặp vô hạn (hoặc chỉ đơn thuần là rất dài) hoặc hoạt động khác. Các giải pháp chung cho vấn đề này là chủ đề thảo luận không thường xuyên trên Lua list, nhưng các giải pháp tốt là rất khó.

+0

Vấn đề với tham chiếu đến đối tượng tàu chết là tuổi thọ của đối tượng được điều khiển bởi chương trình chính C++, do đó đối tượng có thể bị xóa mà không có sự đồng ý hoặc kiểm soát của Lua. Lua có một tham chiếu đến một con trỏ userdata, mà đột nhiên trỏ vào một đối tượng không hợp lệ. Làm thế nào tôi có thể phát hiện ra điều đó? Tôi cần phải có một xử lý tốt hơn về điều này, và sẽ được mở cho bất kỳ lời đề nghị. Tôi đang làm việc dọc theo các dòng của một hộp cát, nhưng tôi muốn cho phép tạo các biến toàn cục miễn là chúng được tạo cố ý và cố ý. Tất cả các đề xuất trên dường như thực hiện điều đó. – Watusimoto

2

"Địa phương theo mặc định là sai". Xin vui lòng xem

http://lua-users.org/wiki/LocalByDefault

http://lua-users.org/wiki/LuaScopingDiscussion

Bạn cần phải sử dụng một số loại bảo vệ môi trường toàn cầu. Có một số công cụ tĩnh để làm điều đó (không quá trưởng thành), nhưng giải pháp phổ biến nhất là sử dụng bảo vệ thời gian chạy, dựa trên __index__newindex trong mục đích của _G.

Shameles cắm: Trang này cũng có thể hữu ích:

http://code.google.com/p/lua-alchemy/wiki/LuaGlobalEnvironmentProtection

Lưu ý rằng trong khi nó thảo luận về Lua nhúng vào swf, các kỹ thuật được mô tả (xem sources) làm việc cho generic Lua. Chúng tôi sử dụng thứ gì đó dọc theo các dòng này trong mã sản xuất của chúng tôi tại nơi làm việc.

+1

Địa phương theo mặc định là (có thể cho là) ​​sai, nhưng sau đó như vậy là toàn cầu theo mặc định! Tôi đã xem qua mã bạn đã trỏ đến và có vẻ như nó hoạt động rất giống với mô-đun 'nghiêm ngặt' được đề xuất ở trên. Chúng có khác biệt trong bất kỳ cách thức quan trọng nào không? – Watusimoto

+0

Toàn cầu theo mặc định là sai, nhưng đó là di sản Lua và hầu như không thể thay đổi (cũng rất hữu ích khi viết mini-DSL ở Lua). –

+0

Sự khác biệt chính (giữa các mô-đun nghiêm ngặt) là của tôi cố gắng để buộc khai báo rõ ràng và súc tích các biến toàn cầu (như tôi thấy chúng như những điều tà ác được loại bỏ trong mã của tôi - bao gồm các hàm toàn cục). Lua của etc/strict.lua là một chút ít rõ ràng. Nhưng nó là một sở thích cá nhân. –

Các vấn đề liên quan