how to call variant arguments method with unpack - lua

I am trying to run a redis lua mock project to test my redis lua code. but obviously, there are bugs in the redis-mock project.
When I call redis.call('hget', 'foo', 'bar') in my test code, the redis mock throw an assert error at hash.lua#22 which is call from RedisLua.lua#20
-- RedisLua.lua
local call = function(self)
return (function(cmd, ...)
cmd = string.lower(cmd)
local arg = {...}
local ret = self.db[cmd](self.db, unpack(arg)) -- line 20
if self.RedisLua_VERBOSE then
print(cmd .. "( " .. table.concat(arg, " ") .. " ) === ".. tostring(ret))
end
return ret
end)
end
-- hash.lua
function RedisDb:hget(self,k,k2)
assert((type(k2) == "string")) -- # line 22
local x = RedisDb.xgetr(self,k,"hash")
return x[k2]
end
After trace, I found, the self is 'foo', the k is 'bar' and the k2 is actually nil, How can I fix this bug, the k should be foo, and the k2 should be 'bar'

I think you need to call redis:call('hget', 'foo', 'bar') or equivalently redis.call(redis,'hget','foo','bar'), rather than redis.call('hget', 'foo', 'bar').

Answer my own question.
When define as :, no self need.
-- hash.lua
function RedisDb:hget(self,k,k2)
assert((type(k2) == "string")) -- # line 22
local x = RedisDb.xgetr(self,k,"hash")
return x[k2]
end
change to
-- hash.lua
function RedisDb:hget(k,k2)
assert((type(k2) == "string")) -- # line 22
local x = RedisDb:xgetr(k,"hash")
return x[k2]
end

Related

Lua variable length function arguments are nil

I am trying to write a curry function in lua 5.2. My code looks like this:
function add(a, b)
return a + b
end
function curry(func, value)
return (function (...)
return func(value, table.unpack(arg))
end)
end
add2 = curry(add, 2)
print(add2(3))
The parameter arg however does not contain the value passed into the add2 function.
When I try and run the example from the Lua documentation it errors because arg is nil.
printResult = ""
function print (...)
for i,v in ipairs(arg) do -- arg is nil
printResult = printResult .. tostring(v) .. "\t"
end
printResult = printResult .. "\n"
end
How can I use variable length functions in 5.2 if this is not working?
Edit:
As user #siffiejoe has pointed out, my function here is just doing partial application, not proper currying. Here is my implementation of a proper curry function in lua using the error fix from the accepted answer.
function curry(func, params)
return (function (...)
local args = params or {}
if #args + #{...} == debug.getinfo(func).nparams then
local args = {table.unpack(args)}
for _,v in ipairs({...}) do
table.insert(args, v)
end
return func(table.unpack(args))
else
local args = {table.unpack(args)}
for _,v in ipairs({...}) do
table.insert(args, v)
end
return curry(func, args)
end
end)
end
Feel free to suggest changes and add test cases here
Since Lua 5.1, arg in this context has been replaced by ... (except that the latter is a list instead of a table).
So, table.unpack(arg) should be just ....
See Breaking Changes. The Lua Reference manuals are very good and this section in particular is highly useful.

How do we change the way print displays a table

Assuming I have a piece of code such as the following
aTable = {aValue=1}
aTable_mt = {}
print(aTable)
What must I do to make Lua print something like aTable current aValue = 1 as opposed to table: 0x01ab1d2.
So far I've tried setting the __tostring metamethod but that doesn't seem to be invoked by print. Is there some metamethod I've been missing or does the answer have nothing to do with metamethods?
__tostring works:
aTable = {aValue=1}
local mt = {__tostring = function(t)
local result = ''
for k, v in pairs(t) do
result = result .. tostring(k) .. ' ' .. tostring(v) .. ''
end
return result
end}
setmetatable(aTable, mt)
print(aTable)
This prints aValue 1 (with one extra whitespace, remove it in real code). The aTable part is not available, because aTable is a variable that references the table, not the content of the table itself.
I'm not sure how you set the metamethod, but the following code prints "stringified" for me:
local aTable = {a = 1, b = 2}
setmetatable(aTable, {__tostring = function() return "stringified" end})
print(aTable)
If you want lua to generally print all tables human readable, you could
hook up/overwrite the print function:
local orig_print = print
print = function(...)
local args = {...}
for i,arg in ipairs(args) do
if type(arg) == 'table' then
args[i] = serialize(arg)
end
end
orig_print(table.unpack(args))
end
serialize could be serpent or some other lib from here
Note that this must be done before any other module/script is loaded.

