Lua 中的并行迭代

我想在 Lua 中并行地循环遍历多个表。我可以这样做:

for i in range(#table1)
  pprint(table1[i])
  pprint(table2[i])
end

但我更愿意像 Python 的 zip 一样:

for elem1, elem2 in zip(table1, table2):
  pprint(elem1)
  pprint(elem2)
end

标准 Lua 中是否有这样的东西(或者至少在 torch 中打包的东西中)?

点赞
用户3677376
用户3677376

如果您想在 Lua 中使用类似于 Python 函数的功能,您应该首先查看 Penlight。对于这个特定的情况,存在一个 seq.zip 函数。根据 Penlight 的描述,它似乎是与 Torch 一起安装的,但您也可以通过 LuaRocks 获取它(至少一个 Torch 实现捆绑了 LuaRocks)。

无论如何,Penlight 中的 seq.zip 函数只支持将两个序列压缩在一起。这里有一种更像 Python 的 zip 函数的版本,它允许压缩超过两个序列:

local zip
do
  local unpack = table.unpack or unpack
  local function zip_select( i, var1, ... )
    if var1 then
      return var1, select( i, var1, ... )
    end
  end

  function zip( ... )
    local iterators = { n=select( '#', ... ), ... }
    for i = 1, iterators.n do
      assert( type( iterators[i] ) == "table",
              "you have to wrap the iterators in a table" )
      if type( iterators[i][1] ) ~= "number" then
        table.insert( iterators[i], 1, -1 )
      end
    end
    return function()
      local results = {}
      for i = 1, iterators.n do
        local it = iterators[i]
        it[4], results[i] = zip_select( it[1], it[2]( it[3], it[4] ) )
        if it[4] == nil then return nil end
      end
      return unpack( results, 1, iterators.n )
    end
  end
end

-- 示例代码(假设该文件名为 "zip.lua"):
local t1 = { 2, 4, 6, 8, 10, 12, 14 }
local t2 = { "a", "b", "c", "d", "e", "f" }
for a, b, c in zip( {ipairs( t1 )}, {ipairs( t2 )}, {io.lines"zip.lua"} ) do
  print( a, b, c )
end
2016-03-17 17:50:27
用户3735873
用户3735873
--------------------------------------------------------------------------------
-- Python-like zip() iterator
--------------------------------------------------------------------------------

-- 类似于 Python 的 zip() 迭代器
function zip(...)
  local arrays, ans = {...}, {} -- 多个数组作为参数并存储在数组中
  local index = 0 -- 初始索引为 0
  return -- 返回一个函数
    function()
      index = index + 1 -- 索引自增
      for i,t in ipairs(arrays) do -- 遍历数组
        if type(t) == 'function' then ans[i] = t() else ans[i] = t[index] end -- 若数组元素是函数则执行该函数,否则取出该数组对应索引的元素
        if ans[i] == nil then return end -- 如果该元素为 nil 则结束循环
      end
      return unpack(ans) -- 返回多个值
    end
end

--------------------------------------------------------------------------------
-- Example use:
--------------------------------------------------------------------------------

a = {'a','b','c','d'} -- 定义数组 a
b = {3,2,1} -- 定义数组 b
c = {7,8,9,10,11} -- 定义数组 c

for a,b,c,line in zip(a,b,c,io.lines(arg[0])) do -- 用 zip() 迭代器迭代多个数组
  print(a,b,c,line) -- 打印出每个数组对应索引的值和文件中的一行
end

print '\n--- Done! ---' -- 输出结束标志
2016-03-19 00:18:11
用户13525363
用户13525363

我基于其他回答做了一版可以包含不同数量列表的版本并且添加了协程,虽然不是最快的,但我认为非常易于阅读。我会使用它,但也许其他人也会觉得有用。

另外,这是为使用neovim的LuaJit而制作的。

local zipgen = function(args)
    -- 查找最小迭代器的长度
    local min = #args[1]
    for i=2, #args, 1 do
        min = #args[i] < min and #args[i] or min
    end

    -- 创建列表,其中包含所有迭代器的“i”元素
    for i=1, min do
        local ans = {}
        for j=1, #args do
            -- 获取“j”迭代器的“i”元素
            ans[j] = args[j][i]
        end

        -- 返回“i”元素的列表
        coroutine.yield(unpack(ans))
    end
end

-- 类似于Python的zip迭代器
zip = function(...)
    local args = {...}
    -- 返回一个函数,当被调用时恢复协程
    return coroutine.wrap(function() zipgen(args) end)
end
2022-10-26 22:19:46