haskeller
haskeller

Reputation: 141

How to dynamically generate functions in Lua?

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 < y.field1 --there 0:'<',1:'>=' 
end

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

How to generate this code dynamically?

Upvotes: 0

Views: 628

Answers (1)

Aki
Aki

Reputation: 2928

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 --[[ < or >= ]] 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 == "asc" then
      return x.field1 < y.field1
   end
   if o == "dsc" then
      return x.field1 >= y.field1
   end
   error("Unknown option")
end

-- Example:
foo(FIRST, SECOND, "asc")
foo(FIRST, SECOND, "dsc")

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 < 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

-- Example:
bar(FIRST, SECOND, asc)
bar(FIRST, SECOND, dsc)
bar(FIRST, SECOND, function (a, b) return a < 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 < b end)
local dsc = make(function (a, b) return a >= 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("return function (x, y) return x.field1 %s y.field1 end", op))()
end

-- Example:
local asc = generate("<")
local dsc = generate(">=")
asc(FIRST, SECOND)
dsc(FIRST, SECOND)

Upvotes: 4

Related Questions