intersect multiple sets with lua script using redis.call("sinter", ...) command - lua

I want to intersect multiple sets (2 or more). The number of sets to be intersected are passed as ARGV from command line. As number of sets are being passed from command-line. So the number of arguments in redis.call() function are uncertain.
How can I do so using redis.call() function in Lua script.
However, I have written a script which has algo like:
Accepting the number of sets to be intersected in the KEYS[1].
Intersecting the first two sets by using setIntersected = redis.call(ARGV[1], ARGV[2]).
Running a loop and using setIntersected = redis.call("sinter", tostring(setIntersected), set[i])
Then finally I should get the intersected set.
The code for the above algorithm is :
local noOfArgs = KEYS[1] -- storing the number of arguments that will get passed from cli
--[[
run a loop noOfArgs time and initialize table elements, since we don't know the number of sets to be intersected so we will use Table (arrays)
--]]
local setsTable = {}
for i = 1, noOfArgs, 1 do
setsTable[i] = tostring(ARGV[i])
end
-- now find intersection
local intersectedVal = redis.call("sinter", setsTable[1], setsTable[2]) -- finding first intersection because atleast we will have two sets
local new_updated_set = ""
for i = 3, noOfArgs, 1 do
new_updated_set = tostring(intersectedVal)
intersectedVal = redis.call("sinter", new_updated_set, setsTable[i])
end
return intersectedVal
This script works fine when I pass two sets using command-line.
EG:
redic-cli --eval scriptfile.lua 2 , points:Above20 points:Above30
output:-
1) "playerid:1"
2) "playerid:2"
3) "playerid:7"
Where points:Above20 and points:Above30 are sets. This time it doesn't go through the for loop which starts from i = 3.
But when I pass 3 sets then I always get the output as:
(empty list or set)
So there is some problem with the loop I have written to find intersection of sets.
Where am I going wrong? Is there any optimized way using which I can find the intersection of multiple sets directly?

What you're probably looking for is the elusive unpack() Lua command, which is equivalent to what is known as the "Splat" operator in other languages.
In your code, use the following:
local intersectedVal = redis.call("sinter", unpack(setsTable))
That said, SINTER is variadic and can accept multiple keys as arguments. Unless your script does something in addition to just intesects, you'd be better use that instead.

Related

I am looking for a Lua find and replace logic

