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

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

我想写一个函数/宏

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

这样调用

(apply-funcs (list #'f #'g #'h) x)
就相当于
(h (g (f x)))
。如何才能做到这一点?

common-lisp
4个回答
4
投票
(defun apply-funcs (functions value)
  (loop for f in functions
        for result = (funcall f value) then (funcall f result)
        finally (return result)))

3
投票

看起来您想要

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

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))))))

0
投票

您可以将

reduce
:from-end t
结合使用,如下例所示:

(defun working-copy-name (repository)
  (flet ((trim-final-dotgit (string)
           (subseq string 0 (search ".git" string :from-end t)))
         (trim-initial-location (string)
           (subseq
            string
            (or
             (1+ (position #\Slash string :from-end t))
             0)))
         (trim-initial-cl-hyphen (string)
           (if (eq 0 (search "cl-" string))
               (subseq string 3)
               string)))
    (reduce #'funcall
            (list #'trim-initial-cl-hyphen
                  #'trim-final-dotgit
                  #'trim-initial-location)
            :initial-value repository
            :from-end t)))

这个功能的用法是

CL-USER> (working-copy-name "https://github.com/melusina-org/cl-atelier.git")
"atelier"

请注意,此变体使用组合顺序(列表中的第一个函数是应用的最后一个函数)。如果您坚持按照应用的顺序编写函数列表,您可以

reverse
列表或使用
funcall
的后缀版本,如
(lambda (x f) (funcall f x))
并删除
:from-end t
)

© www.soinside.com 2019 - 2024. All rights reserved.