Searching for a table in table lua - lua

I have a table in a table structure like this:
[1] = {
[1] = {
category = "WORD",
cursor = <filtered>,
ptype = "STATEMENT",
ttype = "SERVICE",
value = "service",
<metatable> = <filtered>
},
[2] = {
category = "VARIABLE",
cursor = <filtered>,
ptype = "STATEMENT",
ttype = "IDENTIFIER",
value = "TestService",
<metatable> = <filtered>
},
[3] = {
ttype = "BRACE_BLOCK",
value = {
[1] = { ...
...
[2] = {
[1] = {
category = "WORD",
cursor = <filtered>,
ptype = "STATEMENT",
ttype = "SERVICE",
value = "service",
<metatable> = <filtered>
},
[2] = {
category = "VARIABLE",
cursor = <filtered>,
ptype = "STATEMENT",
ttype = "IDENTIFIER",
value = "HelloWorld",
<metatable> = <filtered>
},
I programmed a simply loop which looks for the first table with the ttype, filtered that information out and would like to assign the rest of the tokens until the next Service starts to corresponding service. My idea looks like that:
local found_service = 0
if found_service == 0 then
for k1, v1 in pairs (parse_tree) do
for i=1,#v1 do
if v1[i].ttype == "SERVICE" then
--Store wanted data in an object
found_service = 1
end
if (found_service == 1 and v1[i].ttype ~= "SERVICE") then
-- ->assign the rest to last found service
end
if found_service == 1 and v1[i].ttype == "SERVICE" then
-- ->Found the next service -->starting over
found_service = 0
end
end
end
end
The problem is that I stuck at index i, and v1[i] is a "SERVICE", so he enters directly the last if-clause, too. How do I end one loop-iteration (after the first if-clause). Or ist there a much better way to do this?
Thanks in advise.
Theo

I'm not sure if I understand your general idea, but here is the answer of how to skip loop body on first "SERVICE" capture event.
local found_service = 0
if found_service == 0 then
for k1, v1 in pairs (parse_tree) do
for i=1,#v1 do
if (found_service == 0 and v1[i].ttype == "SERVICE") then
--Store wanted data in an object
found_service = 1
else
if (found_service == 1 and v1[i].ttype ~= "SERVICE") then
-- ->assign the rest to last found service
end
if found_service == 1 and v1[i].ttype == "SERVICE" then
-- ->Found the next service -->starting over
found_service = 0
end
end
end
end
end
But I'm still don't get it what should be done on current record not "SERVICE" and found_service == 0. By the way, in my answer after found_service become 0 in third if, the first if could be true again.
If your idea is to build some kind of vector like:
SERVICE_1 (other ttype tables until next SERVICE)
SERVICE_2 (other ttype tables until next SERVICE)
...
In that case code could be:
local found_service = 0
if found_service == 0 then
for k1, v1 in pairs (parse_tree) do
for i=1,#v1 do
if (found_service == 0 and v1[i].ttype == "SERVICE") then
--Store wanted data in an object
found_service = 1
current_service = v1[i]
else
if (found_service == 1 and v1[i].ttype ~= "SERVICE") then
-- ->assign the rest to last found service
end
if found_service == 1 and v1[i].ttype == "SERVICE" then
-- ->Found the next service -->starting over
current_service = v1[i]
end
end
end
end
end

Related

How to implement the exercise 15.5 in pil4?

I am working on this exercise in pil4.
Exercise 15.5:
The approach of avoiding constructors when saving tables with cycles is too radical. It is
possible to save the table in a more pleasant format using constructors for the simple case, and to use
assignments later only to fix sharing and loops. Reimplement the function save (Figure 15.3, “Saving
tables with cycles”) using this approach. Add to it all the goodies that you have implemented in the previous
exercises (indentation, record syntax, and list syntax).
I have tried this with the code below, but it seems not to work on the nested table with a string key.
local function basicSerialize(o)
-- number or string
return string.format("%q",o)
end
local function save(name,value,saved,indentation,isArray)
indentation = indentation or 0
saved = saved or {}
local t = type(value)
local space = string.rep(" ",indentation + 2)
local space2 = string.rep(" ",indentation + 4)
if not isArray then io.write(name," = ") end
if t == "number" or t == "string" or t == "boolean" or t == "nil" then
io.write(basicSerialize(value),"\n")
elseif t == "table" then
if saved[value] then
io.write(saved[value],"\n")
else
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("{\n")
end
local indexes = {}
for i = 1,#value do
if type(value[i]) ~= "table" then
io.write(space2)
io.write(basicSerialize(value[i]))
else
local fname = string.format("%s[%s]",name,i)
save(fname,value[i],saved,indentation + 2,true)
end
io.write(",\n")
indexes[i] = true
end
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("}\n")
else
io.write("{}\n")
end
saved[value] = name
for k,v in pairs(value) do
if not indexes[k] then
k = basicSerialize(k)
local fname = string.format("%s[%s]",name,k)
save(fname,v,saved,indentation + 2)
io.write("\n")
end
end
end
else
error("cannot save a " .. t)
end
end
local a = { 1,2,3, {"one","Two"} ,5, {4,b = 4,5,6} ,a = "ddd"}
local b = { k = a[4]}
local t = {}
save("a",a,t)
save("b",b,t)
print()
And I got the wrong ouput.
a = {
1,
2,
3,
{
"one",
"Two",
}
,
5,
{
4,
5,
6,
}
a[6]["b"] = 4
,
}
a["a"] = "ddd"
b = {}
b["k"] = a[4]
How could I make the text ' a[6]["b"] = 4 ' jump out of the table constructor?

How to Put Data Into a Table From an Existing File

I have a program for a turtle in ComputerCraft that is meant to be a storage unit. I input an item, and it should read a file to find where to put the item. If there is a new item, it adds the information to a file.
When I do
blockTypes = {}
local file = fs.open(blockTable","r")
line = file.readAll()
table.insert(blockTypes,line)
file.close()
The information gets put in as a string. This means that I can't do
print(blockTypes[1][1])
And recieve the value that would normally be in that position.
Here's my code:
blockAmount = 3
highestVal = {2,0,5}
blockTypes = {}
function addBlock()
local file = fs.open("blockTable", "a")
file.write(name)
file.write(" = {")
file.write(highestVal[1])
file.write(",")
file.write(highestVal[2])
file.write(",")
file.write(highestVal[3])
file.writeLine("};")
file.close()
end
function getBlock()
local file = fs.open("blockTable","r")
line = file.readAll()
table.insert(blockTypes,line)
file.close()
end
--This was used to test before implementing the new file system that I am trying to get to work
blockTypesOld = {
log = {2.0,0,1};
dirt = {2,0,2};
cobblestone = {2,0,3};
iron_ingot = {2,0,4};
planks = {2,0,5};
}
pos = {0,0,0}
looking = 0
function fuel()
if turtle.getFuelLevel() < 20 then
turtle.select(16)
turtle.refuel(1)
end
end
function left()
turtle.turnLeft()
looking = looking - 1
if looking < 0 then
looking = 3
end
end
function right()
turtle.turnRight()
looking = looking + 1
if looking > 3 then
looking = 0
end
end
function forward()
fuel()
if turtle.forward() then
if looking == 0 then
pos[1] = pos[1] - 1
elseif looking == 1 then
pos[3] = pos[3] - 1
elseif looking == 2 then
pos[1] = pos[1] + 1
elseif looking == 3 then
pos[3] = pos[3] + 1
else
end
end
end
function up()
fuel()
turtle.up()
pos[2] = pos[2] + 1
end
function down()
fuel()
turtle.down()
pos[2] = pos[2] - 1
end
function goHome()
while pos[3] > 0 do
while looking > 1 do
left()
end
forward()
end
while pos[2] > 0 do
down()
end
while pos[1] > 0 do
while looking > 0 do
left()
end
forward()
end
end
function goTo(a,b,c)
goHome()
while looking < 2 or looking > 2 do
right()
end
for i = pos[1],a do
forward()
end
while looking > 3 or looking < 3 do
right()
end
for i = pos[3],c do
forward()
end
for i = pos[2],b do
up()
end
while looking < 2 or looking > 2 do
left()
end
end
while true do
turtle.select(15)
while not turtle.suck() do
sleep(1)
end
itemDetails = turtle.getItemDetail()
--Finding what mod item is from and removing corresponding labels--
--EX: "minecraft:log" becomes "log"
if itemDetails.name:match("^ae2stuff:(.+)$") then
name = itemDetails.name:match("^ae2stuff:(.+)$")
elseif itemDetails.name:match("^minecraft:(.+)$") then
name = itemDetails.name:match("^minecraft:(.+)$")
elseif itemDetails.name:match("^appliedenergistics2:(.+)$") then
name = itemDetails.name:match("^appliedenergistics2:(.+)$")
elseif itemDetails.name:match("^buildcraftbuilders:(.+)$") then
name = itemDetails.name:match("^buildcraftbuilders:(.+)$")
elseif itemDetails.name:match("^forge:(.+)$") then
name = itemDetails.name:match("^forge:(.+)$")
elseif itemDetails.name:match("^buildcraftenergy:(.+)$") then
name = itemDetails.name:match("^buildcraftenergy:(.+)$")
elseif itemDetails.name:match("^buildcraftfactory:(.+)$") then
name = itemDetails.name:match("^buildcraftfactory:(.+)$")
elseif itemDetails.name:match("^buildcraftsilicon:(.+)$") then
name = itemDetails.name:match("^buildcraftsilicon:(.+)$")
elseif itemDetails.name:match("^buildcrafttransport:(.+)$") then
name = itemDetails.name:match("^buildcrafttransport:(.+)$")
elseif itemDetails.name:match("^buildcraftcore:(.+)$") then
name = itemDetails.name:match("^buildcraftcore:(.+)$")
elseif itemDetails.name:match("^buildcraftlib:(.+)$") then
name = itemDetails.name:match("^buildcraftlib:(.+)$")
elseif itemDetails.name:match("^computercraft:(.+)$") then
name = itemDetails.name:match("^computercraft:(.+)$")
elseif itemDetails.name:match("^enderstorage:(.+)$") then
name = itemDetails.name:match("^enderstorage:(.+)$")
elseif itemDetails.name:match("^extracells:(.+)$") then
name = itemDetails.name:match("^extracells:(.+)$")
elseif itemDetails.name:match("^thermaldynamics:(.+)$") then
name = itemDetails.name:match("^thermaldynamics:(.+)$")
elseif itemDetails.name:match("^thermalexpansion:(.+)$") then
name = itemDetails.name:match("^thermalexpansion:(.+)$")
elseif itemDetails.name:match("^thermalfoundation:(.+)$") then
name = itemDetails.name:match("^thermalfoundation:(.+)$")
elseif itemDetails.name:match("^tconstruct:(.+)$") then
name = itemDetails.name:match("^tconstruct:(.+)$")
elseif itemDetails.name:match("^webdisplays:(.+)$") then
name = itemDetails.name:match("^webdisplays:(.+)$")
elseif itemDetails.name:match("^ironchest:(.+)$") then
name = itemDetails.name:match("^ironchest:(.+)$")
else
print("ERROR MOD NOT FOUND")
end
getBlock()
local elem = blockTypes[name]
--Gets fuel from fuel chest
right()
turtle.select(16)
turtle.suck(5)
turtle.select(15)
if elem then
--If the item does exist, the turtle goes to it's chest and places it in the chest
goTo(elem[1]-1, elem[2]-1, elem[3]-1)
turtle.select(15)
turtle.drop()
goHome()
right()
turtle.select(16)
turtle.drop()
turtle.select(15)
left()
else
--Creates information for new item--
addBlock()
blockAmount = blockAmount + 1
highestVal[3] = highestVal[3] + 1
if highestVal[3] > 5 then
highestVal[3] = 1
highestVal[2] = highestVal[2] + 1
end
if highestVal[2] > 4 then
highestVal[2] = 0
highestVal[1] = highestVal[1] + 2
end
blockTypes[blockAmount] = name
blockTypes[name] = {highestVal[1],highestVal[2],highestVal[3]}
local elem = blockTypes[name]
left()
turtle.select(15)
turtle.drop()
right()
turtle.select(16)
turtle.suck(2)
fuel()
turtle.drop()
goTo(1,-1,4)
--Crafts an Iron Chest for the New Item
for i = 1,3 do
turtle.select(i)
turtle.suck(1)
end
turtle.select(5)
turtle.suck(1)
turtle.select(7)
turtle.suck(1)
for i = 9,11 do
turtle.select(i)
turtle.suck(1)
end
turtle.select(6)
turtle.craft()
goTo(1,-1,3)
for i = 1,3 do
turtle.select(i)
turtle.suck(1)
end
turtle.select(5)
turtle.suck(1)
turtle.select(7)
turtle.suck(1)
for i = 9,11 do
turtle.select(i)
turtle.suck(1)
end
turtle.select(1)
turtle.craft()
goHome()
right()
turtle.select(16)
turtle.suck(5)
goTo(elem[1]-1, elem[2]-1, elem[3]-1)
turtle.select(1)
turtle.place()
goHome()
end
end
The file containing the coordinates for the items is called blockTable and consist of this:
--blockName = {xCoord,yCoord,zCoord};--
oak_stairs = {2.0,0.0,5.0};
iron_ingot = {2.0,0.0,4.0};
turtle = {2.0,0.0,5.0};
When I put a new item in, it registers the item, makes a chest, and puts it in. When I put the same item in, it goes straight to the chest without crafting a new one. However, when I reboot the turtle and put the same item in, it creates another new chest and tries to place it for the new item. I want it to be able to keep its information even after a reboot. I've been struggling with this program for a while now, any help will be appreciated. Thank you!
Reading / Writing the entire file every time you add a new block seems a bit messy, specially if you consider that you might restart the turtle in the process of writing the new file and end up with half your data being lost.
You do have a file system, so why not use that instead? Create a directory and write a new file for every block name, then save the coordinates in it. This also allows you to store more than one chest for each block-type, in case you get filthy rich happens sooner than you'd expect when using turtles to excavate huge chunks of the map.
I just realized that I didn't actually answer the question, so here goes that:
When you have a file file, you can easily read a single line with file.readLine(). This returns either the line it read as a string, or nil if you're at the end of a file. Assuming you just write three coordinates separated by spaces after the block name, you can then parse them into a table like this:
local file = fs.open('chests')
local chests = {}
while true do
local line = file.readLine()
if line then
local name, x, y, z = line:match("(%a+) ([%d.+-]+) ([%d.+-]+) ([%d.+-]+)")
chests[name] = {
tonumber(x),
tonumber(y),
tonumber(z)
}
else
break
end
end
Wrap this all up in a function for extra neatness.
Side note:
Computercraft is somewhat inconvenient for dealing with files. In regular Lua I would have done this:
local function map(f, elem, ...)
if elem then return f(elem), map(f, ...) end
end
local function readchests(file)
local res = {}
for line in io.open(file):lines() do
local name, x, y, z = line:match("(%a+)"..(" [%d.-+]+"):rep(3))
res[name]={map(tonumber, x, y, z)}
end
return res
end
If you want to keep the format of storing data as valid Lua code, you could read the entire file with file.readAll(), add "return {" at the start and "}" at the end and then just load and execute that string. In general I don't recommend that though. Id't be better if the entire file was just valid Lua code that you can read and run.
While it is not advisable to read the file every time you add to a new block as stated by DarkWiiPlayer.
To answer your question, you can read the table in and the execute the code from the string using loadstring.
To do this using the existing strings in the file you provided there is some extra work that is needed.
This extra work can be removed by adjusting your the string in the file.
blockTypes = {}
line = "oak_stairs = {2.0,0.0,5.0};"
table_name = line:match("[%a_]+%s")
do_line = assert(loadstring('local ' .. line .. ' return ' .. table_name))
table = do_line();
table.insert(blockTypes,table)
Here we get the name of the table being loaded using match.
Create a string for loadstring that builds and returns the table.
Execute the loaded string, and insert it to the blockTypes.
Alternatively you can adjust the file you are saving to that it acts as a module.
local blocktypes = {
oak_stairs = {2.0,0.0,5.0},
iron_ingot = {2.0,0.0,4.0},
turtle = {2.0,0.0,5.0},
}
return blockTypes
Then you would this to load the data:
blockTypes = require("blockTable")

Can't figure out this table arrangement

--The view of the table
local originalStats = {
Info = {Visit = false, Name = "None", Characters = 1},
Stats = {Levels = 0, XP = 0, XP2 = 75, Silver = 95},
Inventory = {
Hats = {"NoobHat"},
Robes = {"NoobRobe"},
Boots = {"NoobBoot"},
Weapons = {"NoobSword"}
}
}
local tempData = {}
--The arrangement here
function Module:ReadAll(player)
for k,v in pairs(tempData[player]) do
if type(v) == 'table' then
for k2, v2 in pairs(v) do
print(k2) print(v2)
if type(v2) == 'table' then
for k3, v3 in pairs(v2) do
print(k3) print(v3)
end
else
print(k2) print(v2)
end
end
else
print(k) print(v)
end
end
end
I'm sorry, but I can't seem to figure out how to arrange this 'ReadAll' function to where It'll show all the correct stats in the right orders.
The output is something like this:
Boots
table: 1A73CF10
1
NoobBoot
Weapons
table: 1A7427F0
1
NoobSword
Robes
table: 1A743D50
1
NoobRobe
Hats
table: 1A73C9D0
1
NoobHat
XP2
75
XP2
75
Levels
2
Levels
2
XP
0
XP
0
Here's a way to print all the elements without double or table reference values showing up.
As the name states, this function will print all the elements within a table, no matter how many nested tables there are inside it. I don't have a way to order them at the moment, but I'll update my answer if I find a way. You can also get rid of the empty spaces in the print line, I just used it so it would look neater. Let me know if it works.
function allElementsInTable(table)
for k,v in pairs(table) do
if type(table[k]) == 'table' then
print(k .. ":")
allElementsInTable(v)
else
print(" " .. k .. " = " .. tostring(v))
end
end
end
--place the name of your table in the parameter for this function
allElementsInTable(originalStats)
After more experimenting, I got this, if anyone wants it, feel free to use it.
tempData = { Info = {Visit = false, Name = 'None'},
Stats = {LVL = 0, XP = 0, Silver = 75},
Inventory = { Armors = {'BasicArmor'},
Weapons = {'BasicSword'} }
}
function Read()
for i, v in pairs(tempData['Info']) do
print(i..'\t',v)
end
----------
for i2, v2 in pairs(tempData['Stats']) do
print(i2..'\t',v2)
end
----------
for i3, v3 in pairs(tempData['Inventory']) do
print(i3..':')
for i4, v4 in pairs(v3) do
print('\t',v4)
end
end
end
Read()
Don't expect table's fields to be iterated with pairs() in some specific order. Internally Lua tables are hashtables, and the order of fields in it is not specified at all. It will change between runs, you can't have them iterated in the same order as they were filled.Only arrays with consecutive integer indices will maintain the order of their elements.

Getting random item in a table for a game

local limiteds = {
test1 = {
value = 999999999,
itemid = -1,
released = false
}
}
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
function randomitem()
local limited
repeat
local limited = limiteds[math.random(tablelength(limiteds))]
until limited.released == false
end
but it is crashing my game at the repeat loop and I cant figure out why. limited seems to be nil for some odd reason?
function randomitem()
local limited
repeat
local key
for i = 1, math.random(tablelength(limiteds)) do
key, limited = next(limiteds, key)
end
until limited.released == false
return limited
end

How to sort a hash by a function depending on values from the hash

I want to sort a hash by the following conditions:
An object that has hash[:sold] as false comes before an object that has hash[:sold] as true.
If both objects have hash[:sold] as false enter code here, then compute two variables distance_a, distance_b for the two hashes. I want object a to come before object b if distance_a < distance_b.
Code:
curr_hash.sort do |a,b|
status_a = a[1][:sold]
status_b = b[1][:sold]
if status_a == false && status_b == false
a_first_loc = [a[1][:loc_id], current_loc_id].min
a_second_loc = [a[1][:loc_id], current_loc_id].max
b_first_loc = [b[1][:loc_id], current_loc_id].min
b_second_loc = [b[1][:loc_id], current_loc_id].max
distance_a = LocationDistance.find_by(city_id: current_city.id, loc_a: a_first_loc, loc_b: a_second_loc).distance
distance_b = LocationDistance.find_by(city_id: current_city.id, loc_a: b_first_loc, loc_b: b_second_loc).distance
distance_a <=> distance_b
else return status_a == false ? 1 : 0
end
end
Borrowing #Maxim's distance function and using sort_by means the distance is only calculated once for each item.
Extracting distance as a function also has the advantage that it can be tested independently
def distance(hash)
LocationDistance.find_by(loc_a: hash[1][:loc_id], current_loc_id].min,
loc_b: [hash[1][:loc_id], current_loc_id].max,
city_id: current_city.id).distance
end
a.sort_by(|x| [true ? 1 : 0, distance(x)])
This has to do the job nicely for you:
def distance(hash)
LocationDistance.find_by(loc_a: hash[1][:loc_id], current_loc_id].min,
loc_b: [hash[1][:loc_id], current_loc_id].max,
city_id: current_city.id).distance
end
a.sort{|a,b| a[:sold] == b[:sold] ? ((distance(a) < distance(b)) ? -1 : 1) : (a[:sold] ? 1 : -1)}

Resources