【问题标题】:A better way to assign multiple return values to table keys in Lua在 Lua 中为表键分配多个返回值的更好方法
【发布时间】:2017-05-16 00:54:20
【问题描述】:

假设我有一个返回多个值的函数。我碰巧正在与 LÖVE 的(Image):getDimensions 合作。这将返回两个值,我知道它们是width,height。我想将它们作为数组分配给一个新表。我想要命名(字符串)键。因此,例如,我想将 getDimensions() 函数的返回值分配给一个新表,键分别为 widthheight

我知道以下作品...

image = {}
image.data = love.graphics.newImage('myimage.png')
image.size = {}
image.size.width, image.size.height = image.data:getDimensions()

我想知道是否有任何我可以使用的语法糖,或者任何标准库函数的使用将允许语法更符合...

image.size = { width, height = image.data:getDimensions() }

我知道上面的行不起作用,以及我尝试过的许多变体,包括使用unpack() 的各种尝试。我是 Lua 的新手(现在大约 2 天),所以也许还有另一个我不知道的标准函数或最佳实践会将一个键表关联到一个类似数组的表。谢谢!

【问题讨论】:

  • 你可以做image.size = table.pack(image.data:getDimensions()),但这会创建image.size[1]和image.size[2],即生成图像。用整数键调整表的大小。这可能不是你想要的。
  • @MarcBalmer 谢谢你,马克,但这是我试图避免的。我希望以后能够通过特定名称引用元素,例如widthheight。看来我什至不需要使用table.pack(),因为我可以将函数调用包装在大括号中(这可能与table.pack() 做同样的事情),就像image.size = { image.data:getDimensions() } 一样。我目前正在考虑我真正想要的将通过使用辅助函数的类类构造和带有__newindex 定义的__metatable 来完成。

标签: lua lua-table


【解决方案1】:

您可以编写自己的函数:

local function set_fields(tab, fields, ...)
   -- fields is an array of field names
   -- (use empty string to skip value at corresponging position)
   local values = {...}
   for k, field in ipairs(fields) do
      if field ~= "" then 
         tab[field] = values[k]
      end
   end
   return tab
end

local function get_fields(tab, fields)
   local values = {}
   for k, field in ipairs(fields) do
      values[k] = tab[field]
   end
   return (table.unpack or unpack)(values, 1, #fields)
end

用法示例#1:

image.size = set_fields({}, {"width", "height"}, image.data:getDimensions())

用法示例 #2:
即时交换值!

local function get_weekdays()
   return "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
end
-- we want to save returned values in different order
local weekdays = set_fields({}, {7,1,2,3,4,5,6}, get_weekdays())
-- now weekdays contains {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}

用法示例#3:

local function get_coords_x_y_z()
   return 111, 222, 333  -- x, y, z of the point
end
-- we want to get the projection of the point on the ground plane
local projection = {y = 0}
-- projection.y will be preserved, projection.x and projection.z will be modified
set_fields(projection, {"x", "", "z"}, get_coords_x_y_z())
-- now projection contains {x = 111, y = 0, z = 333}

用法示例#4:
如果require("some_module") 返回一个模块,里面有很多函数,但你只需要其中的几个:

local bnot, band, bor = get_fields(require("bit"), {"bnot", "band", "bor"})

【讨论】:

  • 谢谢叶戈尔。我一直在考虑这种方法,并且喜欢它的普遍适用性。
【解决方案2】:

使用类构造,我想出了以下...

Size = {}    
Size.mt = {}
Size.prototype = { width = 0, height = 0 }

function Size.new (dimensions)
  local size = setmetatable({}, Size.mt)

  if dimensions ~= nil and type(dimensions) == 'table' then
    for k,v in pairs(dimensions) do    
      size[k] = v
    end
  end

  return size
end

Size.mt.__index = function (t, k)
  if k == 'width' or k == 'height' then
    rawset(t, k, Size.prototype[k])

    return t[k]
  else
    return nil
  end
end

Size.mt.__newindex = function (t, k, v)
  if k == 1 or k == 'width' then
    rawset(t, 'width', v)
  elseif k == 2 or k == 'height' then
    rawset(t, 'height', v)
  end
end

然后我可以通过多种方式初始化一个 Size 对象

  • 使用多个返回值:
    • image.size = Size.new{image.data:getDimensions()}
    • image.size = Size.new(table.pack(image.data:getDimensions())
  • 使用默认值:
    • image.size = Size.new()
    • image.size = Size.new{}
    • image.size = Size.new({})
  • 使用混合数组和哈希表:
    • image.size = Size.new({height=20, width=30})
    • image.size = Size.new({height=20, 30})

这种方法与 Egor 的(实用函数)相比各有利弊,如果没有简单的语法技巧或我不知道的现有函数,我正在考虑这样做。

优点:

  • (个人)学习 Lua 中 OO 构造的经验
  • 我可以限制表上实际键的数量,同时允许通过扩展__index__newindexif/else 逻辑中的接受值来添加这些键的“同义词”
  • 表中字段的显式定义,无需担心将键表与值表同步(与通用实用程序函数一样)

缺点

  • 需要为我想要此行为的每个数据结构重复此模式
  • 成本高昂,开销很大,而对消费者的影响很小

我确信我可以及时使这种方法更加稳健,但如果有任何反馈,我将不胜感激。

【讨论】:

  • 这个可以:Size.new({height=20, 30}),但是相反的Size.new({width=30, 20})不行。
  • @EgorSkriptunoff 好点。我可以争辩说它是理想的行为,或者可以将一些类似于if rawget('width', 1) ~= nil 的附加逻辑添加到__newindex 中的if k == 1 ...。这将不得不扩展到检查rawget('height', 2)k == 2,然后继续丢弃任何额外的数组键。对于表中更多所需字段,此方法不能很好地扩展。虽然我认为这是对类的不好使用,但传递混合表也变得不那么用户友好,我越来越喜欢通用实用程序函数:)
猜你喜欢
  • 2012-03-28
  • 1970-01-01
  • 1970-01-01
  • 2019-01-19
  • 1970-01-01
  • 1970-01-01
  • 2022-07-07
  • 2012-09-30
  • 1970-01-01
相关资源
最近更新 更多