2

我有各种函数,我想用相同的值调用每个函数。例如,我有这些功能:

(defun OP1 (arg) ( + 1 arg) )
(defun OP2 (arg) ( + 2 arg) )
(defun OP3 (arg) ( + 3 arg) )

以及一个包含每个函数名称的列表:

(defconstant *OPERATORS* '(OP1 OP2 OP3))

到目前为止,我正在尝试:

(defun TEST (argument) (dolist (n *OPERATORS*) (n argument) ) )

我尝试过使用evalmapcarapply,但这些都不起作用。

这只是一个简化的例子;我正在编写的程序具有扩展搜索树中的节点所需的八个函数,但目前,这个例子就足够了。

4

3 回答 3

3

其他答案提供了一些惯用的解决方案mapcar。有人指出,您可能想要一个函数列表(*operators*不是)而不是符号列表(它*operators*是),但在 Common Lisp 中可以funcall使用符号。为此使用某种映射构造(例如,mapcar)可能更常见,但是由于您已经使用 提供了代码dolist,我认为也值得研究一下如何迭代地执行此操作。不过,让我们先介绍一下映射的(可能更惯用的)解决方案。

映射

您有一个固定的参数 ,argument并且您希望能够获取一个函数function并使用该参数调用它。我们可以将其抽象为一个函数:

(lambda (function)
  (funcall function argument))

现在,我们要使用您定义的每个操作调用此函数。这很简单mapcar

(defun test (argument)
  (mapcar (lambda (function)
            (funcall function argument))
          *operators*))

除了运算符,您还可以编写'(op1 op2 op3)or (list 'op1 'op2 'op3),它们是符号列表,或者(list #'op1 #'op2 #'op3)是函数列表。所有这些工作都是因为funcall函数指示符作为其第一个参数,而函数指示符是

一个表示函数的对象,它是以下之一:符号(表示在全局环境中由该符号命名的函数)或函数(表示自身)。

迭代地

您可以使用dolist. [documentation for 实际上显示它dolist还有一些技巧。完整的语法来自文档

dolist (var list-form [result-form]) declaration* {tag | statement}*

我们不需要担心这里的声明,也不会使用任何标签,但请注意可选的 result-form。您可以指定一个表单来生成dolist返回的值;你不必接受它的默认值nil。在迭代循环中将值收集到列表中的常见习惯用法是将push每个值放入一个新列表中,然后返回该reverse列表的。由于新列表不与其他任何东西共享结构,我们通常使用破坏性地反转它nreverse。你的循环会变成

(defun test (argument)
  (let ((results '()))
    (dolist (op *operators* (nreverse results))
      (push (funcall op argument) results))))

从风格上讲,我不喜欢let只引入一个值,并且可能会&aux在函数中使用一个变量(但这是一个品味问题,而不是正确性问题):

(defun test (argument &aux (results '()))
  (dolist (op *operators* (nreverse results))
    (push (funcall op argument) results)))

您也可以方便地使用loop

(defun test2 (argument)
  (loop for op in *operators*
     collect (funcall op argument)))

您也可以使用以下方式做一些简洁,但可能不太可读do

(defun test3a (argument)
  (do ((results '() (list* (funcall (first operators) argument) results))
       (operators *operators* (rest operators)))
      ((endp operators) (nreverse results))))

这表示在第一次迭代中,results和分别用和operators初始化。循环在空列表时终止,并且无论何时终止,返回值为。在连续迭代中,是一个分配的新值,,就像将下一个值添加到结果中一样,并更新为。'()*operators*operators(nreverse results)results(list* (funcall (first operators) argument) results)pushoperators(rest operators)

于 2013-10-03T13:22:00.493 回答
2
  1. FUNCALL 适用于符号。
  2. 从愚蠢的招数系。
(defconstant *operators* '(op1 op2 o3))

(defun test (&rest arg)
  (setf (cdr arg) arg)
  (mapcar #'funcall *operators* arg))
于 2013-10-03T07:07:28.337 回答
2

有一个库,在任何复杂的项目中几乎都是必需的:Alexandria。它有许多有用的功能,还有一些东西可以让你的代码更漂亮/更少冗长和更有意识。

假设您想调用多个具有相同值的函数。以下是你的做法:

(ql:quickload "alexandria")

(use-package :alexandria)

(defun example-rcurry (value)
  "Calls `listp', `string' and `numberp' with VALUE and returns
a list of results"
  (let ((predicates '(listp stringp numberp)))
    (mapcar (rcurry #'funcall value) predicates)))

(example-rcurry 42) ;; (NIL NIL T)
(example-rcurry "42") ;; (NIL T NIL)

(defun example-compose (value)
  "Calls `complexp' with the result of calling `sqrt'
with the result of calling `parse-integer' on VALUE"
  (let ((predicates '(complexp sqrt parse-integer)))
    (funcall (apply #'compose predicates) value)))

(example-compose "0") ;; NIL
(example-compose "-1") ;; T

函数rcurrycompose来自 Alexandria 包。

于 2013-10-03T22:46:29.313 回答