Elisp中有没有办法编写函数来组合函数?

问题描述 投票:0回答:3

我想创建一个函数,它返回几个其他函数的组合,这样

(funcall (compose 'f 'g) x) == (f (g x))

我觉得我在这方面非常失败。迄今为止我最好的尝试:

(defun compose (funcs)
  "composes several funcitons into one"
  (lambda (arg)
    (if funcs
        (funcall (car funcs) (funcall (compose (cdr funcs)) arg))
        arg)))

但由于某种原因,以下仍然返回 0:

(funcall (compose '(
           (lambda (a) (* a 3))
           (lambda (a) (+ a 2))
)) 0)

有办法解决吗?

emacs elisp
3个回答
7
投票

您的代码需要词法作用域的 lambda,而 Emacs Lisp 默认情况下不支持。如果您使用 Emacs 24,请将

lexical-binding
设置为
t
,您的示例将按编写的方式工作。

如果您使用的是较旧的 Emacs,则可以使用显式

lexical-let
形式创建词法范围的 lambda:

(require 'cl)

(defun compose (funcs)
  "composes several funcitons into one"
  (lexical-let ((funcs funcs))
    (lambda (arg)
      (if funcs
          (funcall (car funcs) (funcall (compose (cdr funcs)) arg))
        arg))))

0
投票

如果您想要一个库,请参阅 dash 功能,它有

-compose
采取
(&rest fns)


0
投票

对于那些不喜欢其他答案的人,这里有一个

compose
,其中

  1. 处理最内层函数的任意数量的参数,
  2. 没有依赖项(既不是包,也不是打开词法绑定),并且
  3. 更接近于手写 lambda:
(defmacro compose (&rest functions)
    (setq functions (nreverse functions))
    (let ((form `(apply ,(car functions) arguments)))
        (while (setq functions (cdr functions))
            (setq form `(funcall ,(car functions) ,form)))
        `(lambda (&rest arguments) ,form)))

这也说明了另一种方法:无需创建必须记住函数列表并在每次调用时遍历它的递归 lambda,而是将整个函数列表扩展为 lambda 形式一次。

例如,

(compose 'f 'g 'h)
扩展为:

(lambda (&rest arguments)
    (funcall 'f (funcall 'g (apply 'h arguments))))
© www.soinside.com 2019 - 2024. All rights reserved.