“Transpose/Zip”函数未如预期工作。

我正在尝试使用 Lua 中的 mapn 和 zip 函数构建一个优雅的转置函数。

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

现在 transpose({{1,2},{3,4},{5,6}}) 可以像预期的那样生成 {{1,3,5},{2,4,6}}。

但是 transpose({{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

请批评和改进这个候选解决方案。

原文链接 https://stackoverflow.com/questions/8046009

点赞
stackoverflow用户206020
stackoverflow用户206020
我修改了你的代码,现在它应该按照你的意图工作了。我在代码中添加了一些注释。

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, ...) -- 使用可变长参数绑定到一个数组中。 local arrays = {...} local new_array = {} -- 简单的 for 循环。 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

-- 使用 'mapn' 替代 'map'(可能是你的本意)。 function zip(...) return mapn(function(...) return {...} end,...) end

-- 和之前一样的函数。 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 -- 输出: 1 3 5 -- 2 4

```

2011-11-08 15:15:34
stackoverflow用户12048
stackoverflow用户12048

你示例中的 {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
2011-11-08 18:46:45