Is it possible to emulate binding in Lua?

Given a lua function with one argument, is it possible to associate this argument with a fixed value to get a function without arguments?

More generally, how do I associate certain input arguments to a lua function with specific values?

+6
source share
3 answers

Yes, this can be done in almost any language that has functions as first-class values.

function f1(a) return a+1 end function bind(a) return function() return f1(a) end end local f2 = bind(42) print(f2()) -- 43 

This specific example works with a specific function and number of arguments, but can be easily extended to use arbitrary functions / arguments instead.

+10
source

I just made a helper function to do what Boost :: bind does:

  • It processes the placeholder to be able to replace any positional argument
  • It handles nothing anywhere

Using:

function_binder(base_function, placeholders_format, args...)

  • base_function (function): function to carry
  • placeholders_format (string): represent how arguments should be placed, each character represents an argument
    • . : present an argument from the shell
    • A : represents an argument from a binder
    • ~ : represent all pending arguments from the shell

See the following example for a better understanding.

A simple example:

 local function divide(a, b) return a / b end local div_by_4 = function_binder(divide, ".A", 4) print(div_by_4(12)) -- => prints: 3 local div_12_by = function_binder(divide, "A.", 12) print(div_12_by(4)) -- => prints: 3 

You can also use the built-in function:

 local debug_print = function_binder(print, "A~", "DEBUG:") debug_print("some logs for debug") -- => prints: DEBUG some logs for debug -- '~' will put all following arguments: debug_print("some", "logs", "for", "debug") -- => prints: DEBUG some logs for debug 

You can also create complex things:

 local function stuff(...) print("Args: ", ...) end local wrapper = function_binder(stuff, ".A..AA~", "two", "five", "six") wrapper(1, 3, 4, 7, nil, 9) -- => prints: 1 two 3 4 five six 7 nil 9 

Object oriented path

Customization

To make more OO files, you can use the debug library:

 -- apply metatable to all function debug.setmetatable(function()end, { __index = { bind = function_binder, }, }) 

Using

 local debug_print = print:bind("A~", "DEBUG:") debug_print("add some log") -- => prints: DEBUG: add some log 

Here is the function_binder code:

 local function packed_args_append(packed, nb_insert, ...) nb_insert = nb_insert > 0 and nb_insert or select('#', ...) for i = 1, nb_insert do packed[packed.n + 1] = select(i, ...) packed.n = packed.n + 1 end end -- replace table.unpack as it doesn't always handle nil values correctly.. local function unpacknil(packed) local nb_args = packed.n local function unpack_n(n) if n == nb_args then return packed[n] end return packed[n], unpack_n(n + 1) end return unpack_n(1) end function function_binder(self, placeholder_format, ...) local placeholders = table.pack(...) return function(...) local args = {n = 0} local arg_idx = 1 local placeholder_idx = 1 for c in placeholder_format:gmatch"." do if c == 'A' then packed_args_append(args, 1, placeholders[placeholder_idx]) placeholder_idx = placeholder_idx + 1 elseif c == '.' then packed_args_append(args, 1, select(arg_idx, ...)) arg_idx = arg_idx + 1 elseif c == '~' then packed_args_append(args, -1, select(arg_idx, ...)) break end end --return self(table.unpack(args)) return self(unpacknil(args)) end end 

It works great with Lua 5.3

Hosting here: https://github.com/Bew78LesellB/luaMiscTests/commit/e9e52aea7933b58332fff36738356f18aed3db92

0
source

You can also do the following if you just want to define a default value for the parameter:

 function f1(a) return (a or 0) + 1 end 

The code uses the fact that the or operator returns it to the first operand if it can be calculated as true (all values ​​except nil and false). However, if the first operand is false, the second operand is returned. Therefore, when f1 is called without parameters, a is set to nil, nil evaluates to false, (false or 0) returns 0. Ergo, in this case a has a default value of 0, which means that you can call f1 (), and also f1 (0) (both will give the same result):

 print(f1()) -- 1 print(f1(0)) -- 1 print(f1(5)) -- 6 
-1
source

Source: https://habr.com/ru/post/918546/


All Articles