其他答案提供了一些惯用的解决方案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)
push
operators
(rest operators)