如何在Lua中动态生成函数?

假设我有一个表格 {field1=1, field2=0} (0: 升序,1: 降序)

我想要得到一个函数:

function(x, y)
  return x.field1 < y.field1 --这里0是'<',1是'>='
end

field1 和排序规则都可以注入进这个函数。

如何动态生成这个代码?

点赞
用户4567755
用户4567755

让我们不只讨论代码生成方法!就像问题所述,考虑一个函数的需求:

function (x, y)
   return x.field1 --[[ < or >= ]] y.field1
end

为了方便示例,假设我们有全局变量:

FIRST = {field1 = 3}
SECOND = {field1 = 5}

分支

我想到的最简单的解决方法(可能也是最典型的?)是冗长的 if

function foo (x, y, o)
   if o == nil or o == "asc" then
      return x.field1 < y.field1
   end
   if o == "dsc" then
      return x.field1 >= y.field1
   end
   error("Unknown option")
end

-- 例子:
foo(FIRST, SECOND, "asc")
foo(FIRST, SECOND, "dsc")

请注意,此情况下早期返回使 else 不必要,但不一定始终如此。

匿名函数

但是,嘿,那是什么?如果我们传递更奇怪的东西,而不是字符串选项,例如......可以调用的东西怎么办?这次我们要采用一个简单的函数:

local asc = function (a, b) return a < b end
local dsc = function (a, b) return a >= b end

function bar (x, y, compare)
   compare = compare or asc
   return compare(x.field1, y.field1)
end

-- 例子:
bar(FIRST, SECOND, asc)
bar(FIRST, SECOND, dsc)
bar(FIRST, SECOND, function (a, b) return a < b end)

高阶函数

先前的示例对于如此简单的操作看起来并不是很好,但我们将其作为基础,创建一个将返回所需函数的函数:

function make (compare)
   return function (x, y) return compare(x.field1, y.field1) end
end

-- 例子:
local asc = make(function (a, b) return a < b end)
local dsc = make(function (a, b) return a >= b end)
asc(FIRST, SECOND)
dsc(FIRST, SECOND)

实际生成

现在,让我们尝试更接近代码生成的内容。Lua 给了我们使用 load* 函数族随时加载代码块的能力。

请注意,在 5.1 中的 load 与 5.2 或 5.3 中的 load 不同。在 5.1 中,您需要使用 loadstring 而不是 load

function generate (op)
   return load(string.format("return function (x, y) return x.field1 %s y.field1 end", op))()
end

-- 例子:
local asc = generate("<")
local dsc = generate(">=")
asc(FIRST, SECOND)
dsc(FIRST, SECOND)
2020-01-03 12:12:11