I have various functions and I want to call each function with the same value. For instance, I have these functions:
(defun OP1 (arg) ( + 1 arg) )
(defun OP2 (arg) ( + 2 arg) )
(defun OP3 (arg) ( + 3 arg) )
And a list containing the name of each function:
(defconstant *OPERATORS* '(OP1 OP2 OP3))
So far, I'm trying:
(defun TEST (argument) (dolist (n *OPERATORS*) (n argument) ) )
I've tried using eval
, mapcar
, and apply
, but these haven't worked.
This is just a simplified example; the program that I'm writing has eight functions that are needed to expand nodes in a search tree, but for the moment, this example should suffice.
Other answers have provided some idiomatic solutions with mapcar
. One pointed out that you might want a list of functions (which *operators*
isn't) instead of a list of symbols (which *operators*
is), but it's OK in Common Lisp to funcall
a symbol. It's probably more common to use some kind of mapping construction (e.g., mapcar
) for this, but since you've provided code using dolist
, I think it's worth looking at how you can do this iteratively, too. Let's cover the (probably more idiomatic) solution with mapping first, though.
You have a fixed argument, argument
, and you want to be able to take a function function
and call it with that `argument. We can abstract this as a function:
(lambda (function)
(funcall function argument))
Now, we want to call this function with each of the operations that you've defined. This is simple to do with mapcar
:
(defun test (argument)
(mapcar (lambda (function)
(funcall function argument))
*operators*))
Instead of operators, you could also write '(op1 op2 op3)
or (list 'op1 'op2 'op3)
, which are lists of symbols, or (list #'op1 #'op2 #'op3)
which is a list of functions. All of these work because funcall
takes a function designator as its first argument, and a function designator is
an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself).
You can do this using dolist
. The [documentation for actually shows that dolist
has a few more tricks up its sleeve. The full syntax is from the documentation
dolist (var list-form [result-form]) declaration* {tag | statement}*
We don't need to worry about declarations here, and we won't be using any tags, but notice that optional result-form. You can specify a form to produce the value that dolist
returns; you don't have to accept its default nil
. The common idiom for collecting values into a list in an iterative loop is to push
each value into a new list, and then return the reverse
of that list. Since the new list doesn't share structure with anything else, we usually reverse it destructively using nreverse
. Your loop would become
(defun test (argument)
(let ((results '()))
(dolist (op *operators* (nreverse results))
(push (funcall op argument) results))))
Stylistically, I don't like that let
that just introduces a single value, and would probably use an &aux
variable in the function (but this is a matter of taste, not correctness):
(defun test (argument &aux (results '()))
(dolist (op *operators* (nreverse results))
(push (funcall op argument) results)))
You could also conveniently use loop
for this:
(defun test2 (argument)
(loop for op in *operators*
collect (funcall op argument)))
You can also do somewhat succinctly, but perhaps less readably, using do
:
(defun test3a (argument)
(do ((results '() (list* (funcall (first operators) argument) results))
(operators *operators* (rest operators)))
((endp operators) (nreverse results))))
This says that on the first iteration, results
and operators
are initialized with '()
and *operators*
, respectively. The loop terminates when operators
is the empty list, and whenever it terminates, the return value is (nreverse results)
. On successive iterations, results
is a assigned new value, (list* (funcall (first operators) argument) results)
, which is just like push
ing the next value onto results, and operators
is updated to (rest operators)
.
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments