Выталкивание первого элемента из массива

У меня есть массив x в Lua. Я хотел бы установить head = x[1] и rest = остальную часть массива, чтобы rest[1] = x[2], rest[2] = x[3] и т. д.

Как я могу это сделать?

(примечание: меня не волнует, будет ли изменен исходный массив. В Javascript я бы сделал head = x.shift(), а x содержал бы оставшиеся элементы.)


person Jason S    schedule 05.02.2011    source источник


Ответы (3)


head = table.remove(x, 1)

«Pop» — это немного неправильное название, поскольку оно предполагает дешевую операцию, а удаление первого элемента таблицы требует перемещения остального содержимого — отсюда и название «shift» в JavaScript и некоторых других языках.

person Miles    schedule 06.02.2011
comment
Обратите внимание, что для массива любого разумного размера это ОЧЕНЬ медленная операция; попробуйте еще раз подумать, почему вы хотите это сделать... - person daurnimator; 07.02.2011
comment
@daurnimator вы имеете в виду массив любого неразумного размера B-) - person Nas Banov; 12.12.2015
comment
Вместо того, чтобы пересматривать мотивацию для этого, подумайте, возможно, об использовании структуры данных, отличной от массива. Связанный список (легко создать в Lua) делает эту операцию дешевой за счет большего объема памяти, необходимого для каждого узла. - person Phrogz; 27.08.2016

Вы хотите table.remove:

local t = {1,2,3,4}
local head = table.remove(t,1)
print( head )
--> 1
print( #t )
--> 3
print( t[1] )
--> 2

Как указывает @daurnimator, это требует больших усилий со стороны базовой реализации массивов в среде выполнения Lua, перемещая все элементы таблицы. Если вместо этого вы можете представить свои массивы в обратном порядке, вызвав последний элемент в массиве head, то вызов table.remove() будет дешевым поп:

local t = {4,3,2,1}
local head = table.remove(t)
print(head)
--> 1
print( #t )
--> 3
print( t[#t] )
--> 2

Кроме того, вы можете представить свою последовательность элементов в виде связанного списка. В этом случае выталкивание элемента из головы списка также является дешевой операцией (но перемещение элемента в конец — нет, если только вы не отслеживаете «хвост» в своем списке):

local setm,getm = setmetatable,getmetatable
local linkedlist=setm({__index={
  tail = function(l) while l.rest do l=l.rest end return l end, -- N.B. O(n)!
  push = function(l,v,t) t=l:tail() t.rest=setm({val=v},getm(l)) return t end,
  cram = function(l,v) return setm({val=v,rest=l},getm(l)) end,
  each = function(l,v)
    return function() if l then v,l=l.val,l.rest return v end end
  end
}},{ __call=function(lmeta,v,...)
  local head,tail=setm({val=v},lmeta) tail=head
  for i,v in ipairs{...} do tail=tail:push(v) end
  return head
end })

local numbers = linkedlist(1,2,3,4)
for n in numbers:each() do print(n) end
--> 1
--> 2
--> 3
--> 4

local head,rest = numbers.val, numbers.rest
print(head)
--> 1

for n in rest:each() do print(n) end
--> 2
--> 3
--> 4

local unrest = rest:cram('99')
for n in unrest:each() do print(n) end
--> 99
--> 2
--> 3
--> 4

Обратите внимание, в частности, что

local head,rest = numbers.val, numbers.rest

не изменяет никаких структур данных, а просто дает вам rest дескриптор определенного звена в цепочке.

person Phrogz    schedule 06.02.2011

Обычно в Lua действие по вставке элемента x в последовательность...

Eg: S={a,b,c,d,e,f} to S={a,b,c,x,d,e,f}

... очень много времени, потому что d нужно переместить в индекс 5, e в индекс 6 и т. д.

Существует ли другая последовательность вида S, где S[a]=b, S[b]=c, S[c]=d, S[d]=e и S[e]=f? Таким образом, все, что вам нужно сделать, это ввести:

S[c]=x S[x]=d

и бум, x стоит после c и перед d всего за две операции.

person Tim Crinion    schedule 01.12.2018
comment
Недавно узнал, что то, о чем я говорил, называется связанным списком. - person Tim Crinion; 17.11.2020