#lua
#lua
Вопрос:
Я пытаюсь создать элегантную функцию транспонирования, используя функции mapn и zip в Lua.
Mapn и zip являются следующими (из книги lua):
function map(func, array)
local new_array = {}
for i,v in ipairs(array) do
new_array[i] = func(v)
end
return new_array
end
function mapn(func, ...)
local new_array = {}
local i=1
local arg_length = table.getn(arg)
while true do
local arg_list = map(function(arr) return arr[i] end, arg)
if table.getn(arg_list) < arg_length then return new_array end
new_array[i] = func(unpack(arg_list))
i = i 1
end
end
Они работают так, как ожидалось.
Затем я определяю zip и transpose как:
function zip(...)
return mapn(function(...) return {...} end,...)
end
function transpose(...)
return zip(unpack(...))
end
Теперь транспонируйте({{1,2},{3,4},{5,6}}) выдает {{1,3,5},{2,4,6}}, как и ожидалось.
Но транспонировать({{1,2},{3,4},{5}}) не выдает {{1,3,5},{2,4}}. Она выдает только одну строку.
Как я могу заставить ее выдать желаемый результат?
Я просто решил вместо этого написать «неэлегантную» функцию. Кажется, нет простого способа использовать mapn и друзей.
function transp(L)
local n=#L
local m,M=1e42,0
--Get the beginning and end of resultant transpose list.
for i=1,n do
for k,v in pairs(L[i]) do
if M<k then M=k end
if m>k then m=k end
end
end
local nt={}
for i=m,M do
local rt={}
for j=1,n do
rt[j]=L[j][i]
end
table.insert(nt,rt)
end
return nt
end
Пожалуйста, критикуйте и улучшайте это решение-кандидат.
Комментарии:
1. Ваш код содержит некоторые артефакты Lua 5.0 (а именно
arg
иtable.getn
). Если вы читаете «Программирование на Lua», убедитесь, что это второе издание.2. Это
unpack(...)
выглядит для меня крайне подозрительно. Убедитесь, что вы понимаете, что она делает.3. Вы уверены, что весь код в вашем сообщении правильный? Я протестировал
transpose({{1,2},{3,4},{5,6}})
иtranspose({{1,2},{3,4},{5}})
, и это вернулось{{1},{2}}
в обоих случаях. Кстати, я использую Lua 5.1.4. @kikito Я думаю, он имел в виду использовать
mapn
insidezip
вместоmap
.5. Проблема в
if table.getn(arg_list) < arg_length then return new_array end
. Во второй раз,table.getn({2,4}) < table.getn({{1,2},{3,4},{5}})
и поэтому она возвращается перед добавлениемarg_list
вnew_array
. Оставьте это, как в ответе понзао, и это должно сработать.
Ответ №1:
Я исправил несколько вещей в вашем коде, и я думаю, что теперь он работает так, как задумано, я добавил встроенные комментарии.
function map(func, array)
local new_array = {}
for i, v in ipairs(array) do
new_array[#new_array 1] = func(v)
end
return new_array
end
function mapn(func, ...)
-- Variadic arguments bound to an array.
local arrays = {...}
local new_array = {}
-- Simple for-loop.
local i = 1
while true do
local arg_list = map(function(arr) return arr[i] end, arrays)
if #arg_list == 0 then
break
end
new_array[i] = func(unpack(arg_list))
i = i 1
end
return new_array
end
-- Using 'mapn' instead of 'map' (probably how you intended).
function zip(...)
return mapn(function(...) return {...} end,...)
end
-- Same as before.
function transpose(...)
return zip(unpack(...))
end
Пример использования:
for _, row in pairs(transpose({{1,2},{3,4},{5}})) do
for _, col in pairs(row) do io.write(col .. ' ') end
io.write('n')
end
-- Output: 1 3 5
-- 2 4
Комментарии:
1. Ваш
map
и егоmap
делают то же самое, из-за того, какipairs
работает.2. @ponzao: вы выиграли, я допустил небольшую ошибку.
3. @Robin True, исправит это, я не знаю, о чем я думал (некоторое время не кодировал в Lua).
4. Предполагается, что ваш цикл mapn распространяется только на #arrays? Это выглядит неправильно в простом случае, когда в mapn передается только один список.
5. @JohnSmith Вы правы, вот почему всегда следует писать тесты. Я исправлю свой ответ.
Ответ №2:
{5}
В вашем примере игнорируется из-за этой строки:
if table.getn(arg_list) < arg_length then return new_array end
Вместо этого вы можете захотеть выйти из цикла только тогда, arg_list
когда он пуст.
Затем это даст желаемый результат при условии, что строки монотонно увеличиваются в длине.
В более общем случае, когда более поздние строки могут быть короче предыдущих
(например {{1,2},{3,4,5},{6}}
), вам нужно будет отслеживать длины строк, чтобы учесть отверстия. Это можно сделать, добавив необязательный аргумент (и дополнительное возвращаемое значение) в map
, чтобы указать максимальный индекс, i
для которого func(array[i])
был вычислен:
function map(func, array, len)
local new_array = {}
len = len or #array
for i=1,len do
new_array[i] = func(array[i])
end
return new_array, len
end
function mapn(func, ...)
local new_array = {}
local i=1
local arg_length = select('#', ...)
local args = {...}
while true do
local arg_list, num_results = map(function(arr) return arr[i] end, args, arg_length)
if not next(arg_list) then return new_array end
new_array[i] = func(unpack(arg_list, 1, num_results))
i = i 1
end
end
function zip(...)
return mapn(function(...) return {...} end,...)
end
function transpose(...)
return zip(unpack(...))
end