Let's say I do unpack(4) or unpack("hello world"). Are there any unexpected behaviors to this?
Reason is something like this:
function a(bool)
if bool then
return {1, 2}, "foo"
else
return 1, "foo"
end
end
function b(x, z)
end
function b(x, y, z)
end
i, j = a(???)
b(unpack(i), j) -- is this ok?
unpack(4) will cause an error
attempt to get length of a number value
unpack("hello world") will return
nil nil nil nil nil nil nil nil nil nil nil
so that's not very useful as well.
unpack is meant for unpacking tables. If you'd work with a recent version of Lua you would notice that it is now table.unpack()
Other issues with your code:
Lua does not support overloading functions. Functions are variables.
You write:
function b(x, z)
end
function b(x, y, z)
end
The first definition is lost once the second definition is processed.
If you use the another notation it will be more clear.
Your code is equivalent to
b = function (x, z)
end
b = function (x, y, z)
end
and I think you will agree that after
b = 3
b = 4
b will be 4. Same principle...
You can amend the unpack standard function to obtain the desired behavior:
local old_unpack = table.unpack or unpack
local function new_unpack(list, ...)
if type(list) ~= "table" then
list = {list}
end
return old_unpack(list, ...)
end
table.unpack = new_unpack
unpack = new_unpack
-- Usage:
print(unpack(4))
print(unpack("hello world"))
print(unpack(nil)) -- ops! nothing is printed!
Related
I have a Lua iterator function that I don't have control over:
for x, y, z in otherfunc(stuff) do
...
end
I want to write a generic Lua wrapper to this function that skips over some of the values it returns, hiding this implementation detail from my users:
for x, y, z in myfunc(stuff) do
-- not every x/y/z triplet will appear here
end
Though I happen to have three return values here, how can I handle this generically for any number of return values?
local function check_and_return(my_state, x, ...)
if x == nil or my_state[1](x, ...) then
return x, ...
else
return check_and_return(my_state, my_state[2](my_state[3], x))
end
end
local function my_generator(my_state, prev_x)
return check_and_return(my_state, my_state[2](my_state[3], prev_x))
end
function subiterate(selector, generator, state, init_x)
-- the iterator is stateless
-- no closures created at loop initialization
-- one table is created at loop initialization
-- no tables created on every step
return my_generator, {selector, generator, state}, init_x
end
Usage example:
local function everythird(i,_)
return (i-1)%3==2
end
for i, n in subiterate(everythird, ipairs{'a','b','c','d','e','f'}) do
print(i, n)
end
--> 3 c
--> 6 f
For the specific case of a known iterator function with specific number of return values, and a hard-coded selection criteria, I used this:
function myfunc(...)
local generator, constant, x,y,z in otherfunc(...)
return function()
repeat x,y,z = generator(constant,x,y,z)
until (not x) or x+y+z > 7
if x then return x,y,z end
end
end
The above will call the otherfunc() iterator repeatedly, but only yield x/y/z values when the sum of them is greater than 7.
More generically, here's a function that takes a selection function and an iterator, handling an arbitrary number of return values from the iterator and returning them only when the selector function returns a truthy value:
function subiterate(selector, generator, constant, ...)
local values = {...}
return function()
repeat values = table.pack(generator(constant, table.unpack(values)))
until not values[1] or selector(table.unpack(values))
return table.unpack(values)
end
end
local function everythird(i,_)
return (i-1)%3==2
end
for i, n in subiterate(everythird, ipairs{'a','b','c','d','e','f'}) do
print(i, n)
end
--> 3 c
--> 6 f
I make no guarantees about the speed of the generic function, given all the pack() and unpack() going on. But it does work. I welcome any more efficient way to handle arbitrary numbers of return values for an iterator.
I initially came across this while developing my game in LOVE2D, and I can't figure out why.
As much as I know, x:foo() is just a syntactic sugar for x.foo(self) but combining this with other classes results in some weird behavior.
Basically, I have two programs
y = {}
function y:bar()
return self.b
end
x = {}
function x:foo()
return y.bar(self)
end
print(x.foo({b = 3}))
This prints 3 as expected
y = {}
function y:bar()
return self.b
end
x = {}
function x:foo()
return y:bar()
end
print(x.foo({b = 3}))
But this prints nil !!
I don't understand, why are these two programs printing different things?
x:foo() is syntactic sugar for x.foo(x)
which means that
function x:foo()
return y:bar()
end
is actually
function x:foo()
return y.bar(y)
end
look again at your use of self
function x:foo()
return y.bar(self) -- = y.bar(x)
end
function x:foo()
return y:bar() -- = y.bar(y)
end
x:foo() is just a syntactic sugar for x.foo(self)
This is correct in the context of a function definition but not as an expression.
function x.y.z:foo(a, b)
print(self, a, b)
end
is the same as
function x.y.z.foo(self, a, b)
print(self, a, b)
end
A function definition being set into a table field can be a method, that is with a :. Nested table fields are also allowed, for methods and non-methods, if the keys are strings that are valid Lua identifiers, as in x.y.z.
But
print(x.y().z:foo(a, b))
is the same as
local _ = x.y().z
print(_.foo(_, a, b))
With the method call syntax, the lefthand-side of : is an expression (x.y().z) that is evaluated only once.
Looking at the chapter 7.1 – Iterators and Closures from "Programming in Lua" it seems like the the for foo in bar loop takes requires bar to be of type (using Java typesto express it) Supplier<Tuple> and the the for-in will keep calling bar until it returns nil.
So for something like:
for k,v in pairs( tables ) do
print( 'key: '..k..', value: '..v )
end
that implies pairs has a type of Function<Table,Supplier<Tuple>>.
I want to create a function that behaves like pairs except it skips tuples where the first argument starts with an underscore (ie _).
local function mypairs( list )
local --[[ Supplier<Tuple> ]] pairIterator = pairs( list )
return --[[ Supplier<Tuple> ]] function ()
while true do
local key, value = pairIterator()
if key == nil then
return nil
elseif key:sub(1,1) ~= '_' then
return key, value
end
end
end
end
however it doesn't work since
--[[should be: Supplier<Table>]] pairIterator = pairs({ c=3; b=2; a=1 })
when I call it
pairIterator()
it returns
stdin:1: bad argument #1 to 'pairIterator' (table expected, got no value)
stack traceback:
[C]: in function 'pairIterator'
stdin:1: in main chunk
[C]: in ?
but
pairIterator({ c=3; b=2; a=1 })
returns
Lua>pairIterator({ c=3; b=2; a=1 })
c 3
Your basic problem is that you're using Java logic on Lua problems. Java and Lua are different languages with different constructs, and it's important to recognize that.
pairs does not have a return value; it has multiple return values. This is a concept that Java completely lacks. A Tuple is a single value that can store and manipulate multiple values. A Lua function can return multiple values. This is syntactically and semantically distinct from returning a table containing multiple values.
The iterator-based for statement takes multiple values as its input, not a table or container of multiple values. Specifically, it stores 3 values: an iterator function, a state value (which you use to preserve state between calls), and an initial value.
So, if you want to mimic pairs's behavior, you need to be able to store and manipulate its multiple return values.
Your first step is to store what pairs actually returns:
local f, s, var = pairs(list)
You are creating a new iterator function. So you need to return that, but you also need to return the s and var that pairs returns. Your return statement needs to look like this:
return function (s, var)
--[[Contents discussed below]]
end, s, var --Returning what `pairs` would return.
Now, inside your function, you need to call f with s and var. This function will return the key/value pair. And you need to process them correctly:
return function (s, var)
repeat
local key, value = f(s, var)
if(type(key) ~= "string") then
--Non-string types can't have an `_` in them.
--And no need to special-case `nil`.
return key, value
elseif(key:sub(1, 1) ~= '_') then
return key, value
end
until true
end, s, var --Returning what `pairs` would return.
pairs() returns three separate values:
a function to call with parameters (table, key) that returns a key and value
the table you passed to it
the first 'key' value to pass to the function (nil for pairs(), 0 for ipairs())
So something like this:
for k,v in pairs({a=1, b=13, c=169}) do print(k, v) end
Can be done like this:
local f,t,k = pairs({a=1, b=13, c=169})
local v
print('first k: '..tostring(k))
k,v = f(t, k)
while k ~= nil do
print('k: '..tostring(k)..', v: '..tostring(v))
k,v = f(t, k)
end
Results:
first k: nil
k: c, v: 169
k: b, v: 13
k: a, v: 1
And you don't have to take an argument, this has manual if statements for each value:
function mypairs()
-- the function returned should take the table and an index, and
-- return the next value you expect AND the next index to pass to
-- get the value after. return nil and nil to end
local myfunc = function(t, val)
if val == 0 then return 1, 'first' end
if val == 1 then return 2, 'second' end
if val == 2 then return 3, 'third' end
return nil, nil
end
-- returns a function, the table, and the first value to pass
-- to the function
return myfunc, nil, 0
end
for i,v in mypairs() do
print('i: '..tostring(i)..', v: '..tostring(v))
end
-- output:
-- i: 1, v: first
-- i: 2, v: second
-- i: 3, v: third
For your mypairs(list) you can just keep calling the function returned from pairs as long as the key has an underscore to get the next value:
local function mypairs( list )
local f,t,k = pairs(list)
return function(t,k)
local a,b = f(t, k)
while type(a) == 'string' and a:sub(1,1) == '_' do a,b = f(t,a) end
return a,b
end, t, k
end
local list = {a=5, _b=11, c = 13, _d=69}
for k,v in mypairs(list) do print(k, v) end
-- output:
-- c 13
-- a 5
The docs you link to have an iterator that only returns one value and pairs() returns 2, but you could return more if you want. The for ... in ... construct will only execute the body if the first value is non-nil. Here's a version that also returns the keys that were skipped, the body isn't executed if you don't end up with an actual value though so you might not see all the _ keys:
local function mypairs( list )
local f,t,k = pairs(list)
return function(t,k)
local skipped = {}
local a,b = f(t, k)
while type(a) == 'string' and a:sub(1,1) == '_' do
table.insert(skipped, a)
a,b = f(t,a)
end
return a,b,skipped
end, t, k
end
local list = {a=5, _b=11, c = 13, _d=69}
for k,v,skipped in mypairs(list) do
for i,s in ipairs(skipped) do
print('Skipped: '..s)
end
print(k, v)
end
Lua 5.2
I need to iterate an userdata variable.
As I understand, I can do this using getmetatable and __pairs. Like this:
for k, v in getmetatable(userdataVariable).__pairs do
-- someting
end
But I get 'attempt to call a nil value' when I'm trying to do this.
I found a __pairs implementation here: what is actual implementation of lua __pairs?
function meta.__pairs(t)
return function(t, k)
local v
repeat
k, v = next(t, k)
until k == nil or theseok(t, k, v)
return k, v
end, t, nil
end
But I don't understand what I should do with theseok? What function should I define here?
I think you're looking for the __index meta table.
Does anybody know actual implementation of lua 5.2. metamethod __pairs? In other words, how do I implement __pairs as a metamethod in a metatable so that it works exactly same with pairs()?
I need to override __pairs and want to skip some dummy variables that I add in a table.
The following would use the metatable meta to explicitly provide pairs default behavior:
function meta.__pairs(t)
return next, t, nil
end
Now, for skipping specific elements, we must replace the returned next:
function meta.__pairs(t)
return function(t, k)
local v
repeat
k, v = next(t, k)
until k == nil or theseok(t, k, v)
return k, v
end, t, nil
end
For reference: Lua 5.2 manual, pairs
The code below skips some entries. Adapt as needed.
local m={
January=31, February=28, March=31, April=30, May=31, June=30,
July=31, August=31, September=30, October=31, November=30, December=31,
}
setmetatable(m,{__pairs=
function (t)
local k=nil
return
function ()
local v
repeat k,v=next(t,k) until v==31 or k==nil
return k,v
end
end})
for k,v in pairs(m) do print(k,v) end