Я люблю Lua. I love Lua.

Удаление данных из таблицы внутри цикла

Posted in Uncategorized by ilovelua on Октябрь 25, 2011

Есть массив:

local t = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Из него нужно удалить все элементы большие 2 и меньшие 9.

Неправильное решение:

-- не работает
for i, v in ipairs(t) do
  if v > 2 and v < 9 then
    table.remove(t, i)
  end
end

for i, v in ipairs(t) do
  print(i, v)
end

Вывод на консоль:

1 1
2 2
3 4
4 6
5 8
6 9
7 10

Видно, что несколько элементов пропущены.
Правильное решение — удалять данные из массива в обратном порядке:

local counter = #t

for i = #t, 1, -1 do
  local v = t[i]

  if v > 2 and v < 9 then
    table.remove(t, i)
  end
end

Либо воспользоваться функцией next()(история нескольких неправильных реализаций в комментариях):

-- работает
local i, v = next(t)

while i do
  if v > 2 and v < 9 then
    table.remove(t, i)
    i = i - 1

    if i == 0 then
      i = nil
    end
  end

  i, v = next(t, i)
end

Вывод на консоль:

1 1
2 2
3 9
4 10

Для словарей все проще:

-- работает
for i, v in pairs(t) do
  if v > 2 and v < 9 then
    t[i] = nil
  end
end

for i, v in pairs(t) do
  print(i, v)
end

Вывод на консоль:

1 1
2 2
9 9
10 10

Advertisements
Tagged with: , , , , ,

комментариев 5

Subscribe to comments with RSS.

  1. Kan said, on Январь 10, 2012 at 7:51 дп

    Плохой вариант:

    while i do
      if v > 2 and v < 9 then
        table.remove(t, i)
        i = i - 1
      end
    
      i, v = next(t, i)
    end
    

    Если первый же элемент будет больше двух и меньше девяти, то i, v = next(t, i) — где i == уже равно 0, не позволит скомпилировать скрипт. Вылечить разве что условием, мол когда i == 0, то вызывать next(t) без индекса.

  2. ilovelua said, on Январь 10, 2012 at 9:27 дп

    Да, Вы правы. Нужна дополнительная проверка для первого элемента. Исправленный вариант:

    -- работает
    local i, v = next(t)
    
    while i do
      if v > 2 and v < 9 then
        table.remove(t, i)
        
        if i == 1 then
          i = nil
        end
      end
    
      i, v = next(t, i)
    end
    

    Спасибо!

  3. Kan said, on Январь 13, 2012 at 1:10 пп

    сорри за настойчивость, но то тож не катит ^_^ актуальная просто тема для меня)
    если второй и третий соответствуют условию удаления, то третий останется в таблице.

    ...
    while i do
      if v == 1 then
        table.remove(t, i)
        i = i - 1
        if i == 0 then
          i = nil
        end
      end
      i, v = next(t, i)
    end
    ...
    
    • ilovelua said, on Январь 16, 2012 at 5:09 дп

      И снова Вы правы! В общем, если использовать для удаления из массива конструкцию с next(), то код обрастает кучей лишних проверок. Гораздо проще удалять элементы из массива в обратном порядке, например так:

      local t = {1, 3, 4, 9}
      local counter = #t
      
      for i = #t, 1, -1 do
        local value = t[i]
        if value > 2 and value < 9 then
          table.remove(t, i)
        end
      end
      
  4. 16Tomatons said, on Ноябрь 15, 2016 at 10:13 пп

    Функция next в нехешированных массивах работает исключительно медленно.
    Есть способ это исправить, добавив какой-либо символьный ключ, но это не нужно, ибо обход в обратном порядке эффективнее.
    Сам когда-то давно с таким морочился, доходил самостоятельно.
    Очень удивила лаконичность решения.
    P.S. Локальную переменную value (которая равна t[i]) желательно объявлять вне тела цикла, обновляя в цикле, иначе плодится слишком много локальных переменных, которые тут же выбрасываются.

    local t = {1, 3, 4, 9}
    do
      local value
      for i = #t, 1, -1 do
      value = t[i]
        if value > 2 and value < 9 then
          table.remove(t, i)
        end
      end
    end
    

    Или вообще итератор:

    function ripairs(t)
      local i = #t + 1
      return function()
        i = i - 1
        if t[i] then return i, t[i] end
      end
    end
    
    local t = {1, 2, 3, 4, 5}
    for i, v in ripairs(t) do
       if v%2 == 0 then table.remove(t, i) end
    end
    

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s

%d такие блоггеры, как: