5

I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.

The tables are being populated with the following code:

str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end

It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:

tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end

Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:

function comparetables(t1, t2)
matchct = 0
 for i=1,#t1 do
    if t1[i] == t2[i] then
    matchct = matchct + 1
    end
if matchct == #t1 then
return true
end
end

I tried doing

print(comparetables(tableone,tabletwo))

to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!

Additional information:

The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.

  1. When comparing the tables, if the numbers match, Ccount increases by 1.
  2. When comparing tables, if the value exists in a different index position, increment Pcount by 1

For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.

hjpotter92
  • 78,589
  • 36
  • 144
  • 183
Josh
  • 3,225
  • 7
  • 30
  • 44

2 Answers2

5

A slight variant on your code that should work is:

function comparetables(t1, t2)
  if #t1 ~= #t2 then return false end
  for i=1,#t1 do
    if t1[i] ~= t2[i] then return false end
  end
  return true
end

However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.

-- This is not clever enough to find matching table keys
-- i.e. this will return false
--   recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on 
-- an evil table like this:
--     t = {}
--     t[1] = t

function recursive_compare(t1,t2)
  -- Use usual comparison first.
  if t1==t2 then return true end
  -- We only support non-default behavior for tables
  if (type(t1)~="table") then return false end
  -- They better have the same metatables
  local mt1 = getmetatable(t1)
  local mt2 = getmetatable(t2)
  if( not recursive_compare(mt1,mt2) ) then return false end

  -- Check each key-value pair
  -- We have to do this both ways in case we miss some.
  -- TODO: Could probably be smarter and not check those we've 
  -- already checked though!
  for k1,v1 in pairs(t1) do
    local v2 = t2[k1]
    if( not recursive_compare(v1,v2) ) then return false end
  end
  for k2,v2 in pairs(t2) do
    local v1 = t1[k2]
    if( not recursive_compare(v1,v2) ) then return false end
  end

  return true  
end

Here's an example of it in use:

print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • I'm assuming there is an errant "for" in the first code. I probably should have mentioned the reason I'm comparing tables. I'm creating a mastermind-type game, so it needs to check the values to a.) see if they all match, b.) see if any match, and if they do, keep track of how many, and c.) see if the numbers exist in the table at all and keep track of that count. Will update question with the information. – Josh Jan 04 '12 at 06:32
  • @Josh sounds like you're comparing states rather than generic tables. In that case you probably want to use some OO lua magic to do it. I'll outline that in a second answer. – Michael Anderson Jan 04 '12 at 06:35
  • Tried both the solutions above, and both returned false though the tables were the same. – Josh Jan 04 '12 at 06:55
  • @Josh what were the tables you compared with it? I've added an example above that prints the right values for me. – Michael Anderson Jan 04 '12 at 07:00
  • I created a puzzle with for i=1,4 do table.insert(puzzle,math.random(4)) and using the first code above, I generated the guess table using the information in the puzzle table. Both had the same digits at the correct index, but it returned false. – Josh Jan 04 '12 at 07:13
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/6372/discussion-between-michael-anderson-and-josh) – Michael Anderson Jan 04 '12 at 09:08
3

If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.

Something like this should do the trick:

GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index =  GameState.mt.fns

function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
  local retval = {}
  retval[1] = a
  retval[2] = b
  retval[3] = c
  retval[4] = d
  setmetatable(retval, GameState.mt)
  return retval
end

function GameState.mt.fns.print( self )
  print(" GameState: ", self[1], self[2], self[3], self[4] )
end

function GameState.mt.__tostring( self )
  return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end

function GameState.mt.__eq(self, other)
  -- Check it's actually a GameState, and all its bits match
  return getmetatable(other)==GameState.mt and
    (self[1] == other[1]) and 
    (self[2] == other[2]) and 
    (self[3] == other[3]) and 
    (self[4] == other[4])
end

Then you'd use it like this:

state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)

print("State 1 is:")
state1:print()

print("State 2 is:")
print(state2)

print( "state1 == state2 : ", state1 == state2 )

print( "Changing state 2") 
state2[1]=2

print( "state1 == state2 : ", state1 == state2 )
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • Where does the 'self' come from? Just tried it and it returned an error saying it was attempting to index local self, a nil value. Edit: Oh, wait, I see it in the first block. Odd that it returns an error, though. – Josh Jan 04 '12 at 06:58
  • @Josh where are you seeing an error for this? (I've tested this one on my copy of lua and it runs without error). – Michael Anderson Jan 04 '12 at 07:02
  • (\n is just indicating the new line since comments don't allow for multilines) Using wxLuaeditor and both sections of code above, it returns this: State 1 is: \n Error: Lua: Error while running chunk \n [string "*untitled.lua"]:18: attempt to index local 'self' (a nil value) \n stack traceback: \n [string "*untitled.lua"]:18: in function 'print' \n [string "*untitled.lua"]:37: in main chunk – Josh Jan 04 '12 at 07:09
  • Changing the state1.print() to print(state1) seems to have fixed it. Now I can work with it and see how to work it in the program! Thanks! – Josh Jan 04 '12 at 07:50
  • @Josh .. sorry copy paste error... was trying to show that both `state1:print()` which uses the `print` function added to the metatable and `print(state1)` which uses the `__tostring` in the metatable work. (Note the `:` rather than `.`) I've edited the source to fix that now. – Michael Anderson Jan 04 '12 at 09:03