有效地从 lua 表中删除 nil 值

Posted

技术标签:

【中文标题】有效地从 lua 表中删除 nil 值【英文标题】:Effectively remove nil values from lua table 【发布时间】:2020-04-21 11:33:37 【问题描述】:

我是 lua 新手,对编程也很陌生。我正在努力实现以下目标。

我有一个数字表,我可以从中选择一个随机数。

myTable = 

for i = 1 to 100 do 
   table.insert(myTable, i)
end

local numberChosen = myTable[math.random(#myTable)]

到目前为止,一切都很好。下次选择一个数字时,我希望从表中删除该数字。我知道 lua 不会删除值,它们会保持为零。所以

table.remove(myTable, numberChosen)

当我尝试再次运行随机函数时不起作用,如果值为 nil,我得到 “'random' 的错误参数 #1(间隔为空)”

我尝试过创建这样的函数:

function cleanTable(t)
    local  cleanTable = 
    for k, v in ipairs(t)do
        if v ~= nil then
            table.insert(cleanTable, v)
        end
    end
    return cleanTable
end

myTable = cleanTable(myTable)

但这也不起作用,因为随机函数返回相同的错误。有人可以帮忙吗?

编辑 ---------------------------------- ------------------------------------

我的表包含如下键和值:

local columns = 1,2,3,4,5,6,7,8,9,10,11,12
local rows = 1,2,3,4,5,6,7,8,9,10,11,12
local coupledNumbers = 


for i = 1, #columns do
    for a = 1, #rows do
        local coupledNumber = i * a
        table.insert(coupledNumbers, i*a, coupledNumber)
    end
end

如何删除键和值?谢谢

【问题讨论】:

local indexChosen = math.random(#myTable); local numberChosen = myTable[indexChosen]; myTable[indexChosen] = myTable[#myTable]; myTable[#myTable] = nil; 感谢您的意见。这仍然给我同样的错误 【参考方案1】:

您对numberChosen 的定义不正确。当你定义

local numberChosen = myTable[math.random(#myTable)]

您错误地将 numberChosen 定义为表中某个任意索引处的值。

让我们逐步完成您的代码的几个循环

Loop: 1     numberChosen: 1     #myTable 100
Loop: 2     numberChosen: 57    #myTable 99
Loop: 3     numberChosen: 20    #myTable 98
Loop: 4     numberChosen: 82    #myTable 97
Loop: 5     numberChosen: 60    #myTable 96
Loop: 6     numberChosen: 48    #myTable 95
Loop: 7     numberChosen: 35    #myTable 94
Loop: 8     numberChosen: 91    #myTable 93
Loop: 9     numberChosen: 82    #myTable 92
Loop: 10    numberChosen: 74    #myTable 91
Loop: 11    numberChosen: 17    #myTable 90
Loop: 12    numberChosen: 86    #myTable 89
Loop: 13    numberChosen: 70    #myTable 88
Loop: 14    numberChosen: 49    #myTable 87
Loop: 15    numberChosen: 30    #myTable 86
Loop: 16    numberChosen: 3     #myTable 85
Loop: 17    numberChosen: 10    #myTable 84
Loop: 18    numberChosen: 38    #myTable 83
Loop: 19    numberChosen: 16    #myTable 82
Loop: 20    numberChosen: 17    #myTable 81
Loop: 21    numberChosen: 100   #myTable 80 

看看在循环 21 上发生了什么,我们有数字 100,一个不存在的索引。我们从列表中删除了 20 项,因此列表只有 80 项。

当我们调用math.random(#myTable) 时,我们将结果限制在表格的范围内,但是当我们调用myTable[math.random(#myTable)] 时,我们不再有这种信心。

当我们从表中删除值时,它会缩小,但表内的值不会改变,导致引用表边界之外的索引的值增加。

所以我们需要将numberChosen 定义为myTable[math.random(#myTable)] 而不是math.random(#myTable),同时为了确保我们有唯一的索引值,我已经调整了用于构建数组的for循环:

local columns = 1,2,3,4,5,6,7,8,9,10,11,12
local rows = 1,2,3,4,5,6,7,8,9,10,11,12
local coupledNumbers = 

print(coupledNumbers == )


for i = 1, #columns do
    for a = 1, #rows do
        local index = ((i - 1) * #rows) + a
        coupledNumbers[index] = index
    end
end

for i = 1, 144 do
  local numberChosen = math.random(#coupledNumbers)
  print(i, numberChosen, coupledNumbers[numberChosen])
  table.remove(coupledNumbers, numberChosen)
end

【讨论】:

好的,这行得通。我不得不承认,当我问这个问题时,我简化了一点,因为我认为这对答案没有任何影响。我的表实际上有点复杂。请参阅对原始问题的编辑 @Paul 您能详细说明一下,您要从哪个表中删除密钥?耦合数?以及耦合数的值如何指示所需的移除? 好的,这是一个网格类型的游戏。沉船类游戏。因此,当挑选数字时,它表示一行和列。我为此使用索引。我认为这对于解决问题并不重要。但是随机数是 CoupledNumbers 的索引。我想实现的是,随着挑选的随机数,数字将从数组中删除(Lua中的表)。我不想重复这些数字,所以我想消除它们,因为它们被称为 您的coupledNumbers 数组仍然是一个平面数组,因此提供的算法将从随机确定的索引中删除一个值,唯一改变的是第二个循环变为 100它会转到 144。这可能有助于根据答案准确解释什么不起作用,你还会收到错误吗? @Paul 我想我理解您遇到的问题,这与您创建coupledNumbers 数组的方式有关。创建它时它会产生重复的值。例如,当您运行第 1 行第 4 行时,值是 4,当您运行第 2 行第 2 行时也是如此。当使用插入时,这会将新值推入索引 4 并将另一个值移过来,因此数组将具有2个索引,其中值为4。【参考方案2】:

我希望解决这个问题的方法是生成一个棋盘(一个二维数组)并在每个元素被击中时将其设置为 true。然后,您只需在选择每个命中时检查棋盘,如果已经为真,则重新命中,如果为假,则为新命中。

这是最开始的数组:

这是 100 次随机点击的末尾:

没有 100 个真值,因为您可以从下面的控制台中看到很多是重击:

您可以在playGame 函数中从boardHeightboardWidth 更改电路板大小,如果您需要任何帮助集成此功能,请发表评论,我会帮助您。

local function getNewBoard(newHeight, newWidth)
    local newBoard = 
    for i = 1, newHeight do
        newBoard[i] = 
        for j = 1, newWidth do
            newBoard[i][j] = false
        end
    end
    return newBoard
end

local function playGame()
    local boardHeight = 10 --Change to your height
    local boardWidth = 10 --Change to your width
    local gameBoard = getNewBoard(boardHeight, boardWidth)

    for _ = 1, 100 do
        --Get a random hit, you can change this to player entered
        local hitHeight = math.random(1, boardHeight)
        local hitWidth = math.random(1, boardWidth)

        if gameBoard[hitHeight][hitWidth] == true then --Check if already hit
            print("[" .. hitHeight .. ":" .. hitWidth .. "] has already been hit")
        else
            gameBoard[hitHeight][hitWidth] = true --Mark as hit
            print("[" .. hitHeight .. ":" .. hitWidth .. "] has just been hit")
        end
    end
end

playGame()

【讨论】:

【参考方案3】:

您可以使用键值表代替数值表。代码有点丑。

local columns = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
local rows = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
local coupledNumbers = 

-- same like `i = 1, 12` - you don't even need the tables `columns` and `rows`, 'cause you aren't indexing
for i = 1, #columns do
  for j = 1, #rows do
    local idx = i * j
    if not coupledNumbers[idx] then
      coupledNumbers[idx] = true
    end
  end
end

local function getRand(n)
  local res
  if next(coupledNumbers) ~= nil then
    repeat
      local r = math.random(n)
      if coupledNumbers[r] then
        res, coupledNumbers[r] = r, nil
      end
    until res
  end

  return res
end

print(getRand(144))
print(getRand(144))
print(getRand(144))
print(getRand(144))
print('---')

for i in pairs(coupledNumbers) do
 print(i)
end

【讨论】:

if coupledNumbers ~= then 是错的,它永远不会是假的,你不能像这样检查一个表是否为空。当不存在元表时,表的相等性由表是完全相同的引用确定。在 if 子句中,您将 coupledNumbers 创建的表实例进行比较,这些引用永远不会相同,因此它们始终不相等。 我无法关注你,在这种情况下,如果我将所有值都设置为 nil,那么 CoupleNumbers 应该是,不是吗?此外,我认为,如果@Paul 可以写下他真正想要实现的目标,这不是理想的方法。它应该更容易解决。例如,在此示例中,您永远不会得到 13 作为随机值。 @Nifim 是对的。您可以通过 local a = ; print(a == ) 快速测试这一点。它总是会出现错误。检查键值表是否为空的可靠方法是使用next() 函数。 if next(coupledNumbers) ~= nil then 应该可以解决问题 @Kylaaa 谢谢,是的,我忘记了,我会解决的

以上是关于有效地从 lua 表中删除 nil 值的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Set 的情况下有效地从数组中删除重复项

递归地从链表中删除数据

有效地从 32 位数据类型中删除 2 位

lua基础简介

如何正确地从有效载荷中添加和删除元素? (或有条件更换)

如何有效地从 ArrayList 或字符串数​​组中删除所有空元素?