Посмотрим, какие практические выводы можно сделать на основании написанного в предыдущем посте.
Пусть есть задача написать программу, которая бы удаляла из таблицы элементы с определенным значением. Казалось бы, все просто:
t = {1,2,3,3,5}
DEL_ITEM = 3
s = ""
for i = 1,#t do
s = s .. tostring(t[i]) .. ","
if t[i] == DEL_ITEM then
table.remove(t, i)
end
end
message("process:" .. s, 1)
s = ""
for i = 1,#t do
s = s .. tostring(t[i]) .. ","
end
message("result:" .. s, 2)
Запускаем эту программу в QUIK и видим следующие результаты:
process:1,2,3,5,nil,
result:1,2,3,5,
Во-первых, как видно, после работы программы в таблице остался один элемент, равный 3, т.е. не все требуемые элементы удалились. А во-вторых, мы еще и обрабатывали элемент со значением nil! И все потому, что границы для for были вычислены один раз, после чего мы получили последовательную итерацию элементов от 1 до 5, не смотря на то, что после удаления элемента из таблицы количество их уменьшилось.
Наш простой пример пережил обработку nil-элемента, конечно, безболезненно, в реальной программе все может оказаться не так радужно.
В принципе, поправить программу для удаления элементов с определенным значением очень просто: достаточно лишь изменить обхода перебора элементов в цикле for, перебирая не от первого к последнему, а наоборот, от последнего к первому. Вот как это выглядит (по сравнению с предыдущим кодом изменена всего одна строчка с первым for):
t = {1,2,3,3,5}
DEL_ITEM = 3
s = ""
for i = #t,1,-1 do
s = s .. tostring(t[i]) .. ","
if t[i] == DEL_ITEM then
table.remove(t, i)
end
end
message("process:" .. s, 1)
s = ""
for i = 1,#t do
s = s .. tostring(t[i]) .. ","
end
message("result:" .. s, 2)
Запускаем эту программу в QUIK и видим следующие результаты:
process:5,3,3,2,1
result:1,2,5,
Видно, в этом случае мы не обрабатываем "левый" элемент (со значеним nil), кроме того работа алгоритма совершенно корректна: все элементы со значением 3 удалены.
Вывод: при использовании цикла for в варианте "со счетчиком" всегда необходимо подумать: не изменяет тело цикла границы (размер) обрабатываемой сущности. Если размер изменяется - необходимо продумать, как в этом случае поведет себя наш алгоритм и, возможно, реорганизовать его так, чтобы не возникало обращений к несуществующим элементам.