elisp 怎样迭代调用一系列相似的函数?

比如现在有一个下面这样的函数列表(注意: 这里只是为了举例方便,实际可能远远不止这几个函数。)

'(fun1 fun2 fun3)

函数列表是作为参数来传递的,请问用什么样的 form 才能达到如下的效果:

(fun1 (fun2 (fun3 arg)))

**PS:**顺序不重要

(defun demo( args &rest funlist )
  (dolist (f (reverse funlist))
    (setq args (funcall f args))
    )
  
  )
(demo "hello" 'message 'print)

2 个赞

谢谢!

补充一点:顺序不重要的情形下,可以不用加 reverse 函数。

来个递归版的:

(defun recall (arg fn &rest rest-fns)
  (if (null rest-fns)
      (funcall fn arg)
    (funcall fn (apply 'recall arg (car rest-fns) (cdr rest-fns)))))

(defun add1(num)
  (+ num 1))

(recall 0 'add1 'add1 'add1)
;; => 3
1 个赞

这个本质就是 foldRight。dash.el 里面就有 -reduce-r-from:

(-reduce-r-from #'funcall 2 '(1+ 1+))

或者用 Emacs 25 以后自带的 seq.el,这个只有 foldLeft,需要 reverse 列表才能和 foldRight 一样。不过既然楼主不关心顺序,下面就没加 nreverse:

(seq-reduce (lambda (acc x) (funcall x acc)) '(1+ 1+) 2)

cl-reduce 也是可以的(和 seq-reduce 类似):

(cl-reduce (lambda (acc x) (funcall x acc)) '(1+ 1+) :initial-value 2)
1 个赞

前面没认真看,以为参数必须是不定参 &rest,把问题搞复杂了(更😅的是我第一反应竟然是定义一个 add1, 而不是用 1+)。固定参不需要 apply

(defun recall (arg fn-list)
  (if fn-list
      (funcall (car fn-list) (recall arg (cdr fn-list)))
    arg))

(recall 0 '(1+ 1+ 1+))
;; => 3
1 个赞