pico8 lua: unexpeted change of size of an array - lua

I am seeing some unexpected behaviour when using a table as an array in pico8 lua when compared to regular PUC-Rio lua
If I run the following code using PUC-Rio lua5.4.4 (ubuntu)
local t={}
for i=1,10 do t[i] = i*10 end
t[2]=nil
t[4]=nil
t[6]=nil
t[8]=nil
print()
for i=1,#t do print(t[i]) end
I get the expected output
10
nil
30
nil
50
nil
70
nil
90
100
However if i run the same code with pico-8 I get:
10
This appears triggered only when I delete (ie set to nil) the t[8] element. if I comment out that line then I get the expected on pico8
10
nil
30
nil
50
nil
70
80
90
100
It appears, in pico8 lua, that the #t size of the array changes to 1 when the t[8] element is set to nil.

Both are expected results, the length operator # in lua returns a number n where t[n] ~= nil and t[n+1] == nil, if there are holes (nil value) inside, the result is undefined.
To find the maximum numeric index, in lua 5.1 you can use table.maxn, in other versions you have to write one.
table.maxn = function(t)
local n = 0
for k, v in pairs(t) do
if type(k) == 'number' and k > n then
n = k
end
end
return n
end

It seem that the size operator #t is just not well defined in lua in the presence of nil values.
https://www.lua.org/pil/19.1.html
"undefined behaviour" in a scripting language.. Nice.

Related

Reliable way of getting the exact decimals from any number

