使用相同的值调用多个函数

时间:2013-10-03 06:11:44

标签: lisp common-lisp

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

(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,但这些都没有用。

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

3 个答案:

答案 0 :(得分: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)(list 'op1 'op2 'op3)(符号列表)或(list #'op1 #'op2 #'op3)这是一个函数列表。所有这些工作都是因为funcallfunction designator作为其第一个参数,而函数指示符是

  

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

迭代

您可以使用dolist执行此操作。 [实际上的文档显示dolist还有一些技巧。完整语法来自the documentation

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

我们不需要担心这里的声明,我们也不会使用任何标签,但请注意可选的结果表单。您可以指定一个表单来生成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))))

这表示在第一次迭代时,resultsoperators分别使用'()*operators*进行初始化。当operators为空列表时,循环终止,每当它终止时,返回值为(nreverse results)。在连续迭代中,results是指定的新值(list* (funcall (first operators) argument) results),就像{{1}将下一个值放到结果上一样,push更新为operators }。

答案 1 :(得分:2)

  1. FUNCALL适用于符号。
  2. 来自愚蠢的技巧部门。
  3. (defconstant *operators* '(op1 op2 o3))
    
    (defun test (&rest arg)
      (setf (cdr arg) arg)
      (mapcar #'funcall *operators* arg))
    

答案 2 :(得分:2)

有一个图书馆,在任何复杂的项目中几乎都是强制性的:亚历山大。它有许多有用的功能,还有一些能使你的代码更漂亮/更简洁,更有意识的东西。

说,你想用相同的值调用许多函数。这是你如何做到的:

(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包。