是否可以在不制作单个函数的情况下制作折叠变量? [英] Is it possible to make a collapsing variables without making individual functions?

查看:28
本文介绍了是否可以在不制作单个函数的情况下制作折叠变量?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段代码,它从少量变量开始,然后使用这些初始变量生成更多元素.

I have a code that starts as a small amount of variables and makes more elements using those initial variables.

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

当我在中间修改一些东西时,整个事情变得一团糟,因为从我改变的那个开始,直到底部的值彼此不一致

and when I modify something in the middle part, the whole thing becomes a mess because starting from the one i changed until the bottom ones value doesn't agree with each other

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

我正在寻找是否有办法重新定义这些变量,从它们开始直到底部,而无需:(a) 为每个变量创建函数.(b) 在函数中编辑所需的参数.

I'm looking if there's a way for these variables to be redefined, starting from them until the bottom, without: (a) making functions for each of the variables. and (b) editing the required parameters in the function.

如果没有,是否有另一种方法可以有效地做到这一点?

and If there's none, are the an alternate way to do this efficiently?

变量结构如下:

border->body->padding->font

我需要的是一种我可以定义任何的方法,以便后面的也可以改变:

what i needed is a way i can define any of them so that the one that follows also changes like:

object.body.x = 15

它会从重新定义的变量折叠到底部:

and it would collapse from that redefined variable until the bottom:

body->padding->font

我可以像这样从编辑的变量重新定义它们直到底部:

i could just redefine them from the edited variable until the bottom like this:

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

但这意味着在重新定义填充直到字体时我必须做同样的事情,这非常低效,尤其是当我扩展更多元素时.

but that means I have to do the same when redefining the padding until the font which is extremely inefficient especially when I extended more elements.

示例:

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

推荐答案

我很无聊,看到这个问题(再次) 和一个重复的问题.为了好玩,我开始写一些代码,结果是:

I was bored and saw this question (again) along with a duplicate. I started writing some code for fun, leading to this:

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)

代码很多,但我喜欢写它.它提供了这个不错的输出:

It's a lot of code, but I liked writing it. It gives this nice output:

border.x=20, test.text.left=73
border.x=30, test.text.left=83
border.x=40, test.text.left=93

只有在编辑其依赖项之一时才会重新计算所有属性.我让它也适用于子表,这有点棘手,但最后实际上似乎很容易.您可以编辑(例如) body 字段,方法是将其设置为一个全新的表或通过在现有表中设置一个字段,如最后几行所示的代码片段.当您将它分配给一个新表时,它会在其上设置一个元表.您也不能使用对 (& co),除非您使用 5.2 并且可以使用 __pairs.

All properties only get recalculated when one of its dependencies is edited. I made it also work with subtables, which was a bit tricky, but at the end actually seems quite easy. You can edit (for example) the body field by setting it to a completely new table or by setting a field in the already existing table, as seen in the last few lines of the code snippet. When you assign it to a new table, it'll set a metatable on it. You can't use pairs (& co) neither, unless you use 5.2 and can use __pairs.

它可能会解决您的问题.如果没有,我写它很有趣,所以至少我写这篇文章总是积极的.(你必须承认,这是一些漂亮的代码.嗯,它的工作方式,而不是实际的格式)

It might solve your problem. If not, I had fun writing it, so at least it'll always be something positive that I wrote this. (And you have to admit, that's some beautiful code. Well, the way it works, not the actual formatting)

注意:如果您要使用它,请取消对 love.graphicsgui.id 部分的注释,因为我没有有爱,我显然不得不测试代码.

Note: If you're gonna use it, uncomment the love.graphics and gui.id part, as I don't have LÖVE and I obviously had to test the code.

这是我的东西的 API 的快速摘要",因为它在开始时可能会令人困惑:

Here's a quick "summary" of my thing's API, as it might be confusing in the beginning:

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"的需求.使用一些环境魔法,您可以拥有字段"的构造函数(作为示例) 只需 function() return idk+1 end.

You could use setfenv (if Lua 5.1) to remove the need of 'self.FIELD'. With some environment magic you can have the constructor for 'field' (as an example) just be function() return idk+1 end.

这篇关于是否可以在不制作单个函数的情况下制作折叠变量?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