我很無聊,看到這個問題(再次)以及一個副本。 我開始寫爲了好玩一些代碼,導致此:
local function getNeededVars(tab,func)
local needed,this = {}
this = setmetatable({},{
__index = function(s,k)
-- See if the requested variable exists.
-- If it doesn't, we obviously complain.
-- If it does, we log it and return the value.
local var = tab.vars[k]
if not var then
error("Eh, "..k.." isn't registered (yet?)",5)
end needed[k] = true return tab.vals[k]
end;
}) func(this) return needed
end
local function updateStuff(self,key,done)
for k,v in pairs(self.levars) do
if v.needed and v.needed[key] then
if not done[v] then done[v] = true
self.vals[v.name] = v.func(self)
updateStuff(self,v.name,done)
end
end
end
end
local function createSubTable(self,key,tab)
return setmetatable({},{
__newindex = function(s,k,v)
tab[k] = v updateStuff(self,key,{})
end; __index = tab;
})
end
local dependenceMeta
dependenceMeta = {
__index = function(self,k)
-- Allow methods, because OOP
local method = dependenceMeta[k]
if method then return method end
local variable = self.vars[k]
if not variable then
error("Variable "..k.." not found",2)
end return self.vals[k]
end;
__newindex = function(self,k,v)
local variable = self.vars[k]
if not variable then
error("Use :Register() to add stuff",2)
elseif type(v) == "table" then
self.vals[k] = createSubTable(self,k,v)
return updateStuff(self,k,{})
end self.vals[k] = v updateStuff(self,k,{})
end
}
function dependenceMeta:Register(var,value)
local varobject = {func=value,name=var}
self.vars[var] = varobject
table.insert(self.levars,varobject)
if type(value) == "function" then
varobject.needed = getNeededVars(self,value)
self.vals[var] = value(self)
elseif type(value) == "table" then
self.vals[var] = createSubTable(self,var,value)
elseif value then
self.vals[var] = value
end
end
function dependenceMeta:RegisterAll(tab)
for k,v in pairs(tab) do
self:Register(k,v)
end
end
local function DependenceTable()
return setmetatable({
levars = {};
vars = {};
vals = {};
},dependenceMeta)
end
local test = DependenceTable()
test:Register("border",{
x=20; y=50;
height=200;
width=100;
})
test:Register("body",function(self)
return {x=self.border.x+1,y=self.border.y+1,
height=self.border.height-2,
width=self.border.width-2}
end)
test:Register("font",function(self)
local size = (self.body.height+2)-(math.floor((self.body.height+2)/4)+1);
return { size = size; -- Since we use it in the table constructor...
height = size-4; --love.graphics.setNewFont(self.font.size):getHeight();
-- I don't run this on love, so can't use the above line. Should work though.
}
end)
test:Register("padding",function(self)
local height = math.floor(self.border.height*(2/29))
return { height = height; width = height*3 } -- again dependency
end)
test:Register("text",{input=""}) -- Need this initially to keep input
test:Register("text",function(self)
return { input = self.text.input;
centerHeight = math.ceil(self.body.y+((self.body.height-self.font.height)/2));
left = self.body.x+self.padding.width+self.padding.height;
}
end)
test:Register("backspace",{key = false, rate = 3, time = 0, pausetime = 20, pause = true})
-- Again, didn't use gui.id() on the line below because my lack of LÖVE
test:Register("config",{active=true,devmode=false,debug=false,id=123,type='textbox'})
print("border.x=20, test.text.left="..test.text.left)
test.border = {x=30; y=50; height=200; width=100;}
print("border.x=30, test.text.left="..test.text.left)
test.border.x = 40
print("border.x=40, test.text.left="..test.text.left)
這是一個很大的代碼,但我喜歡寫它。它給出了這個不錯的輸出:
border.x=20, test.text.left=73
border.x=30, test.text.left=83
border.x=40, test.text.left=93
所有屬性只有在編輯其依賴項之一時纔會被重新計算。我讓它也適用於子表,這有點棘手,但最終實際上看起來很容易。您可以編輯(例如)正文字段,方法是將其設置爲一個全新的表格或在已存在的表格中設置一個字段,如代碼片段的最後幾行所示。當你將它分配給一個新表時,它會設置一個metatable。除非您使用5.2並且可以使用__pairs,否則您不能使用配對(& co)。
它可能會解決您的問題。如果不是的話,我很樂意寫它,所以至少我寫這篇文章總是積極的。 (你不得不承認,這是一些漂亮的代碼那麼,它的工作方式,而不是實際的格式。)
注意:如果你要使用它,取消對love.graphics和gui.id部分,因爲我沒有LÖVE,我顯然不得不測試代碼。
這裏是我的事情的API的快速「摘要」,因爲它可能在一開始迷惑:
local hmm = DependenceTable() -- Create a new one
print(hmm.field) -- Would error, "field" doesn't exist yet
-- Sets the property 'idk' to 123.
-- Everything except functions and tables are "primitive".
-- They're like constants, they never change unless you do it.
hmm:Register("idk",123)
-- If you want to actually set a regular table/function, you
-- can register a random value, then do hmm.idk = func/table
-- (the "constructor registering" only happens during :Register())
-- Sets the field to a constructor, which first gets validated.
-- During registering, the constructor is already called once.
-- Afterwards, it'll get called when it has to update.
-- (Whenever 'idk' changes, since 'field' depends on 'idk' here)
hmm:Register("field",function(self) return self.idk+1 end)
-- This errors because 'nonexistant' isn't reigstered yet
hmm:Register("error",function(self) return self.nonexistant end)
-- Basicly calls hmm:Register() twice with key/value as parameters
hmm:RegisterAll{
lower = function(self) return self.field - 5 end;
higher = function(self) return self.field + 5 end;
}
-- This sets the property 'idk' to 5.
-- Since 'field' depends on this property, it'll also update.
-- Since 'lower' and 'higher' depend on 'field', they too.
-- (It happens in order, so there should be no conflicts)
hmm.idk = 5
-- This prints 6 since 'idk' is 5 and 'field' is idk+1
print(hmm.field)
你可以使用setfenv(如果Lua的5.1),以去除「self.FIELD」的必要性。有了一些環境魔法,你可以使用'field'的構造函數(作爲示例)只是function() return idk+1 end
。
這裏不清楚你的意思。 「中間部分」做了哪些修改,導致事情變得「混亂」。給我們一個例子。 –
@NicolBolas我在底部舉了一個例子,我所有的變量都存儲在本地,並使用gui.get()和gui.add()等進行訪問。我的意思是在中間部分是當我例如改變body.height元素,我也想改變text.left元素,因爲它也是使用body.height定義的。有點像多米諾骨牌效應,當你影響某些事物時會崩潰,但只能在一個方向而不是所有方面。 – mhiekuru
你可以使用__newindex和一個代理表... – warspyking