Lua recursive function with variable arguments - lua

I am trying to write a string join function that will work with both tables and variable arguments as input. Here is what I have so far:
function join(separator, ...)
local result = ""
local vargs = {...}
local n = #vargs
for i = 1, n do
local varg = vargs[i]
if type(varg) == "table" then
result = join(separator, result, table.unpack(varg))
elseif varg ~= nil then
result = result..tostring(varg)
if i < n then
result = result..separator
end
end
end
return result
end
However, when I try to use it with the following input:
print(join(",", "1", "2", "3"))
print(join(",", {"a", "b", "c"}))
The output is this:
1,2,3
,a,b,c
I did not expect the , at the beginning of a,b,c.
From what I understand, it seems somehow the separator is getting added to the variable arguments when calling the function inside the function (recursiveness). But why is that? And how can I fix it? Thank you!

Try
...
if type(varg) == "table" then
varg = join(separator, table.unpack(varg))
end
if varg ~= nil then
...

Well uh sorry if it's not really useful but maybe it's because you put the: , into "" that it print the ,

Related

Does Luci have print function?

I want to print my parsing value at Luci.
Here is my code.
local val = {}
mm = Map("test", translate("For TEST"))
test=mm:section(TypedSection, "test", translate("TEST"))
test.anonymous = true
test.addremove = false
rssis = test:option(DummyValue, "rssi", translate("RSSI"))
t = test:option(DummyValue, "tx", translate("TX"))
r = test:option(DummyValue, "rx", translate("RX"))
local f = io.popen("iwpriv wlan0 stat")
for line in f:lines() do
for s in line:gmatch("(%S+)%s") do
table.insert(val, s)
end
for i, v in ipairs(val) do
end
end
f:close()
rssis:value(val[35])
if val[41] == "6M" then
t:value(val[41], translate("Disconnect"))
else
t:value(33, translate("Good"))
end
if val[49] == "6M" then
r:value(val[49], translate("DIsconnect"))
else
r:value(33, translate("GOOD"))
end
return mm
I saw the DummyValue which Creates a readonly field in the form.
So I used it instead of print function.
However it has errors "attempt to index global 'rssis' (a nil value)"
Only in lua file(not used for Luci) If i used the print function, it has no error. Does Luci has print function?
There is a luci.util.perror("blah blah") function that prints to the syslog.
you can then use the shell command "logread" to display in a console.
I guess this is what you need to debug your code.

Converting a number to its alphabetical counterpart in lua

I am trying to make a lua script that takes an input of numbers seperated by commas, and turns them into letters, so 1 = a ect, however I have not found a way to do this easily because the string libray outputs a = 97, so I have no clue where to go now, any help?
You can use string.byte and string.char functions:
string.char(97) == "a"
string.byte("a") == 97
If you want to start from "a" (97), then just subtract that number:
local function ord(char)
return string.byte(char)-string.byte("a")+1
end
This will return 1 for "a", 2 for "b" and so on. You can make it handle "A", "B" and others in a similar way.
If you need number-to-char, then something like this may work:
local function char(num)
return string.char(string.byte("a")+num-1)
end
Merely just account for the starting value of a-z in the ascii table.
function convert(...)
local ar = {...}
local con = {}
for i,v in pairs(ar) do
table.insert(con, ("").char(v+96))
end
return con;
end
for i,v in pairs(convert(1,2,3,4)) do
print(v)
end
Alternatively to these answers, you could store each letter in a table and simply index the table:
local letters = {'a','b','c'} --Finish
print(letters[1], letters[2], letters[3])
Define your encoding as follows:
encoding = [[abc...]]
in whatever order you want.
Then use it as follows
function char(i)
return encoding:sub(i,i)
end
If the list of numbers is in a table, then you may use
function decode(t)
for i=1,#t do t[i]=char(t[i]) end
return table.concat(t)
end
You can also save the decoding in a table:
char = {}
for i=1,#encoding do char[i]=encoding:sub(i,i) end
and use char[t[i]] in decode.

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.

lua - checking for duplicate data in a string

