如何将函数列表应用于单个变量?

时间:2014-10-21 06:20:19

标签: common-lisp

我想写一个函数/宏

(defun apply-funcs (functions value) ...)

因此调用(apply-funcs (list #'f #'g #'h) x)将相当于(h (g (f x)))。如何实现这一目标?

3 个答案:

答案 0 :(得分:4)

看起来你想reduce一个值的函数列表。

CL-USER> (defun apply-funcs (functions value)
           (reduce (lambda (memo fn) (funcall fn memo))
                   functions :initial-value value))
CL-USER> (apply-funcs 
            (list (lambda (n) (+ 3 n))
                  (lambda (n) (- n 2))
                  (lambda (n) (* 2 n)))
            6)
14
CL-USER> 

您可能会将reduce视为其他语言的fold。我使用的是funcall而不是apply,因为您之前所说的内容((apply-funcs (list #'f #'g #'h) x) => (h (g (f x))))。如果apply是值列表,则使用x,每个元素都要绑定到单独的参数。例如,如果你想做类似

的事情
(apply-funcs 
 (list (lambda (a b c)
         (list (+ a c) (+ b c)))
       (lambda (d e)
         (+ d e)))
 (list 1 2 3))

然后在apply的定义中您需要funcall而不是apply-funcs

根据具体情况,您也可以采用宏路线;

(defmacro ->> (value &body functions)
  (reduce 
   (lambda (memo fn) `(funcall ,fn ,memo))
   functions :initial-value value))

基本上会做同样的事情。

CL-USER> (->> 6
           (lambda (n) (+ 3 n))
           (lambda (n) (- n 2))
           (lambda (n) (* 2 n)))

14
CL-USER> (macroexpand
      '(->> 6
        (lambda (n) (+ 3 n))
        (lambda (n) (- n 2))
        (lambda (n) (* 2 n))))

(FUNCALL (LAMBDA (N) (* 2 N))
         (FUNCALL (LAMBDA (N) (- N 2)) 
                  (FUNCALL (LAMBDA (N) (+ 3 N)) 6)))
T

答案 1 :(得分:4)

(defun apply-funcs (functions value)
  (loop for f in functions
        for result = (funcall f value) then (funcall f result)
        finally (return result)))

答案 2 :(得分:2)

来自Alexandria库的是compose(和multiple-value-compose)函数,包括compose的编译器宏。您所描述的内容似乎与

类似
(funcall (alexandria:compose #'h #'g #'f) x)

这样

(defun apply-funcs (functions value)
   (funcall (apply #'compose (reverse functions)) value))

会按你的意愿行事 - 虽然我怀疑直接调用compose可能对你的目的更有效,具体取决于具体情况。

库函数是:

(defun compose (function &rest more-functions)
  "Returns a function composed of FUNCTION and MORE-FUNCTIONS that applies its
arguments to to each in turn, starting from the rightmost of MORE-FUNCTIONS,
and then calling the next one with the primary value of the last."
  (declare (optimize (speed 3) (safety 1) (debug 1)))
  (reduce (lambda (f g)
        (let ((f (ensure-function f))
          (g (ensure-function g)))
          (lambda (&rest arguments)
        (declare (dynamic-extent arguments))
        (funcall f (apply g arguments)))))
          more-functions
          :initial-value function))

(define-compiler-macro compose (function &rest more-functions)
  (labels ((compose-1 (funs)
             (if (cdr funs)
                 `(funcall ,(car funs) ,(compose-1 (cdr funs)))
                 `(apply ,(car funs) arguments))))
    (let* ((args (cons function more-functions))
           (funs (make-gensym-list (length args) "COMPOSE")))
      `(let ,(loop for f in funs for arg in args
           collect `(,f (ensure-function ,arg)))
         (declare (optimize (speed 3) (safety 1) (debug 1)))
         (lambda (&rest arguments)
           (declare (dynamic-extent arguments))
           ,(compose-1 funs))))))