开发者

In lua, how to do the function of a-b while a & b are arrays or tables

i am a new programmer and started with lua . i want to do the function that array a -b , the following are my program, it didn't work well

function delTwo (a ,b)  
local i = 0 
   local lengthA = #a
   local lengthB = #b


  for i = 1 ,lengthA do
   for j =1 , lengthB do
   if a[i]==b[j] then
   a[i] = nil
   end
  end

  for i = 1 , lengthA do
   if a[i]~= nil then
   retrun a[i]
   end
  end
  end

  end

a = {10, 20, 30} 
b={11,20,122}
for element in delTwo (a ,b)  do 
  print(element) 
end

I have two questions, the first is input:16: '=' 开发者_开发知识库expected near 'a' retrun a[i] Y should i changed into retrun =a[i] and what is the differences between them

the second is input:3: attempt to get length of local 'a' (a nil value) what is wrong with this, even if i changed into local lengthA = table.getn(a) there will be input:3: bad argument #1 to 'getn' (table expected, got nil)


The first problem was already answered, but regarding the second it just means that a at some point of your program execution is nil (== null). I was not able to repeat this with your example though.

I am not completely certain what you are trying to achieve, but I recommend that you first create a function that creates a new table that stores the desired result and then iterate that using pairs (or normal looping). Something like the following:

function delTwo(a, b)
    local result = {}
    --# Logic here.
    --# Use result[#result + 1] = ... to insert values.
    return result
end

for k, v in pairs(delTwo(a,b)) do print(k, v) end


First of all, your indentation is disguising a problem with your balance between fors and ends.

What you have is:

function delTwo (a ,b)  
  local i = 0 
  local lengthA = #a
  local lengthB = #b

  for i = 1, lengthA do

    for j = 1, lengthB do
      if a[i] == b[j] then
        a[i] = nil
      end
    end

    for i = 1, lengthA do --// Iterating i while iterating i. Bad things happen!
      if a[i] ~= nil then
        return a[i]
      end
    end

  end

end

Also, because you are modifying a inside the loop, its length becomes smaller and you'll end up accessing it with an invalid index.


Then there's how you use delTwo's return value.

Here is an explanation on how iterators work in Lua: http://lua-users.org/wiki/IteratorsTutorial

When you write something like for i in <expr>, <expr> must return three values: an iterator function, a state object, and an initial value.

Every iteration, the iterator function will be called with the state object and the current value (starting with the initial value in <expr>). If it returns nil, the iteration stops, otherwise its return values are assigned to your loop variables, the body of the for loop is executed, and the iterator function will be called again with the same state object and the new current value, which is the first of your loop variables (iin this case).

A (relatively) simple example may help you understand:

local state = {}
state["toggle"] = true

function iterator_func(state, prev_i)
    --// Calculate current value based on previous value
    i = prev_i + 1

    --// Stop iteration if we've had enough
    if i > 10 then
        return nil
    end

    local msg
    if state["toggle"] then
        msg = "It's on!"
        state["toggle"] = false
    else
        msg = "It's off!"
        state["toggle"] = true
    end

    return i, i*2, i*3, msg
end

--// Notice the initial value is 0, the value *before* our first iteration
for  i, double, triple, msg  in  iterator_func, state, 0  do
    print(tostring(i)..", "
          ..tostring(double)..", "
          ..tostring(triple)..", "
          ..tostring(msg))
end

--// Prints:
--//   1, 2, 3, It's on!
--//   2, 4, 6, It's off!
--//   ...
--//   10, 20, 30, It's off!

Lua comes with two iterator generator functions: ipairs and pairs. They both take a table and return what is needed for a for loop to iterate over the values stored in that table.

ipairs expects a table with numeric keys from 1 to #table and generates an iterator which will iterate over those indices in order, returning every time the index and the value:

for i, v in ipairs( { 10, 20, 30 } ) do
    print("["..i.."] = " .. v)
end
--// Prints:
--//    [1] = 10
--//    [2] = 20
--//    [3] = 30

