简短匿名函数 |
|
function() return ... end
对于短函数来说过于冗长,例如函数式编程风格中的 lambda 函数。例如,在 Lua 中,map
函数 [1] 函数可能被这样使用
local y = map(function(p) return translate[p] end, x)
在一些语言中,例如 Perl,这可以更简洁地写成
my @y = map { $translate{$_} } @x;
my @y = @translate{@x};
这里有一些 Lua 的替代方法。
我们可以使用一个名为 fn
的实用函数来改进 Lua 中的这种情况,该函数从更短的字符串表示中创建匿名函数。它可能被这样使用
local y = map(fn("L1[P1]", translate), x)
fn
接受一个字符串表示作为参数,该字符串表示定义了要创建的匿名函数的返回值表达式,以及一个局部变量列表,这些变量在表达式中被引用为 L1
、L2
、L3
、...、L9
。匿名函数的参数被引用为 P1
、P2
、P3
、...、P9
。fn
可以这样定义
function fn(s, ...) local src = [[ local L1, L2, L3, L4, L5, L6, L7, L8, L9 = ... return function(P1,P2,P3,P4,P5,P6,P7,P8,P9) return ]] .. s .. [[ end ]] return loadstring(src)(...) end
以下是运行此示例的其余代码
function map(f, t) local t2 = {} for k,v in pairs(t) do t2[k] = f(v) end return t2 end local translate = {["hello"] = "hola", ["bye"] = "adi�s", ["sir"] = "se�or"} local t = map(fn("L1[P1]", translate), {"hello", "sir"}) print(table.concat(t, " ")) --> hola se�or
但是,请注意,如果 fn
在同一个字符串化表达式上重复调用(例如在循环中),则效率低下,因为每次调用都会调用 loadstring
(这涉及代码生成)。这可以通过对 loadstring
进行记忆化来改进(参见 FuncTables)。这种基本模式(带有记忆化)在 CodeGeneration 中使用过。
如果您的匿名函数没有局部变量,则语法更短。例如,fn"P1 > 0"
是一个函数,它检查其参数是否大于零。
有些人可能会建议支持任意数量的 Ln
和 Pn
变量;但是,请记住,此技术仅适用于短的一行表达式。
此外,在某些情况下,可以将字符串到函数的转换移动到 map 函数中,以获得
local y = map("P1 * 2", x)
这里还有另一种语法
getmetatable("").__call = function(s, ...) return assert(loadstring("return " .. s))()(...) end ("function(x,y) print(x+y) end")(2,3) -- prints 5
这种技术也被称为“字符串 lambda”[1],它已在 JavaScript 和 Erlang 中实现
这种方法有其用途,但并不理想,因为词法变量不能直接在 lambda 内部使用。(一个可能的解决方案是 StringInterpolation。)
--DavidManura,2007-02
字符串 lambda 的实现可与 PenlightLibraries 一起使用。支持两种形式,第一种类似于 MetaLua 实现的,第二种类似于 Scala lambda
> require 'pl' > L = utils.string_lambda > = L'|x| x+2' (1) 3 > = L'_+2' (0) 2 > ls = List{'one','two','three'} > = ls:map(L'_:upper()') {ONE,TWO,THREE}
结果使用备忘录模式缓存。最初,Penlight 中任何期望函数的函数都可以传递一个字符串,并尝试将该字符串解析为字符串 lambda,但最终我们认为这会引入太多魔法。(这绝对适用于对字符串元表的任何修改,以使其直接可调用。)
Steve Donovan,2012
以下方法仅使用 Lua 进行源代码过滤,因此它是完全自包含的
assert(loadstring((([[--filtered function pass(f) f() end function fail(f) if pcall(f) then error 'fail expected' end end pass << x = 1 + 2 >> fail << x = 1 + nil >> print 'DONE' ]]):gsub('<<', '(function(a,b) '):gsub('>>', ' end)'))))()
在第一行的“[["之后,重要的是要有一些文本(例如“--”),以确保第一行在任何错误消息中的行号中被计算在内。此外,此行上的文本将显示在错误消息中,因此最好能传达一些含义。
以下是语法和运行时错误的示例
lua: src.lua:1: [string "--filtered..."]:4: 'then' expected near 'thenn' stack traceback: [C]: in function 'assert' src.lua:1: in main chunk [C]: ? $ lua src.lua lua: [string "--filtered..."]:6: attempt to call global 'Pass' (a nil value) stack traceback: [string "--filtered..."]:6: in main chunk src.lua:9: in main chunk [C]: ?
这种方法的一个缺点可能是语法高亮器将整个主代码着色为字符串(但是,它在 XEmacs 下着色良好)。您可以通过对外部文件执行 `loadfile` 而不是 `loadstring` 来解决此问题。
MetaLua [2] 提供了 `|x,y| x*y` 的语法,用于 `function(x,y) return x*y end`。
Lambda 函数补丁 [3] 通过修补 Lua 解析器提供了相同的语法。
在 LuaPowerPatches 中的“do 补丁”使 `= do ... end` 成为 `= function() ... end` 的语法糖。
LuaRiscLua 提供了 `\ ` 用于 `function` 和 `=>` 用于 `return` 的语法糖。因此
curry = \(f) => \(x) => \(y) => f(x,y) end end end
在 LuaPowerPatches 中的简洁匿名函数补丁使 `= [ ... ]` 成为 `= function() ... end` 的语法糖,而 `= [| ... ]` 则简化为 `= function() return ... end`
Boost 风格的占位符表达式(例如 `tablex.map(_1*_1,{1,2,3,4}) --> {1,4,9,16}`)在 [4]、LuaList:2009-03/msg00452.html 和 LuaList:2009-04/msg00069.html 中的“占位符表达式”中进行了描述。