I have the following string data that I receive as input:
"route1,1234,1,no~,,route2,1234,1,no~,"
It represents two "records" of data... where each record has 4 fields.
I've built code to parse this string into it's individual columns / fields.
But the part that isn't working is when I test to see if I have any duplicates in field 2. Field 2 is the one that currently has "1234" as the value.
Here's the code:
function string:split(delimiter)
local result = { }
local from = 1
local delim_from, delim_to = string.find( self, delimiter, from )
while delim_from do
table.insert( result, string.sub( self, from , delim_from-1 ) )
from = delim_to + 1
delim_from, delim_to = string.find( self, delimiter, from )
end
table.insert( result, string.sub( self, from ) )
return result
end
local check_for_duplicate_entries = function(route_data)
local route
local route_detail = {}
local result =true
local errtxt
local duplicate = false
print("received :" ..route_data)
route = string.gsub(route_data, "~,,", "~")
route = route:sub(1,string.len(route)-2)
print("route :" ..route)
-- break up in to an array
route = string.split(route,"~")
for key, value in pairs(route) do
route_detail[key] = string.split(value,",")
end
local list_of_second_column_only = {}
for key,value in pairs(route_detail) do
local temp = value[2]
print(temp .. " - is the value I'm checking for")
if list_of_second_column_only[temp] == nil then
print("i dont think it exists")
list_of_second_column_only[key] = value[2]
print(list_of_second_column_only[key])
else
--found a duplicate.
return true
end
end
return false
end
print(check_for_duplicate_entries("route1,1234,1,no~,,route2,1234,1,no~,"))
I think where I'm going wrong is the test:
if list_of_second_column_only[temp] == nil then
I think I'm checking for key with the value temp instead of a value with the value that temp contains. But I don't know how to fix the syntax.
Also, I'm wondering if there's a more efficient way to do this.
The number of "records" i receive as input is dynamic / unknown, as is the value of the second column in each record.
Thanks.
EDIT 1
The post I was trying to use as a reference is: Search for an item in a Lua list
In the answer, they show how to test for a record in the table by value, instead of looping through the entire table...
if items["orange"] then
-- do something
end
I was playing around to try and do something similar...
This should be a bit more efficient with only one table creation and less regex matching.
The match does require that you're only interested in dups in the second field.
local function check_for_duplicate_entries(route_data)
assert(type(route_data)=="string")
local field_set = {}
for route in route_data:gmatch"([^~]*)~,?,?" do
local field = route:match",([^,]*)"
if field_set[field] then
return true
else
field_set[field] = true
end
end
return false
end
Try this. It's doing the check on the value of the second field.
I haven't looked at the efficiency.
if list_of_second_column_only[value[2]] == nil then
print("i dont think it exists")
list_of_second_column_only[value[2]] = true
print(list_of_second_column_only[value[2]])
else
--found a duplicate.
return true
end

Comparing two index tables by index-value in lua

I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.
The tables are being populated with the following code:
str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end
It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:
tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end
Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:
function comparetables(t1, t2)
matchct = 0
for i=1,#t1 do
if t1[i] == t2[i] then
matchct = matchct + 1
end
if matchct == #t1 then
return true
end
end
I tried doing
print(comparetables(tableone,tabletwo))
to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!
Additional information:
The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.
When comparing the tables, if the numbers match, Ccount increases by 1.
When comparing tables, if the value exists in a different index position, increment Pcount by 1
For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.
A slight variant on your code that should work is:
function comparetables(t1, t2)
if #t1 ~= #t2 then return false end
for i=1,#t1 do
if t1[i] ~= t2[i] then return false end
end
return true
end
However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.
-- This is not clever enough to find matching table keys
-- i.e. this will return false
-- recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on
-- an evil table like this:
-- t = {}
-- t[1] = t
function recursive_compare(t1,t2)
-- Use usual comparison first.
if t1==t2 then return true end
-- We only support non-default behavior for tables
if (type(t1)~="table") then return false end
-- They better have the same metatables
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not recursive_compare(mt1,mt2) ) then return false end
-- Check each key-value pair
-- We have to do this both ways in case we miss some.
-- TODO: Could probably be smarter and not check those we've
-- already checked though!
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not recursive_compare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not recursive_compare(v1,v2) ) then return false end
end
return true
end
Here's an example of it in use:
print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false
If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.
Something like this should do the trick:
GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index = GameState.mt.fns
function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
local retval = {}
retval[1] = a
retval[2] = b
retval[3] = c
retval[4] = d
setmetatable(retval, GameState.mt)
return retval
end
function GameState.mt.fns.print( self )
print(" GameState: ", self[1], self[2], self[3], self[4] )
end
function GameState.mt.__tostring( self )
return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end
function GameState.mt.__eq(self, other)
-- Check it's actually a GameState, and all its bits match
return getmetatable(other)==GameState.mt and
(self[1] == other[1]) and
(self[2] == other[2]) and
(self[3] == other[3]) and
(self[4] == other[4])
end
Then you'd use it like this:
state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)
print("State 1 is:")
state1:print()
print("State 2 is:")
print(state2)
print( "state1 == state2 : ", state1 == state2 )
print( "Changing state 2")
state2[1]=2
print( "state1 == state2 : ", state1 == state2 )

Resources