pairs takes any kind of table and generates an iterator which returns pairs of key and value, with pairs coming in any order. In this case keys can be anything except nil, even tables!

aKey = {}
t = { ["First"] = 10, [2.0] = 20, [aKey] = 30 }

for k, v in pairs(t) do
    print("["..tostring(k).."] = " .. tostring(v))
end
--// Prints something like:
--//    [table: 0x95860b0] = 30
--//    [First] = 10
--//    [2] = 20

So, you have two approaches here.

If you want delTwo to return a table, you must write your for loop like this:

for idx, element in ipairs(delTwo(a, b)) do
   print(element)
end    
--// delTwo *must* return a table with correct numeric indices

or like this:

for _, element in pairs(delTwo(a, b)) do
   print(element)
end
--// Conventionally, you use _ as a variable name if you plan to just ignore it.

Here's something for you to study. It's a big piece of code, but I hope you can understand it and learn something from it.

--//////////////////////////////////////////////////////////////////////////
--//
--// APPROACH #1
--//

--//
--// This function modifies table a in place,
--// removing elements that are also found in b
--//
local function delTwo_1(a, b)
    local lengthB = #b

    --// a's length may change if we remove an element from it,
    --// so iterate over b and recalculate a's length every iteration.
    for j = 1, lengthB do
        local lengthA = #a
        for i = 1, lengthA do       
            if a[i] == b[j] then
                table.remove(a, i)

                --// Don't use  " a[i] = nil ".
                --// This will just leave you with a nil element in the "middle"
                --// of the table, and as it happens ipairs() stops
                --// at the first nil index it finds.

                --// So:
                --//   a = { [1] = 10, [2] = 20, [3] = 30}
                --//   a[2] = nil
                --//   -- a is now { [1] = 10, [2] = nil, [3] = 30 }.
                --//
                --//   -- ipairs(a) will now return (1, 10) and then stop.
                --//
                --//   -- pairs(a) will return both (1, 10) and (3, 30)
            end
        end
    end

    --// Return table a if you want,but it's been modified "outside" as well
    return a
end

--//////////////////////////////////////////////////////////////////////////
--//
--// APPROACH #2
--//

--//
--// This function calculates the difference between two tables,
--// without modifying any of them.
--// It will be used in our iterator generator.
--//
local function tableDiff(a, b)
    local res = {}

    for i = 1, #a do
        local skip = false

        for j = 1, #b do
            if a[i] == b[j] then
                skip = true
                break
            end
        end

        if not skip then
            res[#res+1] = a[i]
        end
    end

    return res
end

--//
--// This function is an iterator generator.
--// It returns an iterator function, a state object and an initial value
--//
local function delTwo_2(a, b)   

    --// Some preliminary calculations...
    local res = tableDiff(a, b)

    --// We don't really need state in this case, because we could
    --// refer directly to our res variable inside our iterator function,
    --// but this is just for demonstration purposes.
    local state = {}
    state["result"] = res

    local function iterator(state, key)
        local result = state["result"]

        --// Our key is a numeric index, incremented every iteration
        --// before anything else (that's just how it works)
        key = key + 1

        if key > #result then
            --// If key is greater than our table length,
            --//    then we already iterated over all elements.
            --// Return nil to terminate.
            return nil
        end

        local element = result[key]

        --// Just because we can...
        local msg = "We're at element "..key

        return key, element, msg
    end


    local initialKey = 0 --// We start "before" index 1

    return iterator, state, initialKey
end




do
    --// TESTS

    do
        --// TESTING APPROACH #1

        a = {10, 20, 30} 
        b = {11, 20, 122}

        print "*******************  delTwo_1  *******************"
        print "Here's delTwo_1's result:"

        --// Table a is modified in place
        delTwo_1(a, b)
        for i, element in ipairs(a) do
          print("["..i.."] = "..tostring(element))
        end

        print()
        print "Here's a after delTwo_1:"
        for i, element in ipairs(a) do
          print("["..i.."] = "..tostring(element))
        end
    end

    print()
    print()

    do
        --// TESTING APPROACH #2
        a = {10, 20, 30} 
        b = {11, 20, 122}

        print "*******************  delTwo_2  *******************"
        print "Here's delTwo_2's result:"
        --// Notice how this compares to what
        --// is returned by our iterator function
        for idx, element, msg in delTwo_2(a, b) do
          print(tostring(element) .. "     (Msg: "..msg..")")
        end

        print()
        print "Here's a after delTwo_2:"
        for i, element in ipairs(a) do
          print("["..i.."] = "..tostring(element))
        end
    end
end

This post stands as a testament to how much free time I have in my hands :)