Scanning folders using lua

I'm trying to get the name of all the file saved in two folders, the name are saved as :
1.lua 2.lua 3.lua 4.lua and so on
the folders name are :
first folder : "/const/"
second folder: "/virt/"
what I'm trying to do is only get the number of the files and this works but not in the right order, when I get the 17 file for example I get the 17th delivered from the function before the 15 and this causes for me a problem here the code of the function that I'm using :
local virt_path = "/virt/"
local const_path = "/const"
local fs = require "lfs"
local const = {}
for num = 1, (numberoffile)do -- numberoffile is predfined and can't be change
const[num] = assert(
dofile (const_path .. mkfilename(num)),
"Failed to load constant ".. num ..".")
end
local function file_number() --this is the function that causes me a headach
local ci, co, num = ipairs(const)
local vi, vo, _ = fs.dir(virt_path)
local function vix(o)
local file = vi(o)
if file == nil then return nil end
local number = file:match("^(%d+).lua$")
if number == nil then return vix(o) end
return tonumber(number)
end
local function iter(o, num)
return ci(o.co, num) or vix(o.vo, num)
end
return iter, {co=co, vo=vo}, num
end
As I said the function delive the need return values but not the right Arithmetic order.
any idea what I'm doing wrong here ?
I use my path[1] library.
1 We fill table with filenames
local t = {}
for f in path.each("./*.lua", "n") do
t[#t + 1] = tonumber((path.splitext(f)))
end
table.sort(t)
for _, i in ipairs(t) do
-- do work
end
2 We check if files exists
for i = 1, math.huge do
local p = "./" .. i .. ".lua"
if not path.exists(p) then break end
-- do work
end
[1] https://github.com/moteus/lua-path

lua - calling a function from a string

From what I've read on this site the below should work.
Can some kindly soul please point out where I'm going wrong?
I've embedded more description and print returns in the code hopefully to make easier reading
local m = {
{opt = "Solar Panels", cmd = "solarPanel"}
-- There are many more options here.
}
function doTheMenu()
print("Welcome to Spyder's Factory")
print("")
print("What would you like to make?")
local n = 1
local l = #m - 1
while true do --This while loop may or may not be relevant to the question, it's the menu
term.clear() --this is ComputerCraft lua, the term function is defined
term.setCursorPos(1,2) --elsewhere in an API
for i, j in pairs(m) do
if i == n then
if i < 10 then print(i, " ["..j.opt.."]") else print(i, " ["..j.opt.."]") end
fsel = j.cmd --set fsel to the function name I require in case of a break
tsel = j.opt --Ditto, tsel, human-friendly name
else
if i < 10 then print(i, " "..j.opt) else print(i, " "..j.opt) end
end
end
local a, b = os.pullEvent("key")
if b == 200 and n > 1 then n = n - 1 end
if b == 208 and n <= l then n = n + 1 end
if b == 28 then break end
end
write("\nSure, how many "..tsel.."? ")
qty = tonumber(read())
req[fsel] = req[fsel] + qty
str = fsel.."("..qty..")"
print("Loading function '"..fsel.."("..qty..")'") --Returns "Loading function 'solarPanel(1)'"
func = loadstring(str)
print(func) --Returns "function: 2cdfc5a7"
print("Loading function")
func() --The error line, Returns "string:1: attempt to call nil"
--tellUserWhatNeed()
--makeItHappen()
end
doTheMenu()
The issue is the code fails to run with the error:
string:1 attempt to call nil
Also what is term variable, if that's all your code, term is not defined and is null)
That said: either _G[fsel] is nil or fsel is nil ?
Are you sure you have function declared in _G with the name stored in fsel?
e.i. call before the problem line print (_G[fsel]) to see what it gives you.
This was the solution that ended up working:
local f = loadstring(str)
if f then
setfenv(f, getfenv())
f()
end
This replaces the lines above:
print("Loading function '"..fsel.."("..qty..")'")
func = loadstring(str)
print(func)
print("Loading function")
func()
As well as adding basic error handling for loading the function

"Transpose/Zip" function not working as expected

I am trying to build an elegant transpose function using functions mapn and zip in Lua.
The mapn and zip are as follows (From the lua book):
function map(func, array)
local new_array = {}
for i,v in ipairs(array) do
new_array[i] = func(v)
end
return new_array
end
function mapn(func, ...)
local new_array = {}
local i=1
local arg_length = table.getn(arg)
while true do
local arg_list = map(function(arr) return arr[i] end, arg)
if table.getn(arg_list) < arg_length then return new_array end
new_array[i] = func(unpack(arg_list))
i = i+1
end
end
These work as expected.
I then define zip and transpose as:
function zip(...)
return mapn(function(...) return {...} end,...)
end
function transpose(...)
return zip(unpack(...))
end
Now transpose({{1,2},{3,4},{5,6}}) produces {{1,3,5},{2,4,6}} as expected.
But transpose({{1,2},{3,4},{5}}) does not produce {{1,3,5},{2,4}}. It only produces one row.
How can I get it to produce the result I wish for?
I just decided to write an "inelegant" function instead. It seems there's no smooth way to use mapn and friends.
function transp(L)
local n=#L
local m,M=1e42,0
--Get the beginning and end of resultant transpose list.
for i=1,n do
for k,v in pairs(L[i]) do
if M<k then M=k end
if m>k then m=k end
end
end
local nt={}
for i=m,M do
local rt={}
for j=1,n do
rt[j]=L[j][i]
end
table.insert(nt,rt)
end
return nt
end
Please critique and improve this candidate solution.
I fixed a few things in your code and I think it works now as intended, I've added comments inline.
function map(func, array)
local new_array = {}
for i, v in ipairs(array) do
new_array[#new_array + 1] = func(v)
end
return new_array
end
function mapn(func, ...)
-- Variadic arguments bound to an array.
local arrays = {...}
local new_array = {}
-- Simple for-loop.
local i = 1
while true do
local arg_list = map(function(arr) return arr[i] end, arrays)
if #arg_list == 0 then
break
end
new_array[i] = func(unpack(arg_list))
i = i + 1
end
return new_array
end
-- Using 'mapn' instead of 'map' (probably how you intended).
function zip(...)
return mapn(function(...) return {...} end,...)
end
-- Same as before.
function transpose(...)
return zip(unpack(...))
end
Usage example:
for _, row in pairs(transpose({{1,2},{3,4},{5}})) do
for _, col in pairs(row) do io.write(col .. ' ') end
io.write('\n')
end
-- Output: 1 3 5
-- 2 4
The {5} in your example is being ignored because of this line:
if table.getn(arg_list) < arg_length then return new_array end
What you may want to do instead is break out of the loop only when arg_list is empty.
This will then give the result you want provided that the rows are monotonically increasing in length.
For the more general case, when later rows may be shorter than earlier ones
(e.g. {{1,2},{3,4,5},{6}}), you will need to keep track of the row lengths to allow for holes. This can be done by adding an optional argument (and extra return value) to map to indicate the maximum index i for which func(array[i]) was evaluated:
function map(func, array, len)
local new_array = {}
len = len or #array
for i=1,len do
new_array[i] = func(array[i])
end
return new_array, len
end
function mapn(func, ...)
local new_array = {}
local i=1
local arg_length = select('#', ...)
local args = {...}
while true do
local arg_list, num_results = map(function(arr) return arr[i] end, args, arg_length)
if not next(arg_list) then return new_array end
new_array[i] = func(unpack(arg_list, 1, num_results))
i = i+1
end
end
function zip(...)
return mapn(function(...) return {...} end,...)
end
function transpose(...)
return zip(unpack(...))
end

Resources