Не могу отсортировать таблицу с ассоциативными индексами - PullRequest
1 голос
/ 20 января 2010

Почему я не могу использовать table.sort для сортировки таблиц с ассоциативными индексами?

Ответы [ 3 ]

6 голосов
/ 20 января 2010

Как правило, таблицы Lua являются чистыми ассоциативными массивами.Не существует «естественного» порядка, кроме как побочный эффект конкретной реализации хеш-таблицы, используемой в ядре Lua.Это имеет смысл, потому что значения любого типа данных Lua (кроме nil) могут использоваться как ключи и значения;но только строки и числа имеют какой-либо разумный порядок, и только между значениями одинакового типа.

Например, порядок сортировки этой таблицы должен быть следующим:

unsortable = {
    answer=42,
    true="Beauty",
    [function() return 17 end] = function() return 42 end,
    [math.pi] = "pi",
    [ {} ] = {},
    12, 11, 10, 9, 8
}

. Он имеет одну строковую клавишу, одну логическую клавишу, одну функциональную клавишу, одну нецелую клавишу, одну таблицуключ и пять целочисленных ключей.Должна ли функция сортироваться впереди строки?Как вы сравниваете строку с числом?Где сортировать таблицу?А как насчет значений userdata и thread, которые не встречаются в этой таблице?

По соглашению, значения, индексируемые последовательными целыми числами, начинающимися с 1, обычно используются в качестве списков.Несколько функций и общих идиом следуют этому соглашению, и table.sort является одним примером.Функции, которые работают со списками, обычно игнорируют любые значения, хранящиеся в ключах, которые не являются частью списка.Опять же, table.sort является примером: он сортирует только те элементы, которые хранятся в ключах, которые являются частью списка.

Другим примером является оператор #.Для приведенной выше таблицы #unsortable равно 5, потому что unsortable[5] ~= nil и unsortable[6] == nil.Обратите внимание, что значение, хранящееся в числовом индексе math.pi, не учитывается, даже если число от 3 до 4, потому что оно не является целым числом.Кроме того, ни один из других нецелых ключей также не учитывается.Это означает, что простой цикл for может выполнять итерацию по всему списку:

for i in 1,#unsortable do
    print(i,unsortable[i])
end

Хотя это часто записывается как

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

Короче говоря, таблицы Lua представляют собой неупорядоченные наборы значений, каждыйиндексируется ключом;но существует специальное соглашение для последовательных целочисленных ключей, начинающихся с 1.

Редактировать: Для специального случая нецелых ключей с подходящим частичным упорядочением существует обходное решение, включающееотдельная таблица индексов.Описанное содержимое таблиц, снабженных строковыми значениями, является подходящим примером для этого трюка.

Сначала соберите ключи в новой таблице в виде списка.То есть создайте таблицу, индексированную последовательными целыми числами, начинающимися с 1, с ключами в качестве значений и отсортируйте их.Затем используйте этот индекс для перебора исходной таблицы в нужном порядке.

Например, здесь foreachinorder(), который использует эту технику для перебора всех значений таблицы, вызывая функцию для каждой пары ключ / значение, в порядке, определяемом функцией сравнения.

function foreachinorder(t, f, cmp)
    -- first extract a list of the keys from t
    local keys = {}
    for k,_ in pairs(t) do
        keys[#keys+1] = k
    end
    -- sort the keys according to the function cmp. If cmp
    -- is omitted, table.sort() defaults to the < operator
    table.sort(keys,cmp)
    -- finally, loop over the keys in sorted order, and operate
    -- on elements of t
    for _,k in ipairs(keys) do
        f(k,t[k])
    end
end

Создает индекс, сортирует его по table.sort(), затем перебирает каждый элемент в отсортированном индексе и вызывает функцию f для каждого.В функцию f передаются ключ и значение.Порядок сортировки определяется необязательной функцией сравнения, которая передается в table.sort.Он вызывается для сравнения с двумя элементами (в данном случае это ключи к таблице t) и должен возвращать true, если первый меньше второго.Если опущено, table.sort использует встроенный оператор <.

Например, с учетом следующей таблицы:

t1 = {
    a = 1,
    b = 2,
    c = 3,
}

затем foreachinorder(t1,print) печатает:

a    1
b    2
c    3

и foreachinorder(t1,print,function(a,b) return a>b end) отпечатки:

c    3
b    2
a    1
3 голосов
/ 20 января 2010

Вы можете сортировать только таблицы с последовательными целочисленными ключами, начинающимися с 1, то есть списками.Если у вас есть другая таблица пар ключ-значение, вы можете составить список пар и отсортировать их так:

function sortpairs(t, lt)
  local u = { }
  for k, v in pairs(t) do table.insert(u, { key = k, value = v }) end
  table.sort(u, lt)
  return u
end

Конечно, это полезно только в том случае, если вы предоставляете пользовательский порядок (lt), который ожидаетв качестве аргументов пары ключ / значение.

Этот вопрос более подробно обсуждается в связанном вопросе о сортировке таблиц Lua .

2 голосов
/ 20 января 2010

Потому что у них нет никакого заказа в первую очередь. Это все равно что пытаться сортировать мусорный мешок, полный бананов.

...