An alternate version which uses metatables

local mt = {    --// Just creates a metatable base
__sub = function (a, b) --// Function is the same as Zecc just formatted differently
    local lengthB = #b
    for j = 1, lengthB do
        local lengthA = #a
        for i = 1, lengthA do
            if a[i] == b[j] then table.remove(a, i) end
        end
    end
    return a
end
}

a = {10, 20, 30}    --// Same arrays
b = {11, 20, 122}

setmetatable(a, mt) -- //Use this to give the arrays the __sub function
setmetatable(b, mt)

c = a - b   --// Then you can use the maths operator on it

for k, v in ipairs(c) do --// printing them out gives the same as above
    print(k, v)
end

Then if you wanted to use different arrays in the same way, just use the setmetatable(x, mt) where x is the table you want to have the function and it should work.


The problem is like set difference, the following function prepare a dict of possible values to remove from the array b, and check if the array a contain it while traversing. The function has an optional inplace flag if change make in place of the array a or return a new one.

function arrayDiff(a,b,inplace)
  inplace = inplace~=false -- default inplace
  local ret = inplace and a or {}
  local toRemove = {} -- a dict for value to remove
  for i=1,#b do toRemove[b[i]]=true end
  local nxtInsert = 0
  local aLen = #a
  for i=1,aLen do
    local value = a[i]
    if not toRemove[value] then
      nxtInsert = nxtInsert + 1
      ret[nxtInsert] = value
    end    
  end  
  if inplace then
    for i=nxtInsert+1,aLen do ret[i]=nil end
  end  
  return ret
end


Below is my implementation of your function which does the following: 1. subtract from element x in A, element y from B ( If y exists )

function arrDel(a,b)
        local result = {}
        for i = 1, #a, 1 do
            result[i] = a[i] - ( b[i] or 0) -- 'or 0' exists to cope with if #b < #a
        end
        return result
 end

This code creates and populates a table called result which is the 'result' of subtracting b from a. In lua there exists two types of for loop: the numeric and the generic. The loop in the above function utilizes the numeric type of loop. This loop should be used either when the the block of code it contains needs to be executed a known number of times OR indexing to find the value which the loop uses is trivial.

The sytanx is as follows:

for counter = initial, final, increment do
  body
end

The increment is optional, by default it is 1.


To demonstrate the other type of loop this small function prints out a very simple table:

function tprint(t)
    local res = {}
    for _,v in ipairs(t) do
       res[#res+1] = v
    end
    print("{"..table.concat(res,",").."}")
end

Here the generic for is used ( the 'in' keyword is used ). The generic for allows easy traversal of complex objects. For instance, say an object contained records of users, however the records were stored in sections i.e VIP, Administrator, Guest, etc... And you wanted to go over ALL the users. You can write a function which you then use in the generic for to do this easily with an expression similar to this:

for user in object.traverseUsers() do
  body
end

The implementation of such iterators is beyond the scope of an answer to this question, but a great explanation can be found here Generic For (PIL)


  1. You spelled "return" as "retrun".
  2. If there is that possibility that something other than a table would be given but you still want to keep the code running, use this

    local lengthA = a and #a or 0
    local lengthB = b and #b or 0
    
  3. Not really necessary, but just a little info; No need to say

       if a[i] ~= nil then
    

You can just do this instead:

    if a then
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