How to implement the exercise 15.5 in pil4? - lua

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?

Related

Why is this string not splitting in lua

So I am working on a project and I need to split a string that would look something like this:
if (x == 2){ output("Hello") }
This is my code:
local function splitIfStatement(str)
local t = {}
t[1] = ""
t[2] = ""
t[3] = ""
local firstSplit = false
local secondSplit = false
local thirdSplit = false
str:gsub(".", function(c)
if c == "(" then
firstSplit = true
end
if firstSplit == true then
if c == "=" then
firstSplit = false
secondSplit = true
else
if c == "(" then
else
t[1] = t[1] .. c
end
end
end
if secondSplit == true then
if c == ")" then
secondSplit = false
thirdSplit = true
else
if c == "=" then
else
t[2] = t[2] .. c
end
end
end
end)
return t
end
I need to split the string at "(" so t[1] is only equal to "x" and t[2] is equal to 2 and then t[3] is equal to the "output()"
But when I run my code(note I haven't added the t[3]) t[1] returns: "x "Hello") }" and t[2] returns 2 like it should.
Anyways why isn't the split function working on the first split but it works on the second.
Thanks!
In your loop you set firstSplit true if it hits a ( this happens in 2 places in your example, before x and right before "Hello"
you can fix this by setting firstSplit true and ignore the leading if ( before you beginning the loop. Then you allow the logic you have to handle the rest.
I also notice you dont have any logic that references t[3] right now.
That all said you really should use a pattern to parse something like this.
local function splitIfStatement(str)
t = {str:match("if%s*%((%w+)%s*[=<>]+%s*(%d+)%)%s*{(.+)}")}
return t
end
this pattern is very narrow and expects a specific type of if statement, you can learn more about lua patterns here: Understanding Lua Patterns
If the input is of the form
if (AAA == BBB){ CCC("Hello") }
with possible whitespace around the fields in question, then this code works:
S=[[if (x == 2){ output("Hello") } ]]
a,b,c = S:match('%(%s*(.-)%s.-%s+(.-)%)%s*{%s*(.-)%(')
print(a,b,c)

How can I merge two tables like this? - Lua

I have these tables set like this
local tableone = {["Gold"] = 10, ["Gem"] = 5}
local tabletwo = {["Level"] = 1}
This is the code for merging
local test = {tableone, tabletwo}
print(test)
But if I try to merge the tables then the output is like this
[1] = {
["Gold"] = 10,
["Gem"] = 5
},
[2] = {
["Level"] = 1
}
And I would like to have the output like this
[1] = {
["Gold"] = 10,
["Gem"] = 5,
["Level"] = 1
}
Is this possible?
Sorry if I'm not that good at explaining.
You can do this with a simple nested loop.
local function merge(...)
local result <const> = {}
-- For each source table
for _, t in ipairs{...} do
-- For each pair in t
for k, v in pairs(t) do
result[k] = v
end
end
return result
end
local t <const> = {merge(tableone, tabletwo)}
I put the result in a table constructor due to the [1] in the question.

lua open a file with a table and change certain values

I have a file with a large table. Need to open it, get certain values and replace other values with the new ones. With this example, Females and Males Classroom a and b must equal Teachers Classroom a and b.
Read as much as I can on IO, but I cannot seem to get this to work correctly. Any help will be greatly appreciated.
--file with sample data to test, original file has 7000+lines
theData =
{
["theSchool"] =
{
["theTeachers"] =
{
["theClassroom"] =
{
["a"] = 001,
["b"] = 002,
},
},
["theFemales"] =
{
["theClassroom"] =
{
["a"] = 005,
["b"] = 010,
},
},
["theMales"] =
{
["theClassroom"] =
{
["a"] = 012,
["b"] = 014,
},
},
},
}
Then the function file looks like this
local file = io.open(filepath,'r')
local newCa = '["a"] = '..theData.theSchool.theTeachers.theClassroom.a
local newCb = '["b"] = '..theData.theSchool.theTeachers.theClassroom.b
local replaceFa = '["a"] = '..tostring(theData.theSchool.theFemales.theClassroom.a)
local replaceFb = '["b"] = '..tostring(theData.theSchool.theFemales.theClassroom.b)
local replaceMa = '["a"] = '..tostring(theData.theSchool.theMales.theClassroom.a)
local replaceMb = '["b"] = '..tostring(theData.theSchool.theMales.theClassroom.b)
file:close()
--Will it work?
print("Original Values:")
print(newCa)
print(newCb)
print(replaceFa)
print(replaceFb)
print(replaceMa)
print(replaceMb)
file = io.open(filepath,'r+')
function lines_from(filepath)
lines = {}
for line in io.lines(filepath) do
lines[#lines + 1] = line
end
return lines
end
local lines = lines_from(filepath)
for k,v in ipairs(lines) do
if v == replaceFa then
file:write(newCa..'\n')
elseif v == replaceFb then
file:write(newCb..'\n')
elseif v == replaceMa then
file:write(newCa..'\n')
elseif v == replaceMb then
file:write(newCb..'\n')
else
file:write(v..'\n')
end
--('[' .. k .. ']', v)
end
--replaceF = {[a] = newCa, [b] = newCb}
--replaceM = {[a] = newCa, [b] = newCb}
--file:write(replaceF)
--file:write(replaceM)
--print(tostring(theData.theSchool.theFemales.theClassroom.a))
file:close()
file = io.open(filepath,'r')
--Did it work?
print("New Values:")
print(theData.theSchool.theTeachers.theClassroom.a)
print(theData.theSchool.theTeachers.theClassroom.b)
print(theData.theSchool.theFemales.theClassroom.a)
print(theData.theSchool.theFemales.theClassroom.b)
print(theData.theSchool.theMales.theClassroom.a)
print(theData.theSchool.theMales.theClassroom.b)```
> I'll remove all the print things after it works.
try this solution, loading the table in one read of the file will be faster, and writing is also better with one command write
--- read
local handle = io.open("data.lua",'rb')
local data = handle:read("*a")
handle:close()
data = data .. "\n return theData"
local t = loadstring(data)()
-- edit
theData.theSchool.theTeachers.theClassroom.a = theData.theSchool.theTeachers.theClassroom.a + 1
-- write
local function concat(t, r)
r = r or {}
for k,v in pairs(t) do
if type(v)=="table" then
r[#r+1] = string.format('\t["%s"]={\n',k )
concat(v, r)
r[#r+1] = "\t},\n"
else
r[#r+1] = string.format('\t\t["%s"]=%03s,\n',k ,v )
end
end
return r
end
local r = concat(t)
local text = "theData = { \n " .. table.concat(r) .. "}"
print(text) -- just control
local handle = io.open("data.lua",'wb')
local data = handle:write(text)
handle:close()

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.

Resources