I have a helper function which returns a table like this:
function of_type(expected_type)
return {
expected = expected_type,
matches = function(value) return type(value) == expected_type end,
describe = "type " .. expected_type
}
end
Now this was fine with other matchers but here I would like to store the type(value) to a field in the same table when the matches function is called. Something like this:
function of_type(expected_type)
return {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
mismatch = type(value) -- how to do this?
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
end
Is this possible?
Yes, but you need to split it into steps:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
tbl.matches = function(value)
tbl.mismatch = type(value)
return type(value) == tbl.expected
end
return tbl
end
-- testing it
local foo = of_type("string")
print(foo.matches(1), foo.matches("1"))
This should output false true as you would expect.
Essentially, tbl.matches will store the reference to tbl (it's called "upvalue") and will be able to modify all the fields in that table (including the reference to itself it in).
Another way to do this would be the following (notice the changes in the tbl.matches function). Instead of capturing it as an upvalue, you can use tbl:method semantic and pass tbl as implicit self parameter:
function of_type(expected_type)
local tbl = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
describe = "type " .. expected_type
}
function tbl:matches(value)
self.mismatch = type(value) -- how to do this?
return type(value) == self.expected
end
return tbl
end
local foo = of_type("string")
print(foo:matches(1), foo:matches("1"))
This will print the same result. Notice that you are using foo:matches notation to make foo to be passed as the first parameter (references as self in the method). This is the same as using foo.matches(foo, 1).
You don't. Well, not without storing a copy of the table, or passing the function the table as a parameter. Until all of the statements of the table constructor have been processed, the table doesn't exist yet. And since you never stored it anywhere (in this function), your function can't name it in order to find it.
So you should give it a name, even just for a moment:
function of_type(expected_type)
local temp = nil
temp = {
expected = expected_type,
mismatch = nil, -- set it to nil on initialization
matches = function(value)
temp.mismatch = type(value) -- Like this
return type(value) == expected_type
end,
describe = "type " .. expected_type
}
return temp
end
This works because Lua will store temp as an upvalue. Thus, the function you're creating will see changes in temp, such as when you set it to a table value. And since it's a local variable, it won't be visible outside of this function.
Related
local ents = {
GetLocalPlayer = function()
local tbl = {
localplayer = {"Ava", "1", {213,234,234}},
GetIndex = function(self)
return self.localplayer[2]
end,
}
setmetatable(tbl, getmetatable(tbl.localplayer))
return tbl
end
}
local function main()
print(ents.GetLocalPlayer()[2])
end
main() print returns nil. If I was to do ents.GetLocalPlayer():GetIndex() however, it returns 1.
The idea is to have the default return value to be localplayer if I don't do things such as GetIndex()
A table has no default metatable, which is why your getmetatable call returns nil. In order to do anything, the second argument to setmetatable must be a table that has at least one metamethod. (__index is the most common metamethod.)
The solution is to change getmetatable(tbl.localplayer) to {__index = tbl.localplayer}.
In the below code, can anyone explain why does t1:print() works but (t1):print fails. I am attempting to make something like (t1 * 3):print() work without using an intermediate variable.
function classTestTable(members)
members = members or {}
local mt = {
__metatable = members;
__index = members;
}
function mt.print(self)
print("something")
end
return mt
end
TestTable = {}
TestTable_mt = ClassTestTable(TestTable)
function TestTable:new()
return setmetatable({targ1 = 1}, TestTable_mt )
end
TestTable t1 = TestTable:new()
t1:print() -- works fine.
(t1):print() -- fails with error "attempt to call a boolean value"
Lua expressions can extend over multiple lines.
print
(3)
Will print 3
So
t1:print()
(t1):print()
actually is equivalent to
t1:print()(t1):print()
or
local a = t1:print()
local b = a(t1)
b:print()
So you're calling the return value of t1:print()
To avoid that follow Egors advice and separate both statements with a semicolon.
t1:print();(t1):print()
The goal is to match a key of a table with a value depending of the key.
example = { ["dummy"] = this .. " example" }
print example.dummy -- print "dummy example"
Where this is the keyword to refer to the key. Is there any way to do that in Lua?
There is no way to do this directly.
You could do some preprocessing:
example = { ["dummy"] = "{THIS} example" }
for k,v in pairs(example) do
example[k]=v:gsub("{THIS}",k)
end
print(example.dummy)
It's not possible to have as clean an expression as:
t = { foo = this .. ' bar' }
because this would always be expressed without relation to the key or the table. That is, you can't capture an expression as the value of a table entry.
What is possible is implementing some level of indirection using metatables and functions, but it's hardly pretty. Here we do fetch-time evaluation. You could also recompute the results.
local function indirect_table ()
local uptable = {}
return setmetatable({}, {
__index = function (self, key)
local value = uptable[key]
return type(value) == 'function' and uptable[key](key) or value
end,
__newindex = function (self, key, value)
uptable[key] = value
--[[precompute, with no need for an uptable, or __index:
`rawset(self, key, value(key)`]]
end
})
end
local tab = indirect_table()
tab.foo = function (key) return key .. 'bar' end
print(tab.foo) --> 'foobar'
Note: this example uses a closure, but you can implement this kind of pattern using getmetatable as well.
Personally, I'd abstract this into an indirection pattern that allows arbitrary keys and values, and their actions to be specified. I figure this kind of pattern would mostly be used programatically, rather than by hand, where the results of key values are dependent on the inputs received. Again, not pretty, but a little more robust (optional actions).
local function I (_, value) return value end
local K = setmetatable({
__call = function (actor, key)
return actor.action(key, actor.value)
end
}, {
__call = function (K, value, action)
return setmetatable({ value = value, action = action or I }, K)
end
})
local T = setmetatable({
__newindex = function (self, key, value)
if getmetatable(value) == K then
value = value(key)
end
rawset(self, key, value)
end
}, {
__call = function (T, o)
return setmetatable(o or {}, T)
end
})
Simple use:
local function concat (left, right) return left .. right end
local t = T {}
t.foo = K('bar', concat) -- with a common action
t.zar = K({}, unknown_action) -- without action (`nil`)
t.qux = 'qaz' -- standard
print(t.foo, t.zar, t.qux)
This is strange metaprogramming. I'd double-check the reasoning for needing such an approach. Perhaps you're falling into an XY Problem trap? Really feels like a solution to a problem that doesn't need to exist in the first place.
Hope someone can make sense of what I'm attempting to figure out, Just don't seem to understand Lua enough to achieve this.
--[[
tbl.a.test("moo") returns "Table A moo appears"
tbl.b.test("moo") returns "moo appears"
]]
tbl = {
a = { ID = "Table A" },
b = {
test = function(...) print(... .. " appears") end,
},
}
tbl.a__index = function(self, ...) tbl.b[self](tbl.a.ID .. ...) end
What I'm attempting to do is I could create several tables a, c, d, e and not have to copy test to each one. When tbl.a.test, tbl.c.test, tbl.d.test is used, It'll retrieve the tbl.a.ID var, then call tbl.b.test(ID, "moo")
So far all I'm finding out is it's not able to find .test on anything other than tbl.b
** EDIT **
Thank's to support so far the code is now;
tbl = {
a = { ID = "Table A " },
b = { test = function(...) local id, rest = ... print(id .. ": " .. rest) end },
}
setmetatable(tbl.a, {__index=function(self, k, ...) local rest = ... return tbl.b[k](tbl.a.ID, rest) end})
However, the ... is not being progressed for some odd reason :|
You're missing a period between tbl.a and __index.
__index needs to be on a's metatable, not the table itself.
You don't return anything from your __index function
self in the __index function is the table being indexed, not the key (which is the second argument)
This should work:
setmetatable(tbl.a, {__index=function(self, k) return tbl.b[k](tbl.a.ID) end})
--------------------------------------------------------------------------------
-- [Sub]Class creation
--------------------------------------------------------------------------------
function newclass(new_obj,old_obj)
old_obj = old_obj or {} --use passed-in object (if any)
new_obj = new_obj or {}
assert(type(new_obj) == 'table','New Object/Class is not a table')
assert(type(old_obj) == 'table','Old Object/Class is not a table')
old_obj.__index = old_obj --store __index in parent object (optimization)
return setmetatable(new_obj,old_obj) --create 'new_obj' inheriting 'old_obj'
end
--------------------------------------------------------------------------------
prototype = {
test = function(self,s) print('Table ' .. self.id .. ' ' .. s .. ' appears') end
}
tbl = {}
tbl.a = newclass({id = 'A'},prototype)
tbl.b = newclass({id = 'B'},prototype)
tbl.a:test('moo')
tbl.b:test('moo')
The distinction between class and object in Lua is only theoretical. In practice they are implemented exactly the same way.
Anytime you need to do inheritance, you can use my general-purpose newclass() function to either create a new class/object, or inherit from an existing one.
Any common code & data you would like to have passed on should go into the 'prototype' table (whatever you'd like to call it for each case).
Also, you seem to forget to use the method calling syntax (that uses a colon instead of a dot) when calling methods. Without it, the self parameter is not automatically recognized.
I'm trying to write a metatable so that all indexes into the table are shifted up one position (i.e. t[i] should return t[i+1]). I need to do this because the table is defined using index 1 as the first element, but I have to interface with a program that uses index 0 as the first element. Since reading Programming in Lua, I think that I can accomplish what I want with a proxy table, but I can't seem to get it working. So far, I have this:
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
However, this does not create the expected result. In fact, it doesn't return any values at all (every lookup is nil). Is there a better way to do this, or am I just missing something?
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
print(t[0])
outputs "foo" for me when run here: http://www.lua.org/cgi-bin/demo