SMAS 2.0bA Primer |
5. LISP Code for the Examples (Skills)
Each agent must have some skills.
The simplest case is the case of multiplying agents. A multiplying agent when receiving a message containing a MULTIPLY tag simply performs the operation and returns the result. The skill is specified using the defskill macro as follows:
(defskill MULTIPLY MUL-1 :static-fcn static-multiply)
where static-multiply represents a function, associated with the :static-fcn tag, which implements the skill as follows:
(defun static-multiply (agent environment n1 n2) (declare (ignore environment) (static-exit agent (* n1 n2))
In this code, declare tells the compiler to ignore the environment argument that we do not use (more about it later), and a call to static-exit is required for exiting.
Note that the static-multiply function will be shared by all MULTIPLY agents.
For the factorial agent, specifying the skill is a more complex business. Indeed, when the FACTORIAL agent receives a request to compute a particular factorial, since it does not know how to multiply two numbers, it will have to subcontract part of the job. Therefore, the process will be done in several steps: the initial step, called static, will consists of setting up the computational process sending the initial messages, the other steps will have to tell what the FACTORIAL agent must do when receiving an answer for a job it has sub-contracted. We associate the latter steps with the dynamic behavior of the agents.
(defskill FACTORIAL FACTORIAL :static-fcn static-dumb-factorial :dynamic-fcn dynamic-dum-factorial)
As a first dumb approach, the FACTORIAL agent uses only one sub-contracting agent MUL-1, asking it to multiply the result of the previous multiplication by the next number it can generate. The code is written as follows:
(defun static-dumb-factorial (agent environment nn) "this function is called when a factorial agent receives a request ~ to prcess a number. If the number is less than or equal to 2, then ~ the result is 1 and is returned right away. If the number is ~ greatr than 2, then the agent creates a subtask, subcontracting ~ the agent MUL-1 (presumably a friend of its) for performing the ~ multiply operation. At the same time it records in its memory ~ where it stands, i.e., the value of the next number to process." (if (< nn 2) (static-exit agent 1) (let (env subtask) ;; create a subtask for computing the product of ;; the first two top values asking agent MUL-1 to compute (send-subtask agent :to 'MUL-1 :action 'multiply :args (list nn (1- nn))) ;; define the :n tag in the environment to record the value ;; of the next products to compute. e.g., nn-2 -> (nn - 2)! (setq env (append (list :n (- nn 2)) environment)) ;; record environment (update-environment agent env) ;; quit (static-exit agent :done))))
The functions create-subtask, send-subtask, update-environment, static-exit, dynamic-exit (in the following function), are provided by CL-SMAS.
The dynamic part of the FACTORIAL skill has to decide what to do when a result is returned by a sub-contractor. Since FACTORIAL is using only one sub-contractor, the computation will be finished when it gets an answer and when at the same time the value recorded in its memory (environment) is less or equal than 1, in which case FACTORIAL returns the answer it just got from the sub-contractor. Otherwise, FACTORIAL takes the answer and ask MUL-1 to multiply it with the value it has in its memory, and updates its memory.
(defun dynamic-dumb-factorial (agent environment answer) "this function is called whenever we get a result from a subtask. This ~ approach is not particularly clever, since the computation is linear ~ and uses the same multiplying agent." (let ((nn (cadr (member :n environment))) subtask) ;; if the recorded value is 1 or less, then we are through (if (< nn 2) ;; thus we do a final exit (dynamic-exit agent answer) ;; otherwise we multiply the answer with the next high number ;; creating a subtask (progn (setq subtask (create-subtask agent)) (send-subtask agent subtask :to 'MUL-1 :action 'multiply :args (list answer nn)) ;; update environment (setf (cadr (member :n environment)) (1- nn)) (update-environment agent environment) answer ))))
The arguments for the skill functions are predefined.
For the static function: agent, environment and arguments, represent the agent itself (somewhat similar to the self variable in an object environment), its environment and the arguments corresponding to the contents of the received message.
For the dynamic function: agent, environment and answer, represent the agent, the environment and the answer returned by the sub-contractor.
The environment variable contains the current state of the agent memory. The designer is free to structure such a value as she likes. The memory is updated through the update-environment function.
Copyrignt Jean-Paul Barthès@UTC, 1998 Last update, Jan 99