1

Тема: Работа цикла for в Lua

В организации работы цикла for в разных языках программирования есть одно существенное различие: где-то граничные условия вычисляются 1 раз (например, Delphi), где-то условие перевычисляется каждый раз после каждой итерации (например, C). А как с этим обстоят дела в Lua?

Для проверки загрузил в QUIK следующий простенький скрипт:

function GetFirstVal ()
    message("GetFirstVal ()", 2)
    return 1
end

function GetLastVal()
    message("GetLastVal()", 2)
    return 3
end

message("---Start---", 1)

for i = GetFirstVal(), GetLastVal() do
    message(tostring(i), 1)
end

Запустив его, получаем следующую последовательность вывода сообщений:

---Start---
GetFirstVal()
GetLastVal()
1
2
3

Таким образом видно, что условие окончания цикла вычисляется только один раз, после чего цикл всегда отрабатывает это количество итераций (за исключением применения break, конечно).

2 (2013-08-04 19:28:35 отредактировано swerg)

Re: Работа цикла for в Lua

Посмотрим, какие практические выводы можно сделать на основании написанного в предыдущем посте.

Пусть есть задача написать программу, которая бы удаляла из таблицы элементы с определенным значением. Казалось бы, все просто:

t = {1,2,3,3,5}
DEL_ITEM = 3

s = ""
-- удалим элементы, равные DEL_ITEM
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 = ""
-- удалим элементы, равные DEL_ITEM
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 в варианте "со счетчиком" всегда необходимо подумать: не изменяет тело цикла границы (размер) обрабатываемой сущности. Если размер изменяется - необходимо продумать, как в этом случае поведет себя наш алгоритм и, возможно, реорганизовать его так, чтобы не возникало обращений к несуществующим элементам.

3

Re: Работа цикла for в Lua

Очень интересный пример! Спасибо!

4 (2015-02-10 11:07:56 отредактировано kalikazandr)

Re: Работа цикла for в Lua

swerg пишет:

Посмотрим, какие практические выводы можно сделать на основании написанного в предыдущем посте.

вот так проще:

t={1,2,3,3,5}
DEL_ITEM=3
tt=t
for i=1,#tt do
    if i == DEL_ITEM then
        table.remove(t,i)
    end
end
print(#t)-->4
s=""
for i=1,#t do
    s=s .. t[i] ..','
end
print(s)-->1,2,3,5,