首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在lua中组合两个函数

在lua中组合两个函数
EN

Stack Overflow用户
提问于 2014-11-27 20:27:58
回答 2查看 978关注 0票数 3

我刚刚开始学习lua,所以我所要求的可能是不可能的。

现在,我有一个接受函数的方法:

代码语言:javascript
运行
复制
function adjust_focused_window(fn)
  local win = window.focusedwindow()
  local winframe = win:frame()
  local screenrect = win:screen():frame()
  local f, s = fn(winframe, screenrect)
  win:setframe(f)
end

我有几个函数可以接受这些框架和矩形(只显示一个):

代码语言:javascript
运行
复制
function full_height(winframe, screenrect)
   print ("called full_height for " .. tostring(winframe))
  local f = {
     x = winframe.x,
     y = screenrect.y,
     w = winframe.w,
     h = screenrect.h,
  }
  return f, screenrect
end

然后,我可以执行以下操作:

代码语言:js
复制
hotkey.bind(scmdalt, '-', function() adjust_focused_window(full_width) end)

现在,我如何组合几个函数来adjust_focused_window,而不改变它的定义。类似于:

代码语言:js
复制
hotkey.bind(scmdalt, '=', function() adjust_focused_window(compose(full_width, full_height)) end)

在哪里

compose2将返回一个函数,该函数接受与full_widthfull_height,并在内部执行类似以下操作:

代码语言:js
复制
full_height(full_width(...))
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-01-13 11:54:18

正如评论中提到的,要将两个函数链接在一起,您只需执行以下操作:

代码语言:javascript
运行
复制
function compose(f1, f2)
  return function(...) return f1(f2(...)) end
end

但是如果你想把两个以上的函数连接在一起呢?你可能会问,有没有可能将任意数量的函数“组合”在一起?

答案肯定是肯定的--下面我展示了实现这一点的三种不同方法,并快速总结了它们的后果。

迭代表法

这里的想法是依次调用列表中的每个函数。在执行此操作时,将上一次调用的返回结果保存到一个表中,然后解压缩该表并将其传递给下一次调用。

代码语言:javascript
运行
复制
function compose1(...)
    local fnchain = check_functions {...}
    return function(...)
        local args = {...}
        for _, fn in ipairs(fnchain) do
            args = {fn(unpack(args))}
        end
        return unpack(args)
    end
end

The check_functions上面的helper只是检查传入的东西是否真的是函数--如果不是,就抛出一个错误。为简洁起见,省略了实现。

+:相当直截了当的方法。可能是你第一次尝试就能想到的。

-:对资源的效率不是很高。调用之间有很多垃圾表来存储结果。您还必须处理打包和解包结果。

Y-组合器模式

这里的关键洞察是,即使我们调用的函数不是递归的,也可以通过将其打包到递归函数中来使其递归。

代码语言:javascript
运行
复制
function compose2(...)
  local fnchain = check_functions {...}
  local function recurse(i, ...)
    if i == #fnchain then return fnchain[i](...) end
    return recurse(i + 1, fnchain[i](...))
  end
  return function(...) return recurse(1, ...) end
end

+:不会像上面那样创建额外的临时表。精心编写为尾递归--这意味着调用长函数链不需要额外的堆栈空间。它有一种优雅的感觉。

元脚本生成

在最后一种方法中,您将使用一个lua函数,该函数实际生成执行所需函数调用链的确切lua代码。

代码语言:javascript
运行
复制
function compose3(...)
    local luacode = 
    [[
        return function(%s)
            return function(...)
                return %s
            end
        end
    ]]
    local paramtable = {}
    local fcount = select('#', ...)
    for i = 1, fcount do
        table.insert(paramtable, "P" .. i)
    end
    local paramcode = table.concat(paramtable, ",")
    local callcode = table.concat(paramtable, "(") ..
                     "(...)" .. string.rep(')', fcount - 1)
    luacode = luacode:format(paramcode, callcode)
    return loadstring(luacode)()(...)
end

The loadstring(luacode)()(...)可能需要一些解释。在这里,我选择将函数链编码为参数名(P1, P2, P3等)在生成的脚本中。额外的

()括号是用来“解包”嵌套函数的,所以最里面的函数就是返回的函数。The The P1, P2, P3 ... Pn参数成为链中每个函数的捕获上限值,例如。

代码语言:javascript
运行
复制
function(...)
  return P1(P2(P3(...)))
end

注意,您也可以使用setfenv但我选择这条路线只是为了避免Lua5.1和5.2在如何设置函数环境方面的重大变化。

+:避免像方法2那样的额外中间表。不滥用堆栈。

-:需要额外的字节码编译步骤。

票数 4
EN

Stack Overflow用户

发布于 2021-02-25 12:24:21

您可以遍历传递的函数,使用前一个函数的结果连续调用链中的下一个函数。

代码语言:javascript
运行
复制
function module._compose(...)
  local n = select('#', ...)
  local args = { n = n, ... }
  local currFn = nil

  for _, nextFn in ipairs(args) do
    if type(nextFn) == 'function' then
      if currFn == nil then
        currFn = nextFn
      else
        currFn = (function(prev, next)
          return function(...)
            return next(prev(...))
          end
        end)(currFn, nextFn)
      end
    end
  end

  return currFn
end

请注意立即调用的函数Expressions它允许重用的函数变量不会调用无限递归循环,这在以下代码中发生:

代码语言:javascript
运行
复制
function module._compose(...)
  local n = select('#', ...)
  local args = { n = n, ... }
  local currFn = nil

  for _, nextFn in ipairs(args) do
    if type(nextFn) == 'function' then
      if currFn == nil then
        currFn = nextFn
      else
        currFn = function(...)
          return nextFn(currFn(...)) -- this will loop forever, due to closure
        end
      end
    end
  end

  return currFn
end

虽然Lua不支持三元运算符,short-circuit evaluation可以用来移除内部的

if声明:

代码语言:javascript
运行
复制
function module.compose(...)
  local n = select('#', ...)
  local args = { n = n, ... }
  local currFn = nil

  for _, nextFn in ipairs(args) do
    if type(nextFn) == 'function' then
      currFn = currFn and (function(prev, next)
        return function(...)
          return next(prev(...))
        end
      end)(currFn, nextFn) or nextFn
    end
  end

  return currFn
end
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27170825

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档