有效地从 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
函数中从boardHeight
和boardWidth
更改电路板大小,如果您需要任何帮助集成此功能,请发表评论,我会帮助您。
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 值的主要内容,如果未能解决你的问题,请参考以下文章