2016-02-12 65 views
1

我有一個代碼,它以少量變量開始,並使用這些初始變量創建更多元素。是否可以在不製作單個功能的情況下製作摺疊變量?

function new(x, y, width, height) 
    local object = {} 
    --border 
    object.border = { x = x, y = y, width = width, height = height } 
    --body 
    object.body = { x = x+1, y = y+1, width = width-2, height = height-2 } 
    --font 
    object.font = {} 
    object.font.size = (object.body.height+2)-(math.floor((object.body.height+2)/4)+1) 
    object.font.height = love.graphics.setNewFont(object.font.size):getHeight() 
    --padding 
    object.padding = {} 
    object.padding.height = math.floor(object.border.height*(2/29)) 
    object.padding.width = object.padding.height*3 
    --text 
    object.text = { input = '' } 
    object.text.centerHeight = math.ceil(object.body.y+((object.body.height-object.font.height)/2)) 
    object.text.left = object.body.x+object.padding.width+object.padding.height 
    --backspacing 
    object.backspace = {key = false, rate = 3, time = 0, pausetime = 20, pause = true} 
    --config 
    object.config = { active = true, devmode = false, debug = false, id = gui.id(), type = 'textbox' } 
    gui.add(object) 
    return object.config.id 
end 

,當我在中間部分修改的東西,整個事情變得一團糟,因爲從一個我改變,直到底部那些值開始不互相

local x = gui.get(2) 
x.body.height = 50 

我同意我們看看是否有一種方法可以重新定義這些變量,從它們開始直到底部,不需要:(a)爲每個變量生成函數。和(b)編輯功能中所需的參數。

如果沒有,是否有效地做到這一點的替代方法?

編輯: 的變量的結構如下:

border->body->padding->font 

我需要的是一種方法,我可以定義其中任何使後面也改變像一個

object.body.x = 15 

,它會從重新定義變量崩潰,直到底部:

body->padding->font 

我可以只從已編輯的可變重新定義它們,直到底部是這樣的:

--not the actual code, just an example of variables dependent on the variable above 
object.body.x = 15 
object.padding.width = object.body.x+1 
object.font.size = object.padding.width+1 

但是這意味着我有重新定義的填充,直到這是非常低效的特別是當我擴展更多的字體時做同樣的元素。

例如:

--padding->font 
object.padding.width = 5 
object.font.size = object.padding.width+1 
+0

這裏不清楚你的意思。 「中間部分」做了哪些修改,導致事情變得「混亂」。給我們一個例子。 –

+0

@NicolBolas我在底部舉了一個例子,我所有的變量都存儲在本地,並使用gui.get()和gui.add()等進行訪問。我的意思是在中間部分是當我例如改變body.height元素,我也想改變text.left元素,因爲它也是使用body.height定義的。有點像多米諾骨牌效應,當你影響某些事物時會崩潰,但只能在一個方向而不是所有方面。 – mhiekuru

+0

你可以使用__newindex和一個代理表... – warspyking

回答

1

我很無聊,看到這個問題(再次)以及一個副本。 我開始寫爲了好玩一些代碼,導致此:

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.graphicsgui.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

+0

正是我想要發生的,非常感謝你 – mhiekuru

0

你可以使用元表的,更具體的__newindex場:

(當然,需要將其與__index場組合,但EH)

function new(x, y, width, height) 
    local object = { 
     font = {}, padding = {}, text = {input=''}, -- tables themself are static 
     -- also I assume text.input will change and has to stay the way it is 
    } 
    -- more static data here (yes yes, I know. The code is a bit ugly, but if it works fine...) 
    object.config = { active = true, devmode = false, debug = false, id = gui.id(), type = 'textbox' } 
    object.backspace = {key = false, rate = 3, time = 0, pausetime = 20, pause = true} 
     object.border = { x = x, y = y, width = width, height = height } 
    -- stuff that has to be calculated from the above variables goes below 
    local border = object.border 
    local function calculate() 
     --border 
     --body 
     object.body = { x = border.x+1, y = border.y+1, width = border.width-2, height = border.height-2 } 
     --font 
     object.font.size = height-(math.floor(height/4)+1) 
     object.font.height = love.graphics.setNewFont(object.font.size):getHeight() 
     --padding 
     object.padding.height = math.floor(object.border.height*(2/29)) 
     object.padding.width = object.padding.height*3 
     --text 
     object.text.centerHeight = math.ceil(object.body.y+((object.body.height-object.font.height)/2)) 
     object.text.left = object.body.x+object.padding.width+object.padding.height 
     --backspacing 
     --config 
    end 
    calculate() 
    local proxy = setmetatable({},{ 
     __index = object; -- proxy.abc returns object.abc (to get width, use proxy.border.width) 
     __newindex = function(s,k,v) 
      -- fires whenever 'proxy[k] = v' is done 
      -- I assume you'll only change x/y/width/height, as other properties are dynamic 
      -- Doing 'proxy.x = 123' is the same as 'object.border.x = 123' + recalculating 
      object.border[k] = v -- Actually apply the change 
      calculate() -- Recalculate the other properties that depends on the above 
     end; 
    }) 
    gui.add(object) 
    return object.config.id 
end 

您可以運行代碼如proxy.x = 12來編輯X屬性。所有的值都將被重新計算。這不是最好的,但你的代碼微小的惱人的改善。 (不過,嘿,如果它工作正常給你,這是很好的)

注意:您只能設置Xÿ寬度高度。你可以用舊的方式獲得所有的屬性,例如proxy.padding.width(請注意,proxy.x無法正常工作,請使用proxy.border。x)

+0

重新定義x/w /寬度/高度是我的問題中最小的,因爲我可以回想一下使用我想給出的新值的原始函數。然後替換存儲的表中的整個索引。 – mhiekuru

相關問題