我想写一个函数/宏
(defun apply-funcs (functions value) ...)
因此调用(apply-funcs (list #'f #'g #'h) x)
将相当于(h (g (f x)))
。如何实现这一目标?
答案 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))))))