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

huangapple go评论73阅读模式
英文:

How to dynamically generate functions in Lua?

问题

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

我想要生成一个函数:

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

表格中的field1和排序规则可以动态注入到函数中。

如何动态生成这段代码?

英文:

If I have a table {field1=1,field2=0} (0: ascending order, 1: descending order)

I want to get a function:

function(x,y)
  return x.field1 &lt; y.field1 --there 0:&#39;&lt;&#39;,1:&#39;&gt;=&#39; 
end

field1 in the table and the sorting rule can be injected in the function.

How to generate this code dynamically?

答案1

得分: 4

让我们谈谈不仅仅是代码生成方法!如问题中所述,考虑函数的需求:

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)
英文:

Let's talk about more than just the code generation approach! As in the question, consider the need of a function:

function (x, y)
   return x.field1 --[[ &lt; or &gt;= ]] y.field1
end

For the sake of the examples assume that we have global variables:

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

Branching

The simplest solution (perhaps also the most typical?) that comes to my mind is a verbose if:

function foo (x, y, o)
   if o == nil or o == &quot;asc&quot; then
      return x.field1 &lt; y.field1
   end
   if o == &quot;dsc&quot; then
      return x.field1 &gt;= y.field1
   end
   error(&quot;Unknown option&quot;)
end

-- Example:
foo(FIRST, SECOND, &quot;asc&quot;)
foo(FIRST, SECOND, &quot;dsc&quot;)

Note that early returns make else unnecessary in this case but it might not always be true.

Anonymous function

But, hey, what's that? What if rather than a string option we pass something more bizarre like... something that can be called? Let's go with a simple function this time:

local asc = function (a, b) return a &lt; b end
local dsc = function (a, b) return a &gt;= b end

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

-- Example:
bar(FIRST, SECOND, asc)
bar(FIRST, SECOND, dsc)
bar(FIRST, SECOND, function (a, b) return a &lt; b end)

Higher-order function

The previous example doesn't really look that good with a such simple operation, but let's take it as a base and let's create a function that will return us the desired function:

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

-- Example:
local asc = make(function (a, b) return a &lt; b end)
local dsc = make(function (a, b) return a &gt;= b end)
asc(FIRST, SECOND)
dsc(FIRST, SECOND)

Actual generation

Now then, let's try something closer to code generation. Lua gives us the ability to load chunks as we go with load* function family.

Please note that load in 5.1 is different from load in 5.2 or load in 5.3. In 5.1 you want to use loadstring instead of load.

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

-- Example:
local asc = generate(&quot;&lt;&quot;)
local dsc = generate(&quot;&gt;=&quot;)
asc(FIRST, SECOND)
dsc(FIRST, SECOND)

huangapple
  • 本文由 发表于 2020年1月3日 16:07:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/59575104.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定