LinuxForum.Fans
Тема

Lua: ipairs vs for i = 1, #table

Автор: xxblx 3 сообщений 2380 просмотров
#363482

Всем доброго времени суток.
В Lua есть две удобные вещи: pairs и ipairs, предназначенные для использования с таблицами.
pairs для таблиц - ассоциативных массивов (ключ - значение). Точнее сказать, pairs обычно используется с ассоциативными массивами, но и с обычными таблицами-массивами тоже работает.
ipairs для таблиц - обычных массивов (упорядоченные элементы с последовательными индексами от 1 до <количество_элементов_таблицы>). По сути, разница (относительно pairs) в том, что ключ "целочисленный", начинается с единицы и по возрастающей с шагом 1.

text
for i, v in pairs(t) do
  print(i) -- выдаст ключ
  print(v) -- выдаст значение
end
text
> t = { ["A"] = "a", ["B"] = "b", ["C"] = "c"}
> for i, v in pairs(t) do
>> print(i)
>> print(v)
>> end
B
b
C
c
A
a
text
for i, v in ipairs(t) do
  print(i) -- выдаст индекс элемента
  print(v) -- выдаст сам элемент
end
text
> t = { "a", "b", "c", "d" }
> for i, v in ipairs(t) do
>> print(i)
>> print(v)
>> end
1
a
2
b
3
c
4
d

Все вроде бы понятно. Но, как обычно и бывает, способ написанный в документации - не единственный. И чтобы обойти таблицу-массив можно воспользоваться другим вариантом:

text
-- #t = длина таблицы t
for i = 1, #t do
   print(t[i]) -- выдаст элемент
end
text
> for i = 1, #t do
>> print(t[i])
>> end
a
b
c
d

В Lua 5.2 (в других не проверял) если провести сравнительный тест, то становится наглядно видно, чтобы цикла вида for i = 1, #t отрабатывает раза в 1.5-2 быстрее, чем for i, v in ipairs(t).
Откуда берется разница - вроде понятно, for i = 1, #t просто перебирает i с шагом 1, а for i, v in ipairs(t) именно получает значение и индекса, и элемента, по тому и отрабатывает медленней.

Вопрос в следующем, на какие подводные камни можно наткнутся, если использовать for i = 1, #t для обхода таблицы-массива вместо for i, v in ipairs(t). По тому что хоть for i, v in ipairs(t) вроде и правильней, жертвовать скоростью совсем не хочется.
Понятно, что если в цикле предстоит удалять элементы, то for i = 1, #t использовать нельзя

text
-- на момент начала цикла #t - какое-то конкретное значение типа number
for i = 1, #t do
   if (t[i] == "str") then
      -- как только удалим элемент, размер таблицы изменится, 
      -- а значит цикл выйдет за пределы таблицы и будет ошибка
      table.remove(t, i)
   end
end

А помимо удаления и подобных операций, когда таблица в ходе цикла будет заметно изменена, есть еще какие-то ситуации, когда использование for i = 1, #t do чревато проблемами?
И вообще, что посоветуете: не брезгать "быдлокодить" с for i = 1, #t do раз это дает профит по скорости работы, либо же строго придерживаться ipairs() по тому что "так положено" ?

#363520

Почитал архив почтовой рассылки Lua. Но до конца так и не понял, то ли ipairs() хотели убрать, но передумали, то ли уберут в следующих версиях. Непонятно.
По-видимому, это уже довод за for i = 1, #table. Код лишний раз переписывать не придется, если ipairs все-таки выпилят :)
Надеюсь, pairs убирать не будут. На ipairs наплевать, а вот без pairs будет неудобно, конечно.

#363521

в перле это хеши,
причем работают одинаково быстро для любого количества ключ => значение

так же существуют
массивы
массивы хешей
хеши массивов

Войдите или зарегистрируйтесь, чтобы ответить.