This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
HDeffo's profile picture

Function not returning multiple values

Started by HDeffo, 12 March 2015 - 01:56 PM
HDeffo #1
Posted 12 March 2015 - 02:56 PM
I am attempting to make a function return an unpacked table. Testing showed the table does have multiple values. As well separating the function from the rest of the program it returned values as expected…so something else in the program is making it not return correctly. I have spent some hours trying to fix this so figured maybe someone can give me some new ideas on whats wrong. Currently it is only returning the first value in the table "concat"

Spoiler

local snipeComment = {"--[[","{{FILESNIPE VAR HANDLE}}"," ","please do not edit anything in this comment block"," ","{ ,}","]]",}
local mv = {
  __index = function(t,k)
	local file = fs.open(k,"r")
	if not file then return nil end
	local total = {}
	for line in file.readLine do
	  table.insert(total,line)
	end
	file.close()
	return total
  end;
  __newindex = function(t,k,v)
	if type(v)==nil then
	  fs.delete(k)
	else
	  local file = fs.open(k,"w")
	  for i=1,#v do
		file.writeLine(v[i])
	  end
	  file.close()
	end
  end;
}
local function init()
  fileSnipeNew = nil
  fileSnipeNew = setmetatable({
	fileData = {},
	fileName = "nil",
	fileMode = false,
	bypass = false,
	fileIsVar = false,
	handle = function(self,bool)
	  if not bool and not self.bypass and not self.fileIsVar and self.fileData[2]=="{{FILESNIPE VAR HANDLE}}" then
		self.fileIsVar = {}
		for i=1,7 do
		 table.insert(self.fileIsVar,self.fileData[1])
		 table.remove(self.fileData,1)
		end
	  elseif not bypass and self.fileIsVar then
		for i=#self.fileIsVar,1,-1 do
		  table.insert(self.fileData,1,self.fileIsVar[i])
		end
		self.fileIsVar = false
	  end
	end,
	},{
	__index = function(t,k)
	  if t.fileMode then t.fileData=snipeAPIFiles[t.fileName] end	  
	  t:handle(true)
	  if t.fileData[2]~="{{FILESNIPE VAR HANDLE}}" then
		for i=#snipeComment,1,-1 do
		  table.insert(t.fileData,1,snipeComment[i])
		end
	  end
	  local var = textutils.unserialize(t.fileData[6]) or {}
	  return tostring(var[k]):sub(1,8)=="function" and loadstring("return "..var[k])() or var[k]
	end;
	__newindex = function(t,k,v)
	  if type(v)=="function" then
		func = setmetatable({},{
		  __call = function(_,...)
			return rawequal(t,({...})[1]) and v(...) or v(t,...)
		  end;
		})
		rawset(t,k,func)
	  else
		if t.fileMode then t.fileData=snipeAPIFiles[t.fileName] end
		t:handle(true)
		if t.fileData[2]~="{{FILESNIPE VAR HANDLE}}" then
		  for i=#snipeComment,1,-1 do
			table.insert(t.fileData,1,snipeComment[i])
		  end
		  snipeAPIFiles[t.fileName] = t.fileData
		end
		local var=textutils.unserialize(t.fileData[6]) or {}
		var[k] = v
		t.fileData[6] = textutils.serialize(var):gsub("\n","")
		snipeAPIFiles[t.fileName] = t.fileData
	  end
	end;
  })
  fileSnipeNew.getLine = function(self,...)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	local concat = {}
	for _,line in ipairs(arg) do
	  line=line<0 and #self.fileData+(line+1) or line
	  table.insert(concat,line<#self.fileData and line>0 and self.fileData[line])
	end
	return unpack(concat)
  end
  fileSnipeNew.setLine = function(self,line,data)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	line=line<0 and #self.fileData+(line+1) or line
	if line>#self.fileData then
	  for i=1,line-#self.fileData do
		table.insert(self.fileData,"")
	  end
	elseif line<1 then
	  repeat
		table.insert(self.fileData,1,"")
		line = line+1
	  until line>0
	end
	self.fileData[line] = data
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.len = function(self)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	return #self.fileData
  end
  fileSnipeNew.getFile = function(self)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	return self.fileData
  end
  fileSnipeNew.setFile = function(self,...)
	self:handle(false)
	self.fileData=type(arg[1])=="table" and arg[1] or arg
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.remLine = function(self,...)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	arg=type(arg[1])=="table" and arg[1] or arg
	table.sort(arg,function(a,B)/>/>/>/>/> return a>b end)
	for _,line in ipairs(arg) do
	  table.remove(self.fileData,line)
	end
	self:handle(true)
	snipeAPIFiles[self.fileName]=self.fileData
  end
  fileSnipeNew.insert = function(self,line,data)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	if type(data)=="string" then table.insert(self.fileData,line,data)
	elseif type(data)=="table" then
	  for i=#data,1,-1 do
		table.insert(self.fileData,line,data[i])
	  end
	end
	self:handle(true)
	snipeAPIFiles[self.fileName]=self.fileData
  end
  fileSnipeNew.find = function(self,str,whole)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	local instances = {}
	for i=1,#self.fileData do
	  local t=1
	  local cut = {string.find(self.fileData[i],str,t)}
	  while cut[1] do
		local s = self.fileData[i]:sub(cut[2]+1,cut[2]+1)
		if not whole or s==" " or s=="?" or s=="," or s=="!" or s=="." or s=="\"" or s=="'" or s=="(" or s==")" or s=="=" or s=="" then
		  table.insert(instances,{i,cut[1]})
		end
		t=cut[2]+1
		cut = {string.find(self.fileData[i],str,t)}
	  end
	end
	return instances
  end
  fileSnipeNew.swap = function(self,line1,line2)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	if line1<=#self.fileData and line2<=#self.fileData then
	  self.fileData[line1],self.fileData[line2]=self.fileData[line2],self.fileData[line1]
	  self:handle(true)
	  snipeAPIFiles[self.fileName]=self.fileData
	end
  end
  fileSnipeNew.replace = function(self,old,new,index,whole)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	local tbl = self:find(old,whole)
	local segment = self.fileData[tbl[index][1]]
	segment = segment:sub(1,tbl[index][2]-1)..new..segment:sub(tbl[index][2]+old:len(),segment:len())
	print(tostring(tbl[index][1]).." : "..tostring(segment))
	self.fileData[tbl[index][1]] = segment
  end
  fileSnipeNew.replaceAll = function(self,old,new,whole)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	local tbl = self:find(old,whole)
	for i=#tbl,1,-1 do
	  local segment = self:getLine(tbl[i][1])
	  segment = segment:sub(1,tbl[i][2]-1)..new..segment:sub(tbl[i][2]+old:len(),segment:len())
	  self:setLine(tbl[i][1],segment)
	end
  end
  fileSnipeNew.append = function(self,...)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	arg=type(arg[1])=="table" and arg[1] or arg
	for _,data in pairs(arg) do
	  table.insert(self.fileData,data)
	end
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.combineLines = function(self,line1,line2,sep)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	sep = sep or " "
	self.fileData[line1] = self.fileData[line1]..sep..self.fileData[line2]
	table.remove(self.fileData,line2)
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.moveLine = function(self,from,to)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	from=(from<0 and #self.fileData+(from+1) or from) + (from>to and 1 or 0)
	to=(to<0 and #self.fileData+(to+1) or to) + (to>from and 1 or 0)
	table.insert(self.fileData,to+1,self.fileData[from])
	table.remove(self.fileData,from)
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.concatLine = function(self,line,data)
	if self.fileMode then self.fileData=snipeAPIFile[self.fileName] end
	self:handle(false)
	line=line<0 and #self.fileData+(line+1) or line
	self.fileData[line] = self.fileData[line]..data
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.sepLines = function(self,line,sep)
	if self.fileMode then self.fileData=snipeAPIFile[self.fileName] end
	self:handle(false)
	table.insert(self.fileData,line+1,self.fileData[line]:sub(sep,self.fileData[line]:len()))
	self.fileData[line] = self.fileData[line]:sub(1,sep-1)
	self:handle(true)
	snipeAPIFiles[self.fileName] = self.fileData
  end
  fileSnipeNew.rawVar = function(self,key)
	if self.fileMode then self.fileData=snipeAPIFile[self.fileName] end
	local var = textutils.unserialize(self.fileData[6])
	return type(var)~="nil" and var[key]
  end
end
snipeAPIFiles = setmetatable({},mv)
function open(sFile,mode,bypass)
  init()
  fileSnipeNew.fileName = sFile
  bypass,mode=type(mode)~="string" and mode or bypass,nil or bypass,type(mode)=="string" and mode or nil
  fileSnipeNew.fileData = snipeAPIFiles[sFile] or {}
  fileSnipeNew.fileMode=mode=="d"
  fileSnipeNew.bypass = bypass
  return fileSnipeNew
end



function which is failing:

  fileSnipeNew.getLine = function(self,...)
	if self.fileMode then self.fileData=snipeAPIFiles[self.fileName] end
	self:handle(false)
	local concat = {}
	for _,line in ipairs(arg) do
	  line=line<0 and #self.fileData+(line+1) or line
	  table.insert(concat,line<#self.fileData and line>0 and self.fileData[line])
	end
	return unpack(concat)
  end



I understand this is a ton of tightly packed messy code so I don't expect too many people to try and help as its difficult to even read but thanks to anyone who does try to figure this out in advance!



extra note: there may be a few bugs after the replace function as I am currently going through and revamping the code. This is the dev of the update and can't continue update/bug cracking without fixing this return issue. These bugs shouldn't be of any effect to the returning issue since the function only references to items defined before it.


EDIT:
to save effort I am assuming the cause is in lines 65-72 where im saving the function as a table using a __call metamethod. I don't know why this would cause it to not return multiple values though and I haven't managed to isolate the exact cause.
Edited on 12 March 2015 - 02:32 PM
Dragon53535 #2
Posted 12 March 2015 - 04:46 PM
Couple of questions
1. I don't believe self is going to be exactly passed in the way that you want because it's not in the __index metatable?
2. Where in the love of all that is holy is arg defined? I noticed that down the way is it being defined inside a different function as a global variable, but why?

If you're thinking that … is renamed to the variable of arg, then you're mistaken, you need to insert that into a table.


local function foo(...)
  local arg = {...}
end


Edit: If for some reason i'm also mistaken, you also need to catch the other values returned. If you're doing this:

local function derp()
  return unpack({1,2,3,4,5,6,7})
end
local a = derp()
then a will ONLY be 1, and 2,3,4,5,6,7 will disappear
Edited on 12 March 2015 - 03:48 PM
HDeffo #3
Posted 12 March 2015 - 05:13 PM
Already tested and self is passed correctly dragon. It is defined by newindex as t and sent through the call using that variable previously passed. Check the __newindex metamethod for how it handles new indexes that are functions.


arg is predefined it's a table version of … And doesn't need to be defined. Also tested and works just fine.

I am using methods to catch all variables to test the issue save the file I posted as "fileSnipe",try opening lua in a computer and type

os.loadAPI("fileSnipe")
file = fileSnipe.open("fileSnipe")
print(file.getLine(1,2,3,4))

Print can handle multiple arguments as if they were concat using .. So this should work just fine however it prints line one only even though the unpacked table contains all 4 lines.i also tested using

tbl = {file.getLine(1,2,3,4)}
print(textutils.serialize(tbl))
Edited on 12 March 2015 - 04:15 PM
MKlegoman357 #4
Posted 12 March 2015 - 07:40 PM
Haven't tested yet, but I believe it is because of this:


__newindex = function(t,k,v)
          if type(v)=="function" then
                func = setmetatable({},{
                  __call = function(_,...)
                        return rawequal(t,({...})[1]) and v(...) or v(t,...) <-- right here
                  end;
                })
                rawset(t,k,func)
          else

The thing is 'and', 'or' and other operators don't accept a 'varlist' (variable list), but rather take the first variable from it. I'd suggest to make an if statement and two different return statements. Also, I believe with just a function it would be more efficient than with a function and a metatable:


if type(v)=="function" then
  func = function(...)
    if rawequal(t,select(1, ...)) then <-- there is a 'select' function specifically made for that in Lua ;)/>, looks cleaner
      return v(...)
    else
      return v(t,...)
    end
  end

  rawset(t,k,func)
else
Edited on 12 March 2015 - 08:21 PM
HDeffo #5
Posted 12 March 2015 - 07:54 PM
-snip-
You're probably right I didn't even think about the and,or staments applying the want is being returned although I do wonder why this doesn't screw up a thousand times worse than just only sending one result

if rawequal(t,arg[1]) and x,y,z or x,y,z

I feel like this would either return
x,y,z,y,z
OR
y,z,x,y,z
Instead of just x


(Will test a fix when I get home and let you know if it works)


Also I'm not really sure why I chose to use a metatable actually…you have a really good point on just making it a function :P/>
Edited on 12 March 2015 - 07:02 PM
MKlegoman357 #6
Posted 12 March 2015 - 09:28 PM
Function returns don't translate like that, it's just how they work with operators: they give the operator only the first return value discarding the others. Oh, and that above would return: false/x,y,z/x,y,z - 5 variables, unless x,y,z would actually be a function call, then - always x.
Edited on 12 March 2015 - 08:28 PM
HDeffo #7
Posted 12 March 2015 - 11:29 PM
Function returns don't translate like that, it's just how they work with operators: they give the operator only the first return value discarding the others. Oh, and that above would return: false/x,y,z/x,y,z - 5 variables, unless x,y,z would actually be a function call, then - always x.

Thanks for the help this bug was really tormenting me! :P/>