I'm having problem returning spesific amount of decimal numbers from this function, i would like it to get that info from "dec" argument, but i'm stuck with this right now.
Edit: Made it work with the edited version bellow but isn't there a better way?
local function remove_decimal(t, dec)
if type(dec) == "number" then
for key, num in pairs(type(t) == "table" and t or {}) do
if type(num) == "number" then
local num_to_string = tostring(num)
local mod, d = math.modf(num)
-- find only decimal numbers
local num_dec = num_to_string:sub(#tostring(mod) + (mod == 0 and num < 0 and 3 or 2))
if dec <= #num_dec then
-- return amount of deciamls in the num by dec
local r = d < 0 and "-0." or "0."
local r2 = r .. num_dec:sub(1, dec)
t[key] = mod + tonumber(r2)
end
end
end
end
return t
end
By passing the function bellow i want a result like this:
result[1] > 0.12
result[2] > -0.12
result[3] > 123.45
result[4] > -1.23
local result = remove_decimal({0.123, -0.123, 123.456, -1.234}, 2)
print(result[1])
print(result[2])
print(result[3])
print(result[4])
I tried this but it seems to only work with one integer numbers and if number is 12.34 instead of 1.34 e.g, the decimal place will be removed and become 12.3. Using other methods
local d = dec + (num < 0 and 2 or 1)
local r = tonumber(num_to_string:sub(1, -#num_to_string - d)) or 0
A good approach is to find the position of the decimal point (the dot, .) and then extract a substring starting from the first character to the dot's position plus how many digits you want:
local function truncate(number, dec)
local strnum = tostring(number)
local i, j = string.find(strnum, '%.')
if not i then
return number
end
local strtrn = string.sub(strnum, 1, i+dec)
return tonumber(strtrn)
end
Call it like this:
print(truncate(123.456, 2))
print(truncate(1234567, 2))
123.45
1234567
To bulk-truncate a set of numbers:
local function truncate_all(t, dec)
for key, value in pairs(t) do
t[key] = truncate(t[key], dec)
end
return t
end
Usage:
local result = truncate_all({0.123, -0.123, 123.456, -1.234}, 2)
for key, value in pairs(result) do
print(key, value)
end
1 0.12
2 -0.12
3 123.45
4 -1.23
One could use the function string.format which is similar to the printf functions from C language. If one use the format "%.2f" the resulting string will contain 2 decimals, if one use "%.3f" the resulting string will be contain 3 decimals, etc. The idea is to dynamically create the format "%.XXXf" corresponding to the number of decimal needed by the function. Then call the function string.format with the newly created format string to generate the string "123.XXX". The last step would be to convert back the string to a number with the function tonumber.
Note that if one want the special character % to be preserved when string.format is called, you need to write %%.
function KeepDecimals (Number, DecimalCount)
local FloatFormat = string.format("%%.%df", DecimalCount)
local String = string.format(FloatFormat, Number)
return tonumber(String)
end
The behavior seems close to what the OP is looking for:
for Count = 1, 5 do
print(KeepDecimals(1.123456789, Count))
end
This code should print the following:
1.1
1.12
1.123
1.1235
1.12346
Regarding the initial code, it's quite straight-forward to integrate the provided solution. Note that I renamed the function to keep_decimal because in my understanding, the function will keep the requested number of decimals, and discard the rest.
function keep_decimal (Table, Count)
local NewTable = {}
local NewIndex = 1
for Index = 1, #Table do
NewTable[NewIndex] = KeepDecimal(Table[Index], Count)
NewIndex = NewIndex + 1
end
return NewTable
end
Obviously, the code could be tested easily, simply by copy and pasting into a Lua interpreter.
Result = keep_decimal({0.123, -0.123, 123.456, -1.234}, 2)
for Index = 1, #Result do
print(Result[Index])
end
This should print the following:
0.12
-0.12
123.46
-1.23
Edit due to the clarification of the need of truncate:
function Truncate (Number, Digits)
local Divider = Digits * 10
local TruncatedValue = math.floor(Number * Divider) / Divider
return TruncatedValue
end
On my computer, the code is working as expected:
> Truncate(123.456, 2)
123.45

What's the proper way to find the last parcel of an array?

I'm doing some codewars and arr[index] keeps returning nil. I've done this a few different ways, and I'm sure the array exists, as well as the index. What's wrong here, is it syntax?
As I've mentioned in the title, I want to find the last digit of the array.
if arr[index] <= 0 then
return -1
end
Full Code:
local solution = {}
function solution.newAvg(arr, navg)
local currentAverage = 0
local index = 0
for i, v in pairs(arr) do
index = i
currentAverage = currentAverage + v
end
if arr[index] <= 0 then
return -1
end
return math.ceil(((index+1) * navg) - currentAverage)
end
return solution
I see two issues with your code:
Edge case: Empty array
If arr = {}, the loop for i, v in pairs(arr) do won't execute at all and index will remain at 0. Since arr is empty, arr[0] will be nil and arr[index] <= 0 will fail with an "attempt to compare a nil value" error.
Lack of ordering guarantee
You use pairs rather than ipairs to loop over what I assume is a list. This means keys & values might be traversed in any order. In practice pairs usually (but not always!) traverses the list part of a table in the same order as ipairs, but the reference manual clearly states that you can't rely on no specific order. I don't think CodeWars is this advanced but consider the possibility that pairs may be overridden to deliberately shuffle the order of traversal in order to check whether you're relying on the dreaded "undefined behavior". If this is the case, your "last index" might actually be any index that happens to be visited last, obviously breaking your algorithm.
Fixes
I'll assume arr is an "array", that is, it only contains keys from 1 to n and all values are non-nil (i.e. there are no holes). Then you can (and should!) use ipairs to loop over the "array":
for i, v in ipairs(arr) do ... end
I don't know the problem statement so it's hard to tell how an empty array should be handled. I'll assume that it should probably return 0. You could add a simply early return at the top of the function for that: if arr[1] == nil then return 0 end. Nonempty arrays will always have arr[1] ~= nil.
I want to find the last digit of the array.
If you mean the last integer (or entry/item) of the array:
local last = array[#array]
If you mean the last digit (for example array = {10, 75, 44, 62} and you want 2), then you can get the last item and then get the last digit using modulo 10:
local last = array[#array] % 10
for i, v in pairs(arr) do
index = i
currentAverage = currentAverage + v
end
Just a reminder:
#array returns the number of items in a table.
In Lua, arrays are implemented using integer-indexed tables.
There's a difference between pairs() and ipairs().
Regarding point 3 above, the following code:
local array = {
[1] = 12,
[2] = 32,
[3] = 41,
[4] = 30,
[5] = 14,
[6] = 50,
[7] = 62,
[8] = 57
}
for key, value in pairs(array) do
print(key, value)
end
produces the following output (note that the order of keys is not respected):
8 57
1 12
2 32
3 41
4 30
5 14
6 50
7 62
while the same code above with pairs() replaced with ipairs() gives:
1 12
2 32
3 41
4 30
5 14
6 50
7 62
8 57
So, this might be the cause of your problem.

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.

lua - table maintenance (particle system related)

The update() function below gets called on every frame of a game. If the Drop particle has y value greater than 160 I want to remove it from the table. The problem is that I get "attempt to compare number with nil" errors, on the line notated below:
local particles = {};
function update()
local num = math.random(1,10);
if(num < 4) then
local drop = Drop.new()
table.insert ( particles, drop );
end
for i,val in ipairs(particles) do
if(val.y > 160) then --ERROR attempt to compare number with nil
val:removeSelf(); --removeSelf() is Corona function that removes the display object from the screen
val = nil;
end
end
end
What am I doing wrong? Obviously val is nil, but I don't understand why the table iteration would find val in the first place since I set it to nil when it's y value gets larger than 160.
Thanks for the answers, they were all helpful. Here is what ended up working for me. The table.remove call is necessary to keep the loop running properly.
for i = #particles, 1, -1 do
if particles[i].y > 160 then
local child = table.remove(particles, i)
if child ~= nil then
display.remove(child)
child = nil
end
end
end
You're looking in the wrong place, the problem isn't that val is nil, it's val.y that's nil. See this example:
> x=nil
> if x.y > 10 then print("test") end
stdin:1: attempt to index global 'x' (a nil value)
stack traceback:
stdin:1: in main chunk
[C]: ?
> x={y=nil}
> if x.y > 10 then print("test") end
stdin:1: attempt to compare number with nil
stack traceback:
stdin:1: in main chunk
[C]: ?
Also, when you set val to nil, that may not be doing anything (I believe val is a copy):
> t={"a", "b", "c", "d"}
> for i,val in ipairs(t) do print(i, val) end
1 a
2 b
3 c
4 d
> for i,val in ipairs(t) do if i==3 then print("delete", val); val=nil end end
delete c
> for i,val in ipairs(t) do print(i, val) end
1 a
2 b
3 c
4 d
Edit: to delete an element from a table, you want table.remove:
> t[3]=nil
> for i,val in ipairs(t) do print(i, val) end
1 a
2 b
> t[3]="c"
> for i,val in ipairs(t) do print(i, val) end
1 a
2 b
3 c
4 d
> for i,val in ipairs(t) do if i==3 then print("delete", val); table.remove(t, i) end end
delete c
> for i,val in ipairs(t) do print(i, val) end
1 a
2 b
3 d
JeffK's solution should work, but I think the reason it will work is not because of the fact that he's traversing the list backwards, but because he is setting particles[i] = nil instead of val = nil. If you run val = nil you're only setting the local copy of val to nil, not the entry in the table.
Try this:
for i,val in ipairs(particles) do
if(val.y > 160) then
particles[i]:removeSelf()
particles[i] = nil;
end
end
I don't think you are allowed to modify the contents of a table while ipairs is iterating through it. I vaguely remember reading that my hardcopy of the Lua 5.1 Reference Manual, but I can't seem to locate it now. When you set val to nil, it removes an element from the particles table.
You might try processing the table in reverse, since your function is doing a full sweep of the particles table, conditionally removing some items:
for x = #particles, 1, -1 do
if particles[x].y > 160 then
particles[x]:removeSelf()
particles[x] = nil
end
end

How do I get the number of keys in a hash table in Lua?

myTable = {}
myTable["foo"] = 12
myTable["bar"] = "blah"
print(#myTable) -- this prints 0
Do I actually have to iterate through the items in the table to get the number of keys?
numItems = 0
for k,v in pairs(myTable) do
numItems = numItems + 1
end
print(numItems) -- this prints 2
I experimented with both the # operator and table.getn(). I thought table.getn() would do what you wanted but as it turns out it's returning the same value as #, namely 0. It appears that dictionaries insert nil placeholders as necessary.
Looping over the keys and counting them seems like the only way to get the dictionary size.
The length operator:
The length of a table t is defined to be any integer index n such that t[n] is not nil and t[n+1] is nil; moreover, if t[1] is nil, n can be zero. For a regular array, with non-nil values from 1 to a given n, its length is exactly that n, the index of its last value. If the array has "holes" (that is, nil values between other non-nil values), then #t can be any of the indices that directly precedes a nil value (that is, it may consider any such nil value as the end of the array).
so only way to get length is iterate over it.
Aside from iterating through the keys manually, it is simple to automatically keep track of it via metamethods. Considering you probably don't want to keep track of every table you make, you can just write a function that will allow you to convert any table into a key-countable object. The following isn't perfect, but I think it would illustrate the point:
function CountedTable(x)
assert(type(x) == 'table', 'bad parameter #1: must be table')
local mt = {}
-- `keys` will represent the number of non integral indexes
-- `indxs` will represent the number of integral indexes
-- `all` will represent the number of both
local keys, indxs, all = 0, 0, 0
-- Do an initial count of current assets in table.
for k, v in pairs(x) do
if (type(k) == 'number') and (k == math.floor(k)) then indxs = indxs + 1
else keys = keys + 1 end
all = all + 1
end
-- By using `__nexindex`, any time a new key is added, it will automatically be
-- tracked.
mt.__newindex = function(t, k, v)
if (type(k) == 'number') and (k == math.floor(k)) then indxs = indxs + 1
else keys = keys + 1 end
all = all + 1
t[k] = v
end
-- This allows us to have fields to access these datacounts, but won't count as
-- actual keys or indexes.
mt.__index = function(t, k)
if k == 'keyCount' then return keys
elseif k == 'indexCount' then return indxs
elseif k == 'totalCount' then return all end
end
return setmetatable(x, mt)
end
Examples of using this would include:
-- Note `36.35433` would NOT be counted as an integral index.
local foo = CountedTable { 1, 2, 3, 4, [36.35433] = 36.35433, [54] = 54 }
local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }
local foobar = CountedTable { 1, 2, 3, x = 'x', [true] = true, [64] = 64 }
print(foo.indexCount) --> 5
print(bar.keyCount) --> 4
print(foobar.totalCount) --> 6
Live Working Example
Hope this helped! :)
Lua stores table as two separates parts : a hash part and an array part, the len operator only deal with the array part, meaning value indexed by a number value, plus using rules mentioned below, so you don't have any choice for counting "hash" value you need to iterate over the table with pairs() function.

Resources