is there a way to split list to chunks? - lua

I have a very large list (~1M items) and I want to unpack it.
I saw that it is impossible to unpack a list larger than 8000 items so I wanted to chunk it.
Basically this function in python
def chunks(lst, size):
for i in range(0, len(lst), size):
yield lst[i:i+size]
(for reference I am running the lua script on redis using eval)

You could write an iterator that gives you chunks of a certain size.
-- an iterator function that will traverse over lst
function chunks(lst, size)
-- our current index
local i = 1
-- our chunk counter
local count = 0
return function()
-- abort if we reached the end of lst
if i > #lst then return end
-- get a slice of lst
local chunk = table.move(lst, i, i + size -1, 1, {})
-- next starting index
i = i + size
count = count + 1
return count, chunk
end
end
-- test
local a = {1,2,3,4,5,6,7,8,9,10,11}
for i, chunk in chunks(a, 2) do
print(string.format("#%d: %s", i, table.concat(chunk, ",")))
end
Output:
#1: 1,2
#2: 3,4
#3: 5,6
#4: 7,8
#5: 9,10
#6: 11
Read this:
https://www.lua.org/pil/7.1.html
https://www.lua.org/manual/5.4/manual.html#3.3.5

Related

Stack Overflow on Lua metatable

I used to have a construct that worked with luajit:
mytbl = setmetatable({1}, {__index = function(tbl,idx) return tbl[idx - 1] + 1 end})
Now with plain Lua 5.4 this gives me a stack overflow:
> mytbl[1000]
stdin:1: C stack overflow
stack traceback:
stdin:1: in metamethod 'index'
....
The goal is to have a table where the default is to return the index itself:
mytbl[10]
should return 10. But when I say
mytbl[3] = 5
the value of
mytbl[10]
should be 12 (the values from 1 now yield 1,2,5,6,7,8,9,10,11,12,...)
Is there a way to get this in Lua 5.4 without the stack overflow? Or should I create another function for it?
You are accessing the table within the __index. That causes another __index to be called and so on.
Use rawget when you want to access the tbl itself.
If you want to get a custom logic that does not rely on existence of elements, write your function in a way that allows for trailing recursion or write it iteratively without any recursion at all:
__index=function(tbl, idx)
local acc = 0
for i=idx-1, 1, -1 do
local th = rawget(tbl, i)
if th then
return acc + th + 1
else
acc = acc + 1
end
end
return acc
end
This is what I came up with now:
__index=function(tbl, idx)
local max = 0
for k, v in next, tbl do
if k <= idx then max = v - k end
end
return idx + max
end
I only have very few entries in tbl so this should be reasonable fast for my purposes.
My test:
mytbl[5] = 9
for i = 1, 10 do
print(mytbl[i])
end
outputs
1
2
3
4
9
10
11
12
13
14

finding minimum values from a cut table Lua 5.1.5

