Il arrive qu'on parle ici ou là de "récursivité" ou de "fonctions récursives". Qu'en est il plus précisément ?... Comment ça se traduit en LISP ?... Je vais tenter de répondre à ces questions. Une fonction récursive est une fonction qui s'appelle elle même pendant son exécution. En LISP on reconnaît un fonction récursive à l'appel fait à elle même dans son DEFUN. L'exemple le plus souvent utilisé pour expliquer la récursivité est la fonction factorielle, petit rappel de math : Factorielle n s'écrit n! si n = 0 => n! = 1 et pour tout n > 0 => n! = n * (n - 1)! Par exemple : 4! = 4 * 3! = 4 * 3 * 2! = 4 * 3 * 2 * 1! = 4 * 3 * 2 * 1 = 24 En LISP, on peut écrire : (defun fact (n)
(if (zerop n)
1
(* n (fact (1- n)))
)
) Une fonction définie récursivement contient au moins une condition d'arrêt : (zerop n) -> 1 et un appel récursif : (* n (fact (1- n))) Par exemple : (fact 4) sera interprété une première fois (* 4 (fact 3)), puis (* 4 (* 3 (fact 2))) etc,... jusqu'à (* 4 (* 3 (* 2 (* 1 (fact 0))))) où (fact 0) remplit la condition d'arrêt et retourne 1 On appelle "empilement" les appels successifs à la fonction jusquà la condition d'arrêt et "dépilement" leurs interprétations depuis cette condition. On peut voir la procédure dans la fenêtre de suivi de la console VisualLISP en "traçant" la fonction fact avec la fonction LISP trace : http://img155.imageshack.us/img155/4381/rcurs1ku8.png Du fait de l'utilisation de la pile, l'usage de fonctions récursives permet souvent de faire de manière plus élégante ce qui avec une boucle (while) aurait nécéssité l'utilisation d'une nouvelle variable pour stocker les résultats successifs. Par exemple la fonction fact définie de manière itérative, avec while : (defun fact (n / rslt)
(cond
(( (T
(setq rslt n)
(while ( (setq rslt (* rslt (setq n (1- n))))
)
)
)
) L'usage de fonction récursives est aussi très pratique avec des listes. ;;; TRUNC Retourne la liste tronquée à partir de la première occurrence
;;; de l'expression (liste complémentaire de celle retournée par MEMBER)
(defun trunc (expr lst)
(cond
;; Conditions d'arrêt
((or (null lst)
(equal (car lst) expr)
)
nil
)
;; Appel récursif
(T (cons (car lst) (trunc expr (cdr lst))))
)
) http://img86.imageshack.us/img86/4333/rcurs2je6.png Pour comparaison, la fonction trunc définie de façon itérative : (defun trunc (elt lst / n rslt)
(setq n 0)
(while
;; Conditions d'arrêt
(and ( (not (equal (nth n lst) elt))
)
(setq rslt (cons (nth n lst) rslt))
(setq n (1+ n))
)
;; Résultat
(reverse rslt)
) Les fonctions récursives ont aussi quelques inconvénients, la pile n'est pas infinie et ne permet qu'un certains nombre d'évaluations au-delà duquel la "limite de la pile interne atteinte" produit une erreur. D'après mes essais, cette limite semble être fixée à 19975 sur AutoCAD. On ne peut donc utiliser de fonction récursives sur des listes de plus de 19975 éléments. L'avantage de cet inconvénient est que si la condition d'arrêt n'est jamais remplie, on n'entre pas dans une boucle sans fin comme avec (while ...) On peut faire le test avec (fact -1) D'autre part, l'exécution des fonctions récursives semble plus lente que celle des fonctions itératives et ce d'autant plus que le nombre d'interprétations (hauteur de la pile) est important. Bien évidemment, tous les eventuels ajouts, remarques, corrections, questions sont les bienvenus. [Edité le 6/8/2006 par (gile)]