enter image description here
I just started working on lua scripting since a week. I have a lua file where in the logic needs to be written for a certain condition.
The condition when gets triggered
it does an iteration on one of the fields to change value from
(ABC123-XYZ) to this value
(ABC123#1-XYZ) and it keeps increasing whenever iterations happens (ABC123#2-XYZ)
I need to run a function that removes the # followed by number to change it back to (ABC123-XYZ). Looking for any advice!
Edit 1:
Below is the updated code that is written Thanks to #Piglet
I have another scenario if therr are two hashes in the variable.
local x = 'BUS144611111-PNB_00#80901#1555-122TRNHUBUS'
local b = x:gsub("#%d+","")
function remove_char(a) a=a:gsub("#%d+","")
return a;
end if string.match(x,"#")
then print('function')
print(remove_char(x));
else print(x);
end
Expected output should be
x = 'BUS144611111-PNB_00#80901-122TRNHUBUS' for the aforesaid variable
local a = "ABC123#1-XYZ"
local b = a:gsub("#%d+", "")
this will remove any # followed by or one more digits from your string.

Issue returning desired data with Lua

Wondering if I could get some help with this:
function setupRound()
local gameModes = {'mode 1','mode 2','mode 3'} -- Game modes
local maps = {'map1','map2','map3'}
--local newMap = maps[math.random(1,#maps)]
local mapData = {maps[math.random(#maps)],gameModes[math.random(#gameModes)]}
local mapData = mapData
return mapData
end
a = setupRound()
print(a[1],a[2]) --Fix from Egor
What the problem is:
`
When trying to get the info from setupRound() I get table: 0x18b7b20
How I am trying to get mapData:
a = setupRound()
print(a)
Edit:
Output Issues
With the current script I will always the the following output: map3 mode 2.
What is the cause of this?
Efficiency; is this the best way to do it?
While this really isn't a question, I just wanted to know if this method that I am using is truly the most efficient way of doing this.
First of all
this line does nothing useful and can be removed (it does something, just not something you'd want)
local mapData = mapData
Output Issues
The problem is math.random. Write a script that's just print(math.random(1,100)) and run it 100 times. It will print the same number each time. This is because Lua, by default, does not set its random seed on startup. The easiest way is to call math.randomseed(os.time()) at the beginning of your program.
Efficiency; is this the best way to do it?
Depends. For what you seem to want, yes, it's definitely efficient enough. If anything, I'd change it to the following to avoid magic numbers which will make it harder to understand the code in the future.
--- etc.
local mapData = {
map = maps[math.random(#maps)],
mode = gameModes[math.random(#gameModes)]
}
-- etc.
print(a.map, a.mode)
And remember:
Premature optimization is the root of all evil.
— Donald Knuth
You did very good by creating a separate function for generating your modes and maps. This separates code and is modular and neat.
Now, you have your game modes in a table modes = {} (=which is basically a list of strings).
And you have your maps in another table maps = {}.
Each of the table items has a key, that, when omitted, becomes a number counted upwards. In your case, there are 3 items in modes and 3 items in maps, so keys would be 1, 2, 3. The key is used to grab a certain item in that table (=list). E.g. maps[2] would grab the second item in the maps table, whose value is map 2. Same applies to the modes table. Hence your output you asked about.
To get a random game mode, you just call math.random(#mode). math.random can accept up to two parameters. With these you define your range, to pick the random number from. You can also pass a single parameter, then Lua assumes to you want to start at 1. So math.random(3) becomes actually math.random(1, 3). #mode in this case stand for "count all game modes in that table and give me that count" which is 3.
To return your chosen map and game mode from that function we could use another table, just to hold both values. This time however the table would have different keys to access the values inside it; namely "map" and "mode".
Complete example would be:
local function setupRound()
local modes = {"mode 1", "mode 2", "mode 3"} -- different game modes
local maps = {"map 1", "map 2", "map 3"} -- different maps
return {map = maps[math.random(#maps)], mode = modes[math.random(#modes)]}
end
for i = 1, 10 do
local freshRound = setupRound()
print(freshRound.map, freshRound.mode)
end

Why is Lua's arg table not one-indexed?

In the Lua command line, when I pass arguments to a script like this:
lua myscript.lua a b c d
I can read the name of my script and arguments from the global arg table. arg[0] contains the script name, arg[1] - arg[#arg] contain the remaining arguments. What's odd about this is table is that it has a value at index 0, unlike every other Lua array which starts indexing at 1. This means that when iterating over it like this:
for i,v in ipairs(arg) do print(i, v) end
the output only considers index 1-4, and does not print the script name. Also #arg evaluates to 4, not 5.
Is there any good reason for this decision? It initially took me aback, and I had to verify that the manual wasn't mistaken.
Asking why certain design decisions were made is always tricky because only the creator of the language can really answer. I guess it was chosen such that you can iterate over the arguments using ipairs and don't have to handle the first one special because it's the script name and not an argument.
#arg is meaningless anyway because it counts only the number of elements in the consecutive array section but the zeroth and negative indices are stored in the hashmap section. To obtain the actual number of elements use
local n = 0
for _ in pairs(arg) do
n = n + 1
end
At least it is documented in Programming in Lua:
A main script can retrieve its arguments in the global variable arg. In a call like
prompt> lua script a b c
lua creates the table arg with all the command-line arguments, before running the script. The script name goes into index 0; its first argument (a in the example), goes to index 1, and so on. Eventual options go to negative indices, as they appear before the script. For instance, in the call
prompt> lua -e "sin=math.sin" script a b
lua collects the arguments as follows:
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"
More often than not, the script only uses the positive indices (arg[1] and arg[2], in the example).
arg in Lua mimics argv in C: arg[0] contains the name of the script just like argv[0] contains the name of the program.
This does not contradict 1-based arrays in Lua, since the arguments to the script are the more important data. The name of the script is seldom used.

splitting lua strings into variables

I am pretty new to this, so I hope you can give me a hand.
I am programming lights, and what I like to do is take a variable from my lighting desk (a text string called "4 Mythos Stage") and split is into different variables.
to get the variables from the desk I use:
return function ()
local Layer1 = gma.user.getvar("Layer1") -- I placed "4 Mythos Stage" variable in Layer1
gma.feedback(Layer1) -- gives feedback 4 Mythos Stage
end
Now I would like to split the string into 3 new local variables named:
local number -- should produce 4
local fixturetype -- should produce Mythos
local location -- should produce Stage
i tried the following:
local number = string.match('Layer1', '%d+')
local fixturetype = string.match('Layer1', '%a+')
local location = string.match('Layer1', '%a+')
this didn't work, so can somebody please help me in the right direction. I would be really greatful.
with kind regards,
Martijn
You can assign all three variables at the same time, because Lua has multiple returns and multiple assignment. Put parentheses around each of your patterns in order to return them as captures, and combine them into a single pattern with spaces between them:
local number, fixturetype, location = string.match(Layer1, '(%d+) (%a+) (%a+)')
In case you will be using multiple spaces or tabs between the items, this pattern would be better:
local number, fixturetype, location = string.match(Layer1, '(%d+)[ \t]+(%a+)[ \t]+(%a+)')
The reason why your attempt didn't work is because string.match('Layer1', '%d+') is searching inside 'Layer1' (a string) instead of Layer1 (a variable).
But even if you corrected that, you would get 'Mythos' every time you called string.match(Layer1, '%a+') (where Layer1 == '4 Mythos Stage'). string.match always starts from the beginning of the string, unless you supply an index in the third parameter: string.match(Layer1, '%a+', 9) --> 'Stage'.
A robust solution for this task is to split the string into three "words", a word being a sequence of non-whitespace characters:
local number, fixturetype, location = string.match(Layer1, '(%S+)%s+(%S+)%s+(%S+)')

wrk executing Lua script

My question is that when I run
wrk -d10s -t20 -c20 -s /mnt/c/xxxx/post.lua http://localhost:xxxx/post
the Lua script that is only executed once? It will only put one item into the database at the URL.
-- example HTTP POST script which demonstrates setting the
-- HTTP method, body, and adding a header
math.randomseed(os.time())
number = math.random()
wrk.method = "POST"
wrk.headers["Content-Type"] = "application/json"
wrk.body = '{"name": "' .. tostring(number) .. '", "title":"test","enabled":true,"defaultValue":false}'
Is there a way to make it create the 'number' variable dynamically and keep adding new items into the database until the 'wrk' command has finished its test? Or that it will keep executing the script for the duration of the test creating and inserting new 'number' variables into 'wrk.body' ?
Apologies I have literally only being looking at Lua for a few hours.
Thanks
When you do
number = math.random
you're not setting number to a random number, you're setting it equal to the function math.random. To set the variable to the value returned by the function, that line should read
number = math.random()
You may also need to set a random seed (with the math.randomseed() function and your choice of an appropriately variable argument - system time is common) to avoid math.random() giving the same result each time the script is run. This should be done before the first call to math.random.
As the script is short, system time probably isn't a good choice of seed here (the script runs far quicker than the value from os.time() changes, so running it several times immediately after one another gives the same results each time). Reading a few bytes from /dev/urandom should give better results.
You could also just use /dev/urandom to generate a number directly, rather than feeding it to math.random as a seed. Like in the code below, as taken from this answer. This isn't a secure random number generator, but for your purposes it would be fine.
urand = assert (io.open ('/dev/urandom', 'rb'))
rand = assert (io.open ('/dev/random', 'rb'))
function RNG (b, m, r)
b = b or 4
m = m or 256
r = r or urand
local n, s = 0, r:read (b)
for i = 1, s:len () do
n = m * n + s:byte (i)
end
return n
end

Resources