I have a Lua script that turns a table into segments:
function tablecut(t, n)
local result = {}
local j = 0
for i = 1, #t do
if (i-1) % n == 0 then
j = j + 1
result[j] = {}
end
result[j][#result[j]+1] = t[i]
end
return result
end
output = tablecut({'15', '62', '14', '91', '33', '55', '29', '4'}, 4)
for i = 1, #output do
for j = 1, #output[i] do
io.write(tostring(output[i][j])..' ')
end
print()
end
output:
15 62 14 91
33 55 29 4
And I am trying to find the minima from the cut lists so the output would look like this:
15 62 14 91
min = 14
33 55 29 4
min = 4
Edit: If its of any importance this is how I got it to work on Lua 5.3 but there is no table.move function on Lua 5.1. I can't remember how my thought function worked when I wrote this code.
function indexOf(array, value)
for i, v in ipairs(array) do
if v == value then
return i
end
end
return nil
end
Indicies = {}
Answers = {}
function chunks(lst, size)
local i = 1
local count = 0
return function()
if i > #lst then return end
local chunk = table.move(lst, i, i + size -1, 1, {})
i = i + size
count = count + 1
return count, chunk
end
end
local a = {91,52,19,59,38,29,58,11,717,91,456,49,30,62,43,8,17,15,26,22,13,10,2,23} --Test list
for i, chunk in chunks(a, 4) do
x=math.min(a)
print(string.format("#%d: %s", i, table.concat(chunk, ",")))
table.sort(chunk)
print(math.min(chunk[1]))
table.insert(Answers, chunk[1])
table.insert(Indicies, (indexOf(a, chunk[1])))
Output:
#1: 91,52,19,59
19
#2: 38,29,58,11
11
#3: 717,91,456,49
49
your table cut function could be simplified, and your output for loop needs you use an iterator if you want to get an output simply like you do in your 5.3 script.
function cuttable(t,n)
local binned = {}
for i=1,#t,n do
local bin = {}
for j=1,n do
table.insert(bin, t[i + ((j - 1) % n)])
end
table.insert(binned, bin)
end
return binned
end
For the for loop, we can use ipairs on the output of cuttable keeping things pretty simple, then we just do the same steps of concat then sort and print out our results.
for k, bin in ipairs(cuttable(a,4)) do
local output = "#" .. k .. ":" .. table.concat(bin, ",")
table.sort(bin)
print(output)
print(bin[1])
end
Output
#1:91,52,19,59
19
#2:38,29,58,11
11
#3:717,91,456,49
49
#4:30,62,43,8
8
#5:17,15,26,22
15
#6:13,10,2,23
2
One way to implement the cutting would be using a for loop & unpack. I have handled the case of the length not being divisible by 4 after the for loop to (1) maximize performance (check doesn't need to be done every iteration) and (2) be able to directly pass the values to math.min, which doesn't accept nils.
for i = 1, math.floor(#t / 4), 4 do
print(unpack(t, i, i+4))
print("min = " .. math.min(unpack(t, i, i+4)))
end
-- If #t is not divisible by 4, deal with the remaining elements
local remaining = #t % 4
if remaining > 0 then
print(unpack(t, #t - remaining, remaining))
print("min = " .. math.min(unpack(t, #t - remaining, remaining)))
end

How to get the latest x entries of a table in Lua?

If I have (for example) a table with 300 entries, how would I get the latest x entries only?
I was thinking of doing the next, but I'm wondering if there is a better/more optimized way to do this exact thing.
local TestTable = {}
-- Populate table
for i = 1, 300, 1 do
print('Adding: ' .. i)
table.insert(TestTable , i)
end
-- Get latest x of table
function GetLatestFromTable(OriginalTable, Amount)
local TableLength = #OriginalTable
local Retval = {}
for i = 1, Amount, 1 do
if TableLength - i <= 0 then break end -- Dont allow to go under 0
table.insert(Retval, OriginalTable[TableLength - i])
print("Adding to Retval: " .. OriginalTable[TableLength - i] .. ' (Index: ' .. TableLength - i .. ')')
end
return Retval
end
print(#TestTable)
local LatestTable = GetLatestFromTable(TestTable, 10)
print(#LatestTable)
For keys in sequence (and values are string/number) a call to table.concat() allows range parameter.
local tab = {"One", "Two", "Three", "Four", "Five"}
print(table.concat(tab, '\n', #tab - 1, #tab)) -- Last two entries
See: table.concat()
As mentioned by #Luke100000, one way could be to use Lua custom iterators. In Lua, an iterator is a special function which, when called, will return the next value. It is made possible by the fact that functions are first-class citizen in Lua and they can refer to previous scope with a mecanism named closure.
To answer the question, one could start implement a general iterator over a given range.
function IterateRange (Table, Min, Max)
local ClosureIndex = Min - 1
local ClosureMax = math.min(Max, #Table)
local function Closure ()
if (ClosureIndex < ClosureMax) then
ClosureIndex = ClosureIndex + 1
return Table[ClosureIndex]
end
end
return Closure
end
IterateRange is a function returning an anonymous function. The anonymous function does not take any parameter. It simply update the ClosureIndex index defined in the local scope of IterateRange and return the table value.
The first thing that the anonymous function do is to increment ClosureIndex. For that reason, ClosureIndex must be initialized to Min - 1.
This function works as one might expect:
TestTable = {}
for i = 1, 300, 1 do
print('Adding: ' .. i)
table.insert(TestTable , i)
end
for Value in IterateRange(TestTable, 290, 300) do
print(Value)
end
290
291
292
293
294
295
296
297
298
299
300
Now, it's trivial to reuse this general iterator to iterate over the last N entries of a given table:
function IterateLastEntries (Table, Count)
local TableSize = #Table
local StartIndex = (TableSize - Count)
return IterateRange(Table, StartIndex, TableSize)
end
It also work as one might expect:
TestTable = {}
for i = 1, 300, 1 do
print('Adding: ' .. i)
table.insert(TestTable , i)
end
for Value in IterateLastEntries(TestTable, 10) do
print(Value)
end
290
291
292
293
294
295
296
297
298
299
300
And finally, to summarize all this in a fully copy & pasteable solution:
TestTable = {}
for i = 1, 300, 1 do
print('Adding: ' .. i)
table.insert(TestTable , i)
end
function IterateRange (Table, Min, Max)
local ClosureIndex = Min - 1
local ClosureMax = math.min(Max, #Table)
local function Closure ()
if (ClosureIndex < ClosureMax) then
ClosureIndex = ClosureIndex + 1
return Table[ClosureIndex]
end
end
return Closure
end
function IterateLastEntries (Table, Count)
local TableSize = #Table
local StartIndex = (TableSize - Count)
return IterateRange(Table, StartIndex, TableSize)
end
for Value in IterateLastEntries(TestTable, 10) do
print(Value)
end
This should return:
290
291
292
293
294
295
296
297
298
299
300
I will let the OP update the code in order to achieve the same results for 30 entries.

How to iterate through results in sets?

In my Tabletop Simulator mod I have a bag, when something is dropped in the bag the emptyContents() function is called. For example I can drop 15 dice in the bag.
In the emptyContents() function I iterate over the objects in the bag. But as you can see I have to put in multiple if statements to catch the amount of dice put in because I want the dice to be spawned on different positions.
The contents variable is the amount of dice in the bag.
function emptyContents()
contents = self.getObjects()
for i, _ in ipairs(self.getObjects()) do
if i <= 6 then
self.takeObject(setPosition(5, -3))
elseif i <= 12 then
self.takeObject(setPosition(12.4,-5))
elseif i <= 18 then
self.takeObject(setPosition(19.8,-7))
end
end
end
How can I make the function less static? Because now I need to write if statements for each set of 6 dice.
maybe you can add a config like this:
local t = {
{6, 5, -3},
{12, 12.4, -5},
{18, 19.8, -7},
}
function emptyContents()
contents = self.getObjects()
for i, _ in ipairs(self.getObjects()) do
for _, v in ipairs(t) do
local l, p1, p2 = unpack(v)
if i <= l then
self.takeObject(setPosition(p1, p2))
break
end
end
end
end

Explain why unpack() returns different results in Lua

The following script finds prime numbers in a range from 1 to 13.
When I explicitly iterate over the table that contains the results I can see that the script works as expected. However, if I use unpack() function on the table only the first 3 numbers get printed out.
From docs: unpack is "a special function with multiple returns. It receives an array and returns as results all elements from the array, starting from index 1".
Why is it not working in the script below?
t = {}
for i=1, 13 do t[i] = i end
primes = {}
for idx, n in ipairs(t) do
local isprime = true
for i=2, n-1 do
if n%i == 0 then
isprime = false
break
end
end
if isprime then
primes[idx] = n
end
end
print('loop printing:')
for i in pairs(primes) do
print(i)
end
print('unpack:')
print(unpack(primes))
Running
$ lua5.3 primes.lua
loop printing:
1
2
3
5
7
13
11
unpack:
1 2 3
Change
primes[idx] = n
to
primes[#primes+1] = n
The reason is that idx is not sequential as not every number is a prime.

Resources