Lua 中 pairs() 的 "type signature" 是什么?

从 "Programming in Lua" 的章节 7.1 – Iterators and Closures 中可以看出,for foo in bar 循环需要 bar 的类型 (使用 Java 的类型来表示) 为 Supplier<Tuple>,而 for-in 会不断调用 bar 直到它返回 nil

因此对于如下的代码:

for k,v in pairs( tables ) do
    print( 'key: '..k..', value: '..v )
end

可以得出 pairs 的类型为 Function<Table,Supplier<Tuple>>

我想要创建一个和 pairs 行为类似的函数,但是它会跳过第一个参数以 _ 开头的元组。

local function mypairs( list )
    local --[[ Supplier<Tuple> ]] pairIterator = pairs( list )
    return --[[ Supplier<Tuple> ]] function ()
        while true do
            local key, value = pairIterator()
            if key == nil then
                return nil
            elseif key:sub(1,1) ~= '_' then
                return key, value
            end
        end
    end
end

但是这段代码无效,因为

--[[应该是:Supplier<Table>]] pairIterator = pairs({ c=3; b=2; a=1 })

当我调用它时,

pairIterator()

会返回

stdin:1: bad argument #1 to 'pairIterator' (table expected, got no value)
stack traceback:
    [C]: in function 'pairIterator'
    stdin:1: in main chunk
    [C]: in ?

但是

pairIterator({ c=3; b=2; a=1 })

会返回

Lua>pairIterator({ c=3; b=2; a=1 })
c       3
点赞
用户734069
用户734069

您的基本问题是在 Lua 问题中使用 Java 逻辑。Java 和 Lua 是具有不同结构的不同语言,需要认识到这一点很重要。

pairs 没有返回值;它有 多个 返回值。Java 完全缺乏这个概念。元组是一个可以存储和操作多个值的单一值。Lua 函数可以返回多个值。这在语法和语义上与返回包含多个值的表格是不同的。

基于迭代器的 for 语句将多个值作为输入,而不是一个多个值的表格或容器。具体而言,它存储三个值:一个迭代器函数,一个状态值(您用它来保存调用之间的状态),以及一个初始值。

因此,如果您想模拟 pairs 的行为,您需要能够存储和操作它的多个返回值。

第一步是存储 pairs 实际返回的内容:

local f, s, var = pairs(list)

您正在创建一个新的迭代器函数。因此,您需要返回它,但您还需要返回 pairs 返回的 svar。您的返回语句应如下所示:

return function(s, var)
  --[[具体内容在下面讨论]]
end, s, var -- 返回 `pairs` 的返回值。

现在,在您的函数内部,您需要用 svar 来调用 f。这个函数将返回键值对。您需要正确处理它们:

return function(s, var)
  repeat
    local key, value = f(s, var)
    if(type(key) ~= "string") then
      -- 非字符串类型无法包含下划线。
      -- 而且不需要特殊处理 `nil`。
      return key, value
    elseif(key:sub(1, 1) ~= '_') then
      return key, value
    end
  until true
end, s, var -- 返回 `pairs` 的返回值。
2018-06-07 19:04:32
用户369792
用户369792

pairs() 返回三个独立的值:

  • 一个参数为 (table, key) 的函数,返回键和值
  • 传入的表格
  • 传递给函数的第一个“键”值(对于 pairs() 为 nil,ipairs() 为0)

因此像这样:

for k,v in pairs({a=1, b=13, c=169}) do print(k, v) end

可以这样完成:

local f,t,k = pairs({a=1, b=13, c=169})
local v
print('first k: '..tostring(k))
k,v = f(t, k)
while k ~= nil do
  print('k: '..tostring(k)..', v: '..tostring(v))
  k,v = f(t, k)
end

结果:

first k: nil
k: c, v: 169
k: b, v: 13
k: a, v: 1

你不必带上参数,这里有每个值的手动if语句:

function mypairs()
  -- the function returned should take the table and an index, and
  -- return the next value you expect AND the next index to pass to
  -- get the value after.  return nil and nil to end
  local myfunc = function(t, val)
    if val == 0 then return 1, 'first' end
    if val == 1 then return 2, 'second' end
    if val == 2 then return 3, 'third' end
    return nil, nil
  end

  -- returns a function, the table, and the first value to pass
  -- to the function
  return myfunc, nil, 0
end

for i,v in mypairs() do
  print('i: '..tostring(i)..', v: '..tostring(v))
end

-- output:
-- i: 1, v: first
-- i: 2, v: second
-- i: 3, v: third

对于你的 mypairs(list),只要键值有下划线,就可以继续调用从 pairs 返回的函数来获取下一个值:

local function mypairs( list )
  local f,t,k = pairs(list)
  return function(t,k)
    local a,b = f(t, k)
    while type(a) == 'string' and a:sub(1,1) == '_' do  a,b = f(t,a) end
    return a,b
  end, t, k
end

local list = {a=5, _b=11, c = 13, _d=69}
for k,v in mypairs(list) do print(k, v) end

-- output:
-- c    13
-- a    5

你所链接的文档的迭代器只返回一个值,而 pairs() 返回 2 个,但你可以返回更多的值。for ... in ... 结构只有在第一个值非空时执行函数体。这里有一个版本,它还返回被跳过的键,但如果没有实际的值,则不会执行函数体,因此你可能看不到所有的 _ 键:

local function mypairs( list )
  local f,t,k = pairs(list)
  return function(t,k)
    local skipped = {}
    local a,b = f(t, k)
    while type(a) == 'string' and a:sub(1,1) == '_' do
      table.insert(skipped, a)
      a,b = f(t,a)
    end
    return a,b,skipped
  end, t, k
end

local list = {a=5, _b=11, c = 13, _d=69}
for k,v,skipped in mypairs(list) do
  for i,s in ipairs(skipped) do
    print('Skipped: '..s)
  end
  print(k, v)
end
2018-06-07 22:38:00