Ошибка распаковки Lua?

Я наткнулся на странное поведение функции распаковки Lua

table1 = {true, nil, true, false, nil, true, nil}
table2 = {true, false, nil, false, nil, true, nil}

a1,b1,c1,d1,e1,f1,g1 = unpack( table1 )
print ("table1:",a1,b1,c1,d1,e1,f1,g1)

a2,b2,c2,d2,e2,f2,g2 = unpack( table2 )
print ("table2:",a2,b2,c2,d2,e2,f2,g2)

Выход:

table1: true    nil true    false   nil nil nil
table2: true    false   nil nil nil nil nil

Вторая распаковка доставляет параметры до первого нулевого значения. Я мог бы жить с этим. Первая таблица дает 4? параметры с одним нулем в середине. У него есть 4 параметра, которые не равны нулю, но они не те, которые показаны.

Может ли кто-нибудь объяснить это? Это было сделано с помощью codepad.org и lua 5.1.


person Geggamojja    schedule 04.11.2009    source источник


Ответы (2)


Проблему можно решить, просто указав начальный и конечный индексы для unpack() и используя table.maxn() в качестве конечного индекса:

table1 = {true, nil, true, false, nil, true, nil}

a1,b1,c1,d1,e1,f1,g1 = unpack( table1, 1, table.maxn(table1) )
print ("table1:",a1,b1,c1,d1,e1,f1,g1)
-->table1: true    nil     true    false   nil     true    nil

Истинная причина расхождения в том, как обрабатываются две таблицы, заключается в логике определения длины части таблицы, являющейся массивом.

Функция luaB_unpack() использует luaL_getn(), который определяется в терминах lua_objlen(), который вызывает luaH_getn() для таблиц. luaH_getn() смотрит на последнюю позицию массива, и, если она nil, выполняет двоичный поиск границы в таблице («так, что t [i] не равно нулю, а t [i + 1] равно нулю»). Бинарный поиск конца массива является причиной того, что table1 обрабатывается иначе, чем table2.

Это должно быть проблемой только в том случае, если последняя запись в массиве - nil.

Из Программирование на Lua (стр. 16) (Вы должны купить эту книгу .): Когда в массиве есть дыры - нулевые элементы внутри - оператор длины может принять любой из этих нулевых элементов в качестве маркера конца. Поэтому вам следует избегать использования оператора длины для массивов, которые могут содержать дыры.

unpack() использует оператор длины lua_objlen(), который «может принимать любой из [] nil элементов в качестве конца» массива.

person gwell    schedule 04.11.2009
comment
Спасибо, ты спас мне день. Означает ли это, что table.maxn () проходит весь выделенный размер для таблицы? - person Geggamojja; 05.11.2009
comment
table.maxn () Возвращает наибольший положительный числовой индекс данной таблицы см. lua.org/manual/5.1/manual.html#pdf-table.maxn - person gwell; 05.11.2009
comment
действительно ли table.maxn гарантированно вернет здесь правильное значение (= количество объектов в литерале таблицы)? - person u0b34a0f6ae; 29.04.2010
comment
@ kazer.se table.maxn вернет 6, который является наибольшим индексом для таблицы table1 с записью, отличной от нуля. nil в 7-й позиции не будет засчитан table.maxn; однако в этом случае он работает отлично. unpack вернет 6 элементов, а 7-я присвоенная переменная получит nil, поскольку список будет расширен таким количеством нулей, которое необходимо для присвоения. - person gwell; 04.05.2010

2.2 - Значения и типы

[...] Тип таблица реализует ассоциативные массивы, то есть массивы, которые можно индексировать не только числами, но и любым значением (кроме nil). Таблицы могут быть неоднородными; то есть они могут содержать значения всех типов (кроме nil). [...]

Если указать nil для записи, перечисление таблицы будет прервано, и ваши переменные не будут правильно инициализированы.

Вот простой пример, демонстрирующий проблемное поведение:

table1 = {true, false, nil, false, nil, true, nil}
for k,v in ipairs(table1) do
  print(k, v)
end

выход:

1   true
2   false
>Exit code: 0
person Nick Dandoulakis    schedule 04.11.2009
comment
В частности, я подозреваю, что он игнорирует указанные им nil. - person RCIX; 04.11.2009
comment
Нет, это не RCIX, в этом случае таблица 2 также вернула бы 4 значения, я думаю. - person Geggamojja; 04.11.2009
comment
Единственная причина остановки цикла for заключается в том, что итератор вернул nil в позиции 3. Таблица все еще была правильно инициализирована, потому что assert(table1[1]==true and table1[2]==false and table1[3]==nil and table1[4]==false and table1[5]==nil and table1[6]==true and table1[7]==nil) не утверждает. - person gwell; 05.11.2009
comment
@gwell, я сказал, что nil может нарушить перечисление. Я не говорил, что стихии перестанут существовать. - person Nick Dandoulakis; 05.11.2009