Is the following Lua iterator stateless? - lua

I'm confused with the concepts of stateless iterators. As an exercise I write an iterator to print all non-empty substrings of a given string. The code is as follows.
local function iter(state, i)
local str = state.str
if type(str) ~= "string" or str == "" then return nil end
if state.ending > #str then return nil end
local start = state.start
local ending = state.ending
if start == ending then
state.ending = ending + 1
state.start = 1
else
state.start = start + 1
end
return string.sub(str, start, ending)
end
function allSubstrings(str)
return iter, { str = str, start = 1, ending = 1 }, nil
end
for substr in allSubstrings("abcd123") do
print(substr)
end
I use a table { str = str, start = 1, ending = 1 } as the so-called invariant state, but I have to change the fields in this table in the iter local function. So is this iterator stateless or with complex states? If it's not stateless, is there a way to implement this functionality with a stateless iterator?
Thank you.

The PIL-chapter about stateless iterators states:
As the name implies, a stateless iterator is an iterator that does not keep any state by itself. Therefore, we may use the same stateless iterator in multiple loops, avoiding the cost of creating new closures.
In code that means that both for loops in:
f, s, var = pairs( t )
for k,v in f, s, var do print( k, v ) end
for k,v in f, s, var do print( k, v ) end
should produce the same output. This only works if neither the invariant state s nor any upvalues of the iterator function f change during the iteration, or else the second for loop would have different starting conditions than the first loop.
So, no, your iterator is not a stateless iterator because you change the invariant state during iteration.
There is a way to make your iterator stateless (and the popular Lua library luafun uses this technique extensively): keep all mutable state in the loop control variable var (even if you need to allocate a fresh table in each allocation step):
local function iter( str, var )
if type( str ) ~= "string" or str == "" then return nil end
if var[ 2 ] > #str then return nil end
local start, ending = var[ 1 ], var[ 2 ]
if start == ending then
return { 1, ending+1 }, string.sub( str, start, ending )
else
return { start+1, ending }, string.sub( str, start, ending )
end
end
function allSubstrings2( str )
return iter, str, { 1, 1 }
end
for _,substr in allSubstrings2( "abcd123" ) do
print( substr )
end
Drawbacks are that the first iteration variable var (the loop control variable) has no useful meaning for the user of the iterator, and since you have to allocate a table for each iteration step, "avoiding the cost of creating new closures" for another loop doesn't matter much.
luafun does it anyway, because it does not have the ability to recreate an iterator tuple (it is passed via function arguments from outside code), and for some algorithms you absolutely need to run the same iteration multiple times.
An alternative to this approach would be to concentrate all mutable state in the "invariant state" s (all upvalues to f must be immutable), and provide a way to copy/clone this state for later iterations:
f, s, var = allSubstrings("abcd123")
s2 = clonestate( s )
for str in f, s, var do print( str ) end
for str in f, s2, var do print( str ) end
This is still not a stateless iterator, but it is more memory-efficient than the stateless iterator with heap-allocated loop control variable, and it allows you to restart an iteration.

This is not a stateless iterator, and is indeed an iterator with complex state.
In stateless iterators there is only one control variable, and it should be treated as a pure value throughout the iterator.
You could consider using closures to implement this. Not quite stateless, but does use upvalues over table lookups, which should be more efficient.
local function allSubstrings (str)
if type(str) ~= "string" or str == "" then
return function () end -- NOP out on first iteration
end
local length = #str
local start, ending = 1, 1
return function ()
if ending > length then return nil end
local st, ed = start, ending
if start == ending then
ending = ending + 1
start = 1
else
start = start + 1
end
return string.sub(str, st, ed)
end
end
for substr in allSubstrings("abcd123") do
print(substr)
end

PIL book calls that 'Iterators with Complex State'
http://www.lua.org/pil/7.4.html

Related

For/while loop with an f-string

How do I correctly perform a for-loop that returns as a f-string (like in Python.)
For example,
for i=10, 1, -1 do
test = string.gsub('checkbox[val = "a"]','a', i)
return test
end
Returns
checkbox[v10l = "10"]
However, I was expecting:
checkbox[val = "1"]
checkbox[val = "2"]
..
..
checkbox[val = "10"]
I'm honestly not sure what are you trying to do here. Lua has no supports for f-strings and you're trying to return multiple times from function?
This is probably the closest to what you want:
for i=1, 10 do
print(string.format('checkbox[val = "%d"]', i))
end
You're doing the following:
Running for loop from 10 to 1, in opposite direction of what you want.
Replacing all occurrences of string a in checkbox[val = "a"] with loop variable, giving you resulting string and assigning that to (global, which is not good) variable test.
Returning string you just got, thus exiting loop and function that contains loop.
If you're trying to pass multiple values from loop to the outside world, you can do one of the following:
Add them into table and return it:
local tab = {}
for i=1, 10 do
tab[i] = string.format('checkbox[val = "%d"]', i)
end
return tab
Accept callback, so caller passes in a function to call on each value:
function example(cb)
for i=1, 10 do
cb(string.format('checkbox[val = "%d"]', i))
end
end
example(print) -- Here, we pass `print` as a callback, but it's usually your own function
Choose whatever better suits your particular interface.

Generating all combinations from a table in Lua

I'm trying to iterate through a table with a variable amount of elements and get all possible combinations, only using every element one time. I've landed on the solution below.
arr = {"a","b","c","d","e","f"}
function tablelen(table)
local count = 0
for _ in pairs(table) do
count = count + 1
end
return count
end
function spellsub(table,start,offset)
local str = table[start]
for i = start+offset, (tablelen(table)+1)-(start+offset) do
str = str..","..table[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
I'm still missing some further functions, but I'm getting stuck with my current code. What is it that I'm missing? It prints correctly the first time but not the second?
A solution with a coroutine iterator called recursively:
local wrap, yield = coroutine.wrap, coroutine.yield
-- This function clones the array t and appends the item new to it.
local function append (t, new)
local clone = {}
for _, item in ipairs (t) do
clone [#clone + 1] = item
end
clone [#clone + 1] = new
return clone
end
--[[
Yields combinations of non-repeating items of tbl.
tbl is the source of items,
sub is a combination of items that all yielded combination ought to contain,
min it the minimum key of items that can be added to yielded combinations.
--]]
local function unique_combinations (tbl, sub, min)
sub = sub or {}
min = min or 1
return wrap (function ()
if #sub > 0 then
yield (sub) -- yield short combination.
end
if #sub < #tbl then
for i = min, #tbl do -- iterate over longer combinations.
for combo in unique_combinations (tbl, append (sub, tbl [i]), i + 1) do
yield (combo)
end
end
end
end)
end
for combo in unique_combinations {'a', 'b', 'c', 'd', 'e', 'f'} do
print (table.concat (combo, ', '))
end
For a tables with consecutive integer keys starting at 1 like yours you can simply use the length operator #. Your tablelen function is superfluous.
Using table as a local variable name shadows Lua's table library. I suggest you use tbl or some other name that does not prevent you from using table's methods.
The issue with your code can be solved by printing some values for debugging:
local arr = {"a","b","c","d","e","f"}
function spellsub(tbl,start,offset)
local str = tbl[start]
print("first str:", str)
print(string.format("loop from %d to %d", start+offset, #tbl+1-(start+offset)))
for i = start+offset, (#tbl+1)-(start+offset) do
print(string.format("tbl[%d]: %s", i+1, tbl[i+1]))
str = str..","..tbl[i+1]
end
return str
end
print(spellsub(arr,1,2)) -- Outputs: "a,d,e" correctly
print(spellsub(arr,2,2)) -- Outputs: "b" supposed to be "b,e,f"
prints:
first str: a
loop from 3 to 4
tbl[4]: d
tbl[5]: e
a,d,e
first str: b
loop from 4 to 3
b
As you see your second loop does not ran as the start value is already greater than the limit value. Hence you only print the first value b
I don't understand how your code is related to what you want to achieve so I'll leave it up to you to fix it.

How can a Lua function return nil, even if the returned value is not nil inside the function?

I have created a function that (pseudo)randomly creates a table containing numbers. I then loop this function until at least correct result is found. As soon as I've confirmed that at least one such result exists, I stop the function and return the table.
When I create tables containing small values, there are no issues. However, once the random numbers grow to the range of hundreds, the function begins to return nil, even though the table is true the line before I return it.
local sort = table.sort
local random = math.random
local aMin, aMax = 8, 12
local bMin, bMax = 200, 2000
local function compare( a, b )
return a < b
end
local function getNumbers()
local valid = false
local numbers = {}
-- Generate a random length table, containing random number values.
for i = 1, random( aMin, aMax ) do
numbers[i] = random( bMin, bMax )
end
sort( numbers, compare )
-- See if a specific sequence of numbers exist in the table.
for i = 2, #numbers do
if numbers[i-1]+1 == numbers[i] or numbers[i-1] == numbers[i] then
-- Sequence found, so stop.
valid = true
break
end
end
for i = 1, #numbers-1 do
for j = i+1, #numbers do
if numbers[j] % numbers[i] == 0 and numbers[i] ~= 1 then
valid = true
break
end
end
end
if valid then
print( "Within function:", numbers )
return numbers
else
getNumbers()
end
end
local numbers = getNumbers()
print( "Outside function:", numbers )
This function, to my understanding, is supposed to loop infinitely until I find a valid sequence. The only way that the function can even end, according to my code, is if valid is true.
Sometimes, more often than not, with large numbers the function simply outputs a nil value to the outside of the function. What is going on here?
You're just doing getNumbers() to recurse instead of return getNumbers(). This means that if the recursion gets entered, the final returned value will be nil no matter what else happens.
In the else case of the if valid then, you are not returning anything. You only return anything in the valid case. In the else case, a recursive call may return something, but then you ignore that returned value. The print you see is corresponding to the return from the recursive call; it isn't making it out the original call.
You mean to return getNumbers().

Hiding a Lua metatable and only exposing an object's attributes

How do you create a Lua object that only exposes its attributes and not its methods? For example:
local obj = {
attr1 = 1,
attr2 = 2,
print = function(...)
print("obj print: ", ...)
end,
}
Produces:
> for k,v in pairs(obj) do print(k, v) end
attr1 1
attr2 2
print function: 0x7ffe1240a310
Also, is it possible to not use the colon syntax for OOP in Lua? I don't need inheritance, polymorphism, only encapsulation and privacy.
I started out with the above question and after chasing down the rabbit hole, I was surprised by the limited number of examples, lack of examples for the various metamethods (i.e. __ipairs, __pairs, __len), and how few Lua 5.2 resources there were on the subject.
Lua can do OOP, but IMO the way that OOP is prescribed is a disservice to the language and community (i.e. in such a way as to support polymorphism, multiple inheritance, etc). There are very few reasons to use most of Lua's OOP features for most problems. It doesn't necessarily mean there's a fork in the road either (e.g. in order to support polymorphism there's nothing that says you have to use the colon syntax - you can fold the literature's described techniques in to the closure-based OOP method).
I appreciate that there are lots of ways to do OOP in Lua, but it's irritating to have there be different syntax for object attributes versus object methods (e.g. obj.attr1 vs obj:getAttr() vs obj.method() vs obj:method()). I want a single, unified API to communicate internally and externally. To that end, PiL 16.4's section on Privacy is a fantastic start, but it's an incomplete example that I hope to remedy with this answer.
The following example code:
emulates a class's namespace MyObject = {} and saves the object constructor as MyObject.new()
hides all of the details of the objects inner workings so that a user of an object only sees a pure table (see setmetatable() and __metatable)
uses closures for information hiding (see Lua Pil 16.4 and Object Benchmark Tests)
prevents modification of the object (see __newindex)
allows for methods to be intercepted (see __index)
lets you get a list of all of the functions and attributes (see the 'key' attribute in __index)
looks, acts, walks, and talks like a normal Lua table (see __pairs, __len, __ipairs)
looks like a string when it needs to (see __tostring)
works with Lua 5.2
Here's the code to construct a new MyObject (this could be a standalone function, it doesn't need to be stored in the MyObject table - there is absolutely nothing that ties obj once its created back to MyObject.new(), this is only done for familiarity and out of convention):
MyObject = {}
MyObject.new = function(name)
local objectName = name
-- A table of the attributes we want exposed
local attrs = {
attr1 = 123,
}
-- A table of the object's methods (note the comma on "end,")
local methods = {
method1 = function()
print("\tmethod1")
end,
print = function(...)
print("MyObject.print(): ", ...)
end,
-- Support the less than desirable colon syntax
printOOP = function(self, ...)
print("MyObject:printOOP(): ", ...)
end,
}
-- Another style for adding methods to the object (I prefer the former
-- because it's easier to copy/paste function()'s around)
function methods.addAttr(k, v)
attrs[k] = v
print("\taddAttr: adding a new attr: " .. k .. "=\"" .. v .. "\"")
end
-- The metatable used to customize the behavior of the table returned by new()
local mt = {
-- Look up nonexistent keys in the attrs table. Create a special case for the 'keys' index
__index = function(t, k)
v = rawget(attrs, k)
if v then
print("INFO: Successfully found a value for key \"" .. k .. "\"")
return v
end
-- 'keys' is a union of the methods and attrs
if k == 'keys' then
local ks = {}
for k,v in next, attrs, nil do
ks[k] = 'attr'
end
for k,v in next, methods, nil do
ks[k] = 'func'
end
return ks
else
print("WARN: Looking up nonexistant key \"" .. k .. "\"")
end
end,
__ipairs = function()
local function iter(a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
return iter, attrs, 0
end,
__len = function(t)
local count = 0
for _ in pairs(attrs) do count = count + 1 end
return count
end,
__metatable = {},
__newindex = function(t, k, v)
if rawget(attrs, k) then
print("INFO: Successfully set " .. k .. "=\"" .. v .. "\"")
rawset(attrs, k, v)
else
print("ERROR: Ignoring new key/value pair " .. k .. "=\"" .. v .. "\"")
end
end,
__pairs = function(t, k, v) return next, attrs, nil end,
__tostring = function(t) return objectName .. "[" .. tostring(#t) .. "]" end,
}
setmetatable(methods, mt)
return methods
end
And now the usage:
-- Create the object
local obj = MyObject.new("my object's name")
print("Iterating over all indexes in obj:")
for k,v in pairs(obj) do print('', k, v) end
print()
print("obj has a visibly empty metatable because of the empty __metatable:")
for k,v in pairs(getmetatable(obj)) do print('', k, v) end
print()
print("Accessing a valid attribute")
obj.print(obj.attr1)
obj.attr1 = 72
obj.print(obj.attr1)
print()
print("Accessing and setting unknown indexes:")
print(obj.asdf)
obj.qwer = 123
print(obj.qwer)
print()
print("Use the print and printOOP methods:")
obj.print("Length: " .. #obj)
obj:printOOP("Length: " .. #obj) -- Despite being a PITA, this nasty calling convention is still supported
print("Iterate over all 'keys':")
for k,v in pairs(obj.keys) do print('', k, v) end
print()
print("Number of attributes: " .. #obj)
obj.addAttr("goosfraba", "Satoshi Nakamoto")
print("Number of attributes: " .. #obj)
print()
print("Iterate over all keys a second time:")
for k,v in pairs(obj.keys) do print('', k, v) end
print()
obj.addAttr(1, "value 1 for ipairs to iterate over")
obj.addAttr(2, "value 2 for ipairs to iterate over")
obj.addAttr(3, "value 3 for ipairs to iterate over")
obj.print("ipairs:")
for k,v in ipairs(obj) do print(k, v) end
print("Number of attributes: " .. #obj)
print("The object as a string:", obj)
Which produces the expected - and poorly formatted - output:
Iterating over all indexes in obj:
attr1 123
obj has a visibly empty metatable because of the empty __metatable:
Accessing a valid attribute
INFO: Successfully found a value for key "attr1"
MyObject.print(): 123
INFO: Successfully set attr1="72"
INFO: Successfully found a value for key "attr1"
MyObject.print(): 72
Accessing and setting unknown indexes:
WARN: Looking up nonexistant key "asdf"
nil
ERROR: Ignoring new key/value pair qwer="123"
WARN: Looking up nonexistant key "qwer"
nil
Use the print and printOOP methods:
MyObject.print(): Length: 1
MyObject.printOOP(): Length: 1
Iterate over all 'keys':
addAttr func
method1 func
print func
attr1 attr
printOOP func
Number of attributes: 1
addAttr: adding a new attr: goosfraba="Satoshi Nakamoto"
Number of attributes: 2
Iterate over all keys a second time:
addAttr func
method1 func
print func
printOOP func
goosfraba attr
attr1 attr
addAttr: adding a new attr: 1="value 1 for ipairs to iterate over"
addAttr: adding a new attr: 2="value 2 for ipairs to iterate over"
addAttr: adding a new attr: 3="value 3 for ipairs to iterate over"
MyObject.print(): ipairs:
1 value 1 for ipairs to iterate over
2 value 2 for ipairs to iterate over
3 value 3 for ipairs to iterate over
Number of attributes: 5
The object as a string: my object's name[5]
Using OOP + closures is very convenient when embedding Lua as a facade or documenting an API.
Lua OOP can also be very, very clean and elegant (this is subjective, but there aren't any rules with this style - you always use a . to access either an attribute or a method)
Having an object behave exactly like a table is VERY, VERY useful for scripting and interrogating the state of a program
Is extremely useful when operating in a sandbox
This style does consume slightly more memory per object, but for most situations this isn't a concern. Factoring out the metatable for reuse would address this, though the example code above doesn't.
A final thought. Lua OOP is actually very nice once you dismiss most of the examples in the literature. I'm not saying the literature is bad, btw (that couldn't be further from the truth!), but the set of sample examples in PiL and other online resources lead you to using only the colon syntax (i.e. the first argument to all functions is self instead of using a closure or upvalue to retain a reference to self).
Hopefully this is a useful, more complete example.
Update (2013-10-08): There is one notable drawback to the closure-based OOP style detailed above (I still think the style is worth the overhead, but I digress): each instance must have its own closure. While this is obvious in the above lua version, this becomes slightly problematic when dealing with things on the C-side.
Assume we're talking about the above closure style from the C-side from here on out. The common case on the C side is to create a userdata via lua_newuserdata() object and attach a metatable to the userdata via lua_setmetatable(). On face value this doesn't appear like a problem until you realize that methods in your metatable require an upvalue of the userdata.
using FuncArray = std::vector<const ::luaL_Reg>;
static const FuncArray funcs = {
{ "__tostring", LI_MyType__tostring },
};
int LC_MyType_newInstance(lua_State* L) {
auto userdata = static_cast<MyType*>(lua_newuserdata(L, sizeof(MyType)));
new(userdata) MyType();
// Create the metatable
lua_createtable(L, 0, funcs.size()); // |userdata|table|
lua_pushvalue(L, -2); // |userdata|table|userdata|
luaL_setfuncs(L, funcs.data(), 1); // |userdata|table|
lua_setmetatable(L, -2); // |userdata|
return 1;
}
int LI_MyType__tostring(lua_State* L) {
// NOTE: Blindly assume that upvalue 1 is my userdata
const auto n = lua_upvalueindex(1);
lua_pushvalue(L, n); // |userdata|
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, -1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
Note how the table created with lua_createtable() didn't get associated with a metatable name the same as if you would have registered the metatable with luaL_getmetatable()? This is 100% a-okay because these values are completely inaccessible outside of the closure, but it does mean that luaL_getmetatable() can't be used to look up a particular userdata's type. Similarly, this also means that luaL_checkudata() and luaL_testudata() are also off limits.
The bottom line is that upvalues (such as userdata above) are associated with function calls (e.g. LI_MyType__tostring) and are not associated with the userdata itself. As of now, I'm not aware of a way in which you can associate an upvalue with a value such that it becomes possible to share a metatable across instances.
UPDATE (2013-10-14) I'm including a small example below that uses a registered metatable (luaL_newmetatable()) and also lua_setuservalue()/lua_getuservalue() for a userdata's "attributes and methods". Also adding random comments that have been the source of bugs/hotness that I've had to hunt down in the past. Also threw in a C++11 trick to help with __index.
namespace {
using FuncArray = std::vector<const ::luaL_Reg>;
static const std::string MYTYPE_INSTANCE_METAMETHODS{"goozfraba"}; // I use a UUID here
static const FuncArray MyType_Instnace_Metamethods = {
{ "__tostring", MyType_InstanceMethod__tostring },
{ "__index", MyType_InstanceMethod__index },
{ nullptr, nullptr }, // reserve space for __metatable
{ nullptr, nullptr } // sentinel
};
static const FuncArray MyType_Instnace_methods = {
{ "fooAttr", MyType_InstanceMethod_fooAttr },
{ "barMethod", MyType_InstanceMethod_barMethod },
{ nullptr, nullptr } // sentinel
};
// Must be kept alpha sorted
static const std::vector<const std::string> MyType_Instance___attrWhitelist = {
"fooAttr",
};
static int MyType_ClassMethod_newInstance(lua_State* L) {
// You can also use an empty allocation as a placeholder userdata object
// (e.g. lua_newuserdata(L, 0);)
auto userdata = static_cast<MyType*>(lua_newuserdata(L, sizeof(MyType)));
new(userdata) MyType(); // Placement new() FTW
// Use luaL_newmetatable() since all metamethods receive userdata as 1st arg
if (luaL_newmetatable(L, MYTYPE_INSTANCE_METAMETHODS.c_str())) { // |userdata|metatable|
luaL_setfuncs(L, MyType_Instnace_Metamethods.data(), 0); // |userdata|metatable|
// Prevent examining the object: getmetatable(MyType.new()) == empty table
lua_pushliteral(L, "__metatable"); // |userdata|metatable|literal|
lua_createtable(L, 0, 0); // |userdata|metatable|literal|table|
lua_rawset(L, -3); // |userdata|metatable|
}
lua_setmetatable(L, -2); // |userdata|
// Create the attribute/method table and populate with one upvalue, the userdata
lua_createtable(L, 0, funcs.size()); // |userdata|table|
lua_pushvalue(L, -2); // |userdata|table|userdata|
luaL_setfuncs(L, funcs.data(), 1); // |userdata|table|
// Set an attribute that can only be accessed via object's fooAttr, stored in key "fooAttribute"
lua_pushliteral(L, "foo's value is hidden in the attribute table"); // |userdata|table|literal|
lua_setfield(L, -2, "fooAttribute"); // |userdata|table|
// Make the attribute table the uservalue for the userdata
lua_setuserdata(L, -2); // |userdata|
return 1;
}
static int MyType_InstanceMethod__tostring(lua_State* L) {
// Since we're using closures, we can assume userdata is the first value on the stack.
// You can't make this assumption when using metatables, only closures.
luaL_checkudata(L, 1, MYTYPE_INSTANCE_METAMETHODS.c_str()); // Test anyway
auto myTypeInst = static_cast<MyType*>(lua_touserdata(L, 1));
lua_pushstring(L, myTypeInst->str()); // |userdata|string|
return 1; // |userdata|string|
}
static int MyType_InstanceMethod__index(lua_State* L) {
lua_getuservalue(L, -2); // |userdata|key|attrTable|
lua_pushvalue(L, -2); // |userdata|key|attrTable|key|
lua_rawget(L, -2); // |userdata|key|attrTable|value|
if (lua_isnil(L, -1)) { // |userdata|key|attrTable|value?|
return 1; // |userdata|key|attrTable|nil|
}
// Call cfunctions when whitelisted, otherwise the caller has to call the
// function.
if (lua_type(L, -1) == LUA_TFUNCTION) {
std::size_t keyLen = 0;
const char* keyCp = ::lua_tolstring(L, -3, &keyLen);
std::string key(keyCp, keyLen);
if (std::binary_search(MyType_Instance___attrWhitelist.cbegin(),
MyType_Instance___attrWhitelist.cend(), key))
{
lua_call(L, 0, 1);
}
}
return 1;
}
static int MyType_InstanceMethod_fooAttr(lua_State* L) {
// Push the uservalue on to the stack from fooAttr's closure (upvalue 1)
lua_pushvalue(L, lua_upvalueindex(1)); // |userdata|
lua_getuservalue(L, -1); // |userdata|attrTable|
// I haven't benchmarked whether lua_pushliteral() + lua_rawget()
// is faster than lua_getfield() - (two lua interpreter locks vs one lock + test for
// metamethods).
lua_pushliteral(L, "fooAttribute"); // |userdata|attrTable|literal|
lua_rawget(L, -2); // |userdata|attrTable|value|
return 1;
}
static int MyType_InstanceMethod_barMethod(lua_State* L) {
// Push the uservalue on to the stack from barMethod's closure (upvalue 1)
lua_pushvalue(L, lua_upvalueindex(1)); // |userdata|
lua_getuservalue(L, -1); // |userdata|attrTable|
// Push a string to finish the example, not using userdata or attrTable this time
lua_pushliteral(L, "bar() was called!"); // |userdata|attrTable|literal|
return 1;
}
} // unnamed-namespace
The lua script side of things looks something like:
t = MyType.new()
print(typue(t)) --> "userdata"
print(t.foo) --> "foo's value is hidden in the attribute table"
print(t.bar) --> "function: 0x7fb560c07df0"
print(t.bar()) --> "bar() was called!"
how do you create a lua object that only exposes its attributes and not its methods?
If you don't expose methods in any way, you can't call them, right? Judging from your example, it sounds like what you really want is a way to iterate through the attributes of an object without seeing methods, which is fair.
The simplest approach is just to use a metatable, which puts the methods in a separate table:
-- create Point class
Point = {}
Point.__index = Point
function Point:report() print(self.x, self.y) end
-- create instance of Point
pt = setmetatable({x=10, y=20}, Point)
-- call method
pt:report() --> 10 20
-- iterate attributes
for k,v in pairs(pt) do print(k,v) end --> x 10 y 20
is it possible to not use the colon syntax for OOP in Lua?
You can use closures instead, but then pairs is going to see your methods.
function Point(x, y)
local self = { x=x, y=y}
function pt.report() print(self.x, self.y) end
return self
end
pt = Point(10,20)
pt.report() --> 10 20
for k,v in pairs(pt) do print(k,v) end --> x 10 y 20 report function: 7772112
You can fix the latter problem by just writing an iterator that shows only attributes:
function nextattribute(t, k)
local v
repeat
k,v = next(t, k)
if type(v) ~= 'function' then return k,v end
until k == nil
end
function attributes (t)
return nextattribute, t, nil
end
for k,v in attributes(pt) do print(k,v) end --> x 10 y 20
I don't need inheritance, polymorphism
You get polymorphism for free in Lua, without or without classes. If your zoo has a Lion, Zebra, Giraffe each of which can Eat() and want to pass them to the same Feed(animal) routine, in a statically typed OO languages you'd need to put Eat() in a common base class (e.g. Animal). Lua is dynamically typed and your Feed routine can be passed any object at all. All that matters is that the object you pass it has an Eat method.
This is sometimes called "duck typing": if it quacks like a duck and swims like a duck, it's a duck. As far as our Feed(animal) routine is concerned, if it Eats like an animal, it's an animal.
only encapsulation and privacy.
Then I think exposing data members while hiding methods is the opposite of what you want to do.

Redis: Lua script to return every other nth element of a sorted set

I am trying to put together a lua script to be called from Redis (via an EVAL call) in order to return every other nth element of a sorted set (nth being the rank in the set, not the score).
There are very few online examples of Lua scripts that can be used to build upon, would anyone be able to point me in the right direction?
local function copyNOtherElements(table, interval, startpos)
local elemno = 1
local rettab = {}
for k, v in ipairs(table) do
if k >= startpos and (k - startpos) % interval == 0 then
rettab[elemno] = v
elemno = elemno + 1
end
end
return rettab
end
Sorry about formatting, typing on a phone. that's assuming the table is a 1 based array
For future readers, adding Redis into the previous answer, and a bit more efficient code to iterate the Nth elements:
local function zrange_pick(zset_key, step, start, stop)
-- The next four lines can be removed along with the start/stop params if not needed as in OP Q.
if start == nil than
start = 0
if end == nil than
end = -1
local set_by_score = redis.call('ZRANGE', zset_key, start, end)
local result = {}
for n = 1, #set_by_score, step do
table.insert(result, set_by_score[n])
end
return result
end

Resources