Attempt to index a nil value - exception

I'm writring a lua script to realize Decision Tree.But it always throws exception in a function when creating the tree.I can't find where it is wrong even if "assert".And sorry for my poor English:P.
DTree.lua:
-- Decision Tree (ID3)
-- by Darksun2010
function calcShannonEnt(dataSet)
local labelScore={};
for _,v in pairs(dataSet) do
local curLabel=v[#v];
if (labelScore[curLabel]) then
labelScore[curLabel]=labelScore[curLabel]+1;
else
labelScore[curLabel]=1;
end
end
local result=0.0;
for _,v in pairs(labelScore) do
local prob=v/#dataSet;
result=result-prob*math.log(prob,2);
end
return result;
end
function splitDataSet(dataSet,axis,value)
local reDataSet={};
for _,v in pairs(dataSet) do
if v[axis]==value then
local reSet=table.pack(table.unpack(v,1,axis-1),
table.unpack(v,axis+1,#v));
if (reSet[1]==nil) then
table.remove(reSet,1);
end
if (reSet[#reSet]==nil) then
table.remove(reSet);
end
table.insert(reDataSet,reSet);
end
end
return reDataSet;
end
function chooseBest(dataSet)
local numFeaures=#dataSet[1]-1;
local baseEnt=calcShannonEnt(dataSet);
local maxGain,bestFeature=0,-1;
for i=1,numFeaures do
local featSet={};
local newGain=0;
for _,v in pairs(dataSet) do
featSet[v[i]]=1;
end
for k,_ in pairs(featSet) do
local subDataSet=splitDataSet(dataSet,i,k);
local prob=#subDataSet/#dataSet;
newGain=newGain+prob*calcShannonEnt(subDataSet);
end
local infoGain=baseEnt-newGain;
if (infoGain>maxGain) then
maxGain=infoGain;
bestFeature=i;
end
end
return bestFeature;
end
function majorityCnt(classList)
local classCount={};
for _,v in pairs(classList) do
if (classCount[v]) then
classCount[v]=classCount[v]+1;
else
classCount[v]=1;
end
end
local maxN,maxK=0,nil;
for k,v in pairs(classCount) do
if (v>maxN) then
maxN,maxK=v,k;
end
end
return maxK;
end
function makeDTree(dataSet,labels)
local classList={};
for _,v in pairs(dataSet) do
table.insert(classList,v[#v]);
end
local endFlag=true;
for _v in pairs(classList) do
if (not endFlag) then
break;
end
endFlag=endFlag and (v==classList[1]);
end
if (#dataSet[1]==1) then
return majorityCnt(classList);
end
local bestFeat=chooseBest(dataSet);
local bestFeatLabel=labels[bestFeat];
local myTree,featSet={},{};
for _,v in pairs(dataSet) do
featSet[v[bestFeat]]=1;
end
for k,_ in pairs(featSet) do
local subLabels=labels;
local subTree=makeDTree(splitDataSet(dataSet,bestFeat,k),subLabels);
myTree[bestFeatLabel][k]=subTree;
end
return myTree;
end
I call it as this:
>labels={'no surfacing','flippers'}
>dataSet={{1,1,'yes'},{1,1,'yes'},{1,0,'no'},{0,1,'no'},{0,1,'no'}}
>makeDTree(dataSet,labels)
DTree.lua:106: attempt to index a nil value (field '?')
stack traceback:
DTree.lua:106: in function 'makeDTree'
DTree.lua:105: in function 'makeDTree'
(...tail calls...)
[C]: in ?

Line 106 is myTree[bestFeatLabel][k]=subTree; but myTree is empty and so myTree[bestFeatLabel] is nil.

Related

lua function attempt to call global (a nil value)

I am new to lua and i am trying to make a function that receives documents and outputs a table but I am getting the above error. Why??
io.write("How many documents are we evaluating? \nInput: ")
local total_documents=io.read("*n")
io.read()
local docTable = {}
inputDocument()
function inputDocument()
local input
local file
local inputFile = {filename = nil, contents = nil, wordcount = nil}
repeat
io.write("Please enter document (filename.extension): ")
input = io.read()
file =io.open(input)
if file == nil then
print("File does not exist try again")
end
until(file ~=nil)
inputFile.filename = input
return inputFile
end
You need to define inputDocument before using it:
function inputDocument()
...
end
io.write("How many documents are we evaluating? \nInput: ")
...
inputDocument()

Redis Lua Differetiating empty array and object

I encountered this bug in cjson lua when I was using a script in redis 3.2 to set a particular value in a json object.
Currently, the lua in redis does not differentiate between an empty json array or an empty json object. Which causes serious problems when serialising json objects that have arrays within them.
eval "local json_str = '{\"items\":[],\"properties\":{}}' return cjson.encode(cjson.decode(json_str))" 0
Result:
"{\"items\":{},\"properties\":{}}"
I found this solution https://github.com/mpx/lua-cjson/issues/11 but I wasn't able to implement in a redis script.
This is an unsuccessful attempt :
eval
"function cjson.mark_as_array(t)
local mt = getmetatable(t) or {}
mt.__is_cjson_array = true
return setmetatable(t, mt)
end
function cjson.is_marked_as_array(t)
local mt = getmetatable(t)
return mt and mt.__is_cjson_array end
local json_str = '{\"items\":[],\"properties\":{}}'
return cjson.encode(cjson.decode(json_str))"
0
Any help or pointer appreciated.
There are two plans.
Modify the lua-cjson source code and compile redis, click here for details.
Fix by code:
local now = redis.call("time")
-- local timestamp = tonumber(now[1]) * 1000 + math.floor(now[2]/1000)
math.randomseed(now[2])
local emptyFlag = "empty_" .. now[1] .. "_" .. now[2] .. "_" .. math.random(10000)
local emptyArrays = {}
local function emptyArray()
if cjson.as_array then
-- cjson fixed: https://github.com/xiyuan-fengyu/redis-lua-cjson-empty-table-fix
local arr = {}
setmetatable(arr, cjson.as_array)
return arr
else
-- plan 2
local arr = {}
table.insert(emptyArrays, arr)
return arr
end
end
local function toJsonStr(obj)
if #emptyArrays > 0 then
-- plan 2
for i, item in ipairs(emptyArrays) do
if #item == 0 then
-- empty array, insert a special mark
table.insert(item, 1, emptyFlag)
end
end
local jsonStr = cjson.encode(obj)
-- replace empty array
jsonStr = (string.gsub(jsonStr, '%["' .. emptyFlag .. '"]', "[]"))
for i, item in ipairs(emptyArrays) do
if item[1] == emptyFlag then
table.remove(item, 1)
end
end
return jsonStr
else
return cjson.encode(obj)
end
end
-- example
local arr = emptyArray()
local str = toJsonStr(arr)
print(str) -- "[]"

How to send multiple data (conn:send()) with the new SDK (NodeMCU)

I've been reading the NodeMCU documentation and several closed issues about the change of SDK that previouly allowed to send multiple data streams (acting like a queued net.socket:send).
It seems a huge debate grew here (#730) and there (#993) or even here (#999). However, I did not find any convincing example of a webserver code that would allow me to read multiple html files (e.g. head.html and body.html) to display a page. Here's the example from TerryE that I tried to adapt, but with no success:
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on ("receive", function(sck, req)
local response = {}
local f = file.open("head.html","r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
end
local f = file.open("body.html","r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
end
local function sender (sck)
if #response>0 then sck:send(table.remove(response,1))
else sck:close()
end
end
sck:on("sent", sender)
sender(sck)
end )
end )
When connecting to the ESP8266, nothing loads and I get no error from the lua terminal.
For your information, head.html contains:
<html>
<head>
</head>
And body.html contains:
<body>
<h1>Hello World</h1>
</body>
</html>
I am very new to NodeMCU, please be tolerant.
Here is my solution without using tables, saving some memory:
function Sendfile(sck, filename, sentCallback)
if not file.open(filename, "r") then
sck:close()
return
end
local function sendChunk()
local line = file.read(512)
if line then
sck:send(line, sendChunk)
else
file.close()
collectgarbage()
if sentCallback then
sentCallback()
else
sck:close()
end
end
end
sendChunk()
end
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on("receive", function(sck, req)
sck:send("HTTP/1.1 200 OK\r\n" ..
"Server: NodeMCU on ESP8266\r\n" ..
"Content-Type: text/html; charset=UTF-8\r\n\r\n",
function()
Sendfile(sck, "head.html", function() Sendfile(sck, "body.html") end)
end)
end)
end)
And this is for serving single files:
function Sendfile(client, filename)
if file.open(filename, "r") then
local function sendChunk()
local line = file.read(512)
if line then
client:send(line, sendChunk)
else
file.close()
client:close()
collectgarbage()
end
end
client:send("HTTP/1.1 200 OK\r\n" ..
"Server: NodeMCU on ESP8266\r\n" ..
"Content-Type: text/html; charset=UTF-8\r\n\r\n", sendChunk)
else
client:send("HTTP/1.0 404 Not Found\r\n\r\nPage not found")
client:close()
end
end
srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
conn:on ("receive", function(client, request)
local path = string.match(request, "GET /(.+) HTTP")
if path == "" then path = "index.htm" end
Sendfile(client, path)
end)
end)
Thank you for the reply. I actually added the header you mentioned, I didn't know that was necessary and I also removed the sck argument in the sender function. My first code was actually working, I don't know what was wrong last time.
Anyway, it helped me understanding what was happening: the following code seems to concatenate the response array, since the event sent of the socket calls back the sender function (sck:on("sent", sender))
sck:send(table.remove(response,1))
In fact, table.remove(array, 1) returns the first item of the array, and removes this item of the array. Calling this line multiple times has the effect to read through it, item by item.
For the sake of simplicity, here is the code of a simple webserver able to serve multiple files:
header = "HTTP/1.0 200 OK\r\nServer: NodeMCU on ESP8266\r\nContent-Type: text/html\r\n\r\n"
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on ("receive", function(sck, req)
local response = {header}
tgtfile = string.sub(req,string.find(req,"GET /") +5,string.find(req,"HTTP/") -2 )
if tgtfile == "" then tgtfile = "index.htm" end
local f = file.open(tgtfile,"r")
if f ~= nil then
response[#response+1] = file.read()
file.close()
else
response[#response+1] = "<html>"
response[#response+1] = tgtfile.." not Found - 404 error."
response[#response+1] = "<a href='index.htm'>Home</a>"
end
collectgarbage()
f = nil
tgtfile = nil
local function sender ()
if #response>0 then sck:send(table.remove(response,1))
else sck:close()
end
end
sck:on("sent", sender)
sender()
end)
end)
This example was taken from this instructables and fixed to work with the new SDK (which do not allow multiple :send anymore). Please let me know if this code has some issues.
I don't know what is the size limit of the files though. Nevertheless, I manage to append more than 2Ko to the response variable and send it at once without any issue.

meaning of 'return proxy.PROXY_IGNORE_RESULT' in connect_server() hook

I am trying to implement connection pooling(many clients are served by few db connections) with mysql-proxy.
I took a look at
ro-pooling.lua and it seems that some actions must be done in connect_server() hook.
If I want to create a new connection:
Assign target backend index to proxy.connection.backend_ndx.
Return nothing
If I want to use already existing idle connection:
Assign target backend index to proxy.connection.backend_ndx.
Return proxy.PROXY_IGNORE_RESULT
Now, what bothers me that returning proxy.PROXY_IGNORE_RESULT from connect_server() hook seems to have no impact on connection reuse - every time a client connects, a new connection is created and eventually I run into following error: "User 'username' has exceeded the 'max_user_connections' resource (current value: 4)"
So, the question is: What is the meaning of return proxy.PROXY_IGNORE_RESULT in connect_server() hook?
Also, any reference about how mysql-proxy creates and reuses connections would be very helpful - I did not manage to find any...
Any help would be greatly appreciated :)
EDIT:
this is source of script I'm current using:
local default_backend = 1
local min_idle_connections = 1
local max_idle_connections = 4
local connections_limit = 1
local user_name = "user"
if not proxy.global.count then -- never mind this, not using it...
proxy.global.count = 0
end
function connect_server()
local backend = proxy.global.backends[1]
local pool = backend.pool
local cur_idle = pool.users[""].cur_idle_connections
print ("CONNECT SERVER:")
print("current backend:" ..proxy.connection.backend_ndx)
if cur_idle >= min_idle_connections then
print("using pooled connection")
proxy.connection.backend_ndx=0
print("current backend:" ..proxy.connection.backend_ndx)
return proxy.PROXY_SEND_RESULT
end
proxy.global.count = proxy.global.count + 1
print("Creating new connection")
proxy.connection.backend_ndx = default_backend
print("current backend:" ..proxy.connection.backend_ndx)
end
function read_handshake()
print("READ_HANDSHAKE")
print("current backend:" ..proxy.connection.backend_ndx)
end
function read_auth()
local username = proxy.connection.client.username
print("READ_AUTH: " ..username)
print("current backend:" ..proxy.connection.backend_ndx)
end
function disconnect_client()
print ("DISCONNECT CLIENT. ")
print("current backend:" ..proxy.connection.backend_ndx)
end
function read_auth_result(auth)
print("READ_AUTH_RESULT")
if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then
--auth was fine, disconnect from the server--
proxy.connection.backend_ndx = 0
print("disconnected backend after auth")
print("current backend:" ..proxy.connection.backend_ndx)
end
end
function read_query(packet)
print("READ_QUERY:")
print("current backend:" ..proxy.connection.backend_ndx)
if packet:byte() == proxy.COM_QUIT then
print("received signal QUIT")
proxy.response = {
type = proxy.MYSQLD_PACKET_OK,
}
return proxy.PROXY_SEND_RESULT
end
if proxy.connection.backend_ndx == 0 then
print("assigning backend to process query...")
proxy.connection.backend_ndx = default_backend
print("current backend:" ..proxy.connection.backend_ndx)
end
local username = proxy.connection.client.username
local cur_idle = proxy.global.backends[default_backend].pool.users[username].cur_idle_connections
print ("current idle user" ..username.." connections: " ..cur_idle)
if string.byte(packet) == proxy.COM_QUERY then
print("Query: " .. string.sub(packet, 2))
end
proxy.queries:append(1, packet)
return proxy.PROXY_SEND_QUERY
end
function read_query_result( inj )
print("READ_QUERY_RESULT:")
print("current backend:" ..proxy.connection.backend_ndx)
local res = assert(inj.resultset)
local flags = res.flags
if inj.id ~= 1 then
return proxy.PROXY_IGNORE_RESULT
end
is_in_transaction = flags.in_trans
if not is_in_transaction then
-- release the backend
print("releasing backend")
proxy.connection.backend_ndx = 0
end
print("current backend:" ..proxy.connection.backend_ndx)
end

Corona reading and writing files (first time access)

I'm trying to write a function that will read sound and music states before starting my application.
The problem is: The first time it will run, there will be no data recorded.
First, I tried the suggested JSON function from here and I got this error:
Attempt to call global 'saveTable' (a nil value)
Is there a way to test if the file exists?
Then, I tried this one:
-- THIS function is just to try to find the file.
-- Load Configurations
function doesFileExist( fname, path )
local results = false
local filePath = system.pathForFile( fname, path )
--filePath will be 'nil' if file doesn,t exist and the path is "system.ResourceDirectory"
if ( filePath ) then
filePath = io.open( filePath, "r" )
end
if ( filePath ) then
print( "File found: " .. fname )
--clean up file handles
filePath:close()
results = true
else
print( "File does not exist: " .. fname )
end
return results
end
local fexist= doesFileExist("optionsTable.json","")
if (fexist == false) then
print (" optionsTable = nil")
optionsTable = {}
optionsTable.soundOn = true
optionsTable.musicOn = true
saveTable(optionsTable, "optionsTable.json") <<<--- ERROR HERE
print (" optionsTable Created")
end
The weird thing is that I'm getting an error at the saveTable(optionsTable,"optionsTable.json"). I just can't understand why.
If you have a working peace of code that handles the first time situation it will be enough to me. Thanks.
here's some code to check if the file exist you have to try and open the file first to know if it exist
function fileExists(fileName, base)
assert(fileName, "fileName is missing")
local base = base or system.ResourceDirectory
local filePath = system.pathForFile( fileName, base )
local exists = false
if (filePath) then -- file may exist wont know until you open it
local fileHandle = io.open( filePath, "r" )
if (fileHandle) then -- nil if no file found
exists = true
io.close(fileHandle)
end
end
return(exists)
end
and for usage
if fileExists("myGame.lua") then
-- do something wonderful
end
you can refer to this link for details