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) ) )

evalmapcar、およびを使用してみましたが、うまくいきませapplyんでした。

これは簡単な例です。私が書いているプログラムには、検索ツリーでノードを展開するために必要な 8 つの関数がありますが、今のところ、この例で十分です。

4

3 に答える 3

3

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.

Mapping

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).

Iteratively

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 pushing the next value onto results, and operators is updated to (rest operators).

于 2013-10-03T13:22:00.493 に答える