我有各种各样的功能,我想用相同的值调用每个函数。例如, 我有这些功能:
(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) ) )
我尝试过使用eval
,mapcar
和apply
,但这些都没有用。
这只是一个简化的例子;我正在编写的程序有八个在搜索树中展开节点所需的函数,但目前这个例子就足够了。
答案 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)
这是一个函数列表。所有这些工作都是因为funcall
将function 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))))
这表示在第一次迭代时,results
和operators
分别使用'()
和*operators*
进行初始化。当operators
为空列表时,循环终止,每当它终止时,返回值为(nreverse results)
。在连续迭代中,results
是指定的新值(list* (funcall (first operators) argument) results)
,就像{{1}将下一个值放到结果上一样,push
更新为operators
}。
答案 1 :(得分:2)
(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
函数rcurry
和compose
来自Alexandria包。