• Aucun résultat trouvé

[PDF] Cours de base en LISP enjeux et pratique | Formation informatique

N/A
N/A
Protected

Academic year: 2021

Partager "[PDF] Cours de base en LISP enjeux et pratique | Formation informatique"

Copied!
19
0
0

Texte intégral

(1)

Université Abou Bakr Belkaïd – Tlemcen Ecole Doctorale STIC option SIC

Module : Intelligence Artificielle

Exposé

Le langage LISP

Présenté par

Amine Brikci-Nigassa

(nh2 (à) libretlemcen.org) Année universitaire 2006-2007

(2)

Avant-propos

Ce document est un exposé réalisé pour le module d'intelligence artificielle de la 1ère année de magistère dans le cadre de l'école doctorale STIC (option Systèmes d'Information et de Connaissances) à l'université Abou-Bakr Belkaïd de Tlemcen (Algérie).

Le texte est sous licence GNU FDL (GNU Free Documentation License) dans l'objectif d'en faire une base pouvant être améliorée selon la philosophie du Libre.

Ce document a été réalisé sous Debian GNU/Linux grâce au logiciel Libre OpenOffice.org librement téléchargeable sur le site http://fr.OpenOffice.org Il est disponible aux formats OpenDocument (norme ISO/IEC 26300) et PDF à l'adresse http://LibreTlemcen.org/nh2/expose_LISP

Pour toutes suggestions, remarques, réactions, il est possible de contacter l'auteur par courrier électronique :

Amine Brikci-Nigassa : nh2 (à) libretlemcen.org

Note de Licence :

Copyright © 2007 Amine Brikci-Nigassa

Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.2 publiée par la Free Software Foundation à l'adresse http://www.gnu.org/licenses/fdl.html.

(3)

Table des matières

Avant-propos...2 Introduction...4 Historique...5 Particularités de Lisp...7 Programmation fonctionnelle...7 Récursivité...7 Les listes...8

Nature d'un programme Lisp...10

La boucle read-eval-print...11

Le Garbage Collecting...11

Utilisation en Intelligence Artificielle...12

Eléments de syntaxe en Lisp...12

Atomes...12

Listes...13

Exemple de programme en Scheme...13

(4)

Introduction

Souvent associé au domaine de l'intelligence artificielle, le langage LISP

trouve un regain d'intérêt auprès de diverses communautés de programmeurs depuis quelques années.

Assez différent des langages impératifs actuellement communs, presque tous descendants d'ALGOL (Pascal, C ou encore Java), la beauté et la puissance de ses concepts élémentaires en font une expérience que devrait connaître tout bon programmeur.

C'est, après FORTRAN, le plus ancien des langages de haut niveau encore utilisés aujourd'hui. Ses applications sont depuis longtemps sorties du champ des laboratoires de recherche puisque ses capacités en font un langage aux applications multiples.

Sa simplicité est également remarquable. La facilité de sa syntaxe, le faible nombre de ses mots-clés, son interactivité sont des critères qui amènent certains à l'utiliser comme langage d'initiation à la programmation.

(5)

Historique

Pr John Mac Carthy, le père du « LISt Processing language »1 est aussi

l'inventeur du terme « Intelligence Artificielle ». En concevant ce langage, en 1958, le but de ce chercheur au MIT2 était d'appliquer les théories du

lambda-calcul du mathématicien Alonzo Church, ouvrant la voie au paradigme de la programmation fonctionnelle.

Lisp a introduit des concepts nouveaux comme la structure si-alors-sinon reprise dans la plupart des langages de haut niveau (tels que ALGOL et ses successeurs). Il influencera les concepteurs de Smalltalk (l'un des premiers langages orientés objet). C'est d'ailleurs avec Lisp qu'est apparu le premier « ramasse-miettes »3 connu des programmeurs Java.

La nature fonctionnelle du langage Lisp a longtemps posé l'inconvénient de la lenteur et de la forte consommation de mémoire (qui ont autrefois induit la nécessité de machines dédiées), c'est une des raisons pour lesquelles sa popularité n'a ressurgi qu'après plusieurs décennies.

Depuis sa conception, plusieurs dialectes de Lisp ont été développés (MACLISP, InterLisp...) mais les deux les plus employés actuellement sont Common Lisp et Scheme4, le premier étant riche et complexe et le deuxième

se voulant épuré (mais tout aussi puissant). Chacun a fait l'objet d'une

normalisation (respectivement par l'ANSI et l'IEEE) et leurs implémentations sont nombreuses, sous la forme d'interpréteurs et/ou compilateurs (la plupart Libres/Open Source et disponibles sur plusieurs plateformes).

Une autre variante également connue est Emacs Lisp qui est utilisée comme langage intégré à l'éditeur de texte GNU Emacs5 et qui permet entre autre

d'étendre ses fonctionnalités.

Notons aussi que Lisp a inspiré le langage LOGO, développé à la fin des

1 Les mauvaises langues prétendent que c'est plutôt l'acronyme de « Lots of

Irritating Superfluous Parentheses » ou encore « Lots of Idiot and Silly Parentheses »... 2 Massachussets Institute of Technology

3 Traduction française de « garbage collector », aussi appelé « glaneur de cellules » 4 Prononcer 'skim'

5 Emacs est le premier logiciel du projet de système d'exploitation libre GNU réalisé par la Free Software Foundation. Il est entre autre disponible avec les distributions GNU/Linux

(6)

années 60, très utilisé pour l'apprentissage de la programmation aux enfants (en manipulant une tortue qui dessine des formes géométriques à l'écran).

(7)

Particularités de Lisp

Programmation fonctionnelle

En inventant le lambda calcul, Church inspira à Mc Carthy un nouveau

paradigme de programmation, pour lequel toutes les entités sont considérées comme des fonctions.

Contrairement à la programmation impérative (FORTRAN, Pascal ou C par exemple), les valeurs manipulées ne sont pas stockées dans des variables dont l'état change au cours du programme. En programmation fonctionnelle, les valeurs sont maniées d'une fonction à l'autre en tant que paramètres pour les unes et que valeurs de retour pour les autres.

La modularité est de mise puisqu'un programme n'est en fait qu'une collection de fonctions conçues séparément (qui complètent les fonctions déjà fournies par le langage).

Lisp est un pionnier de la catégorie, mais ce n'est pas un langage fonctionnel pur. Il est de nature multi-paradigme, ce qui lui permet d'y inclure les

concepts de la programmation impérative. Le dialecte Scheme constitue (dans une certaine mesure) une tentative de simplification et d'amélioration du langage.

Récursivité

Toute la puissance de Lisp ne pourrait être obtenue sans l'emploi de fonctions récursives.

La récursivité n'est pas l'apanage de ce langage mais sa nature fonctionnelle fait qu'elle est beaucoup plus employée, ne serait-ce que pour implémenter un simple boucle. En effet, bien que tous les dialectes prévoient des structures de boucles, il est plus élégant d'employer une fonction récursive (même si cela aboutit au même résultat).

Il faut cependant faire attention à n'utiliser que des récursions terminales, i.e. des fonctions où l'appel récursif est à la fin de leur définition. C'est le seul

(8)

moyen d'éviter les débordements de pile qui se produisent quand la fonction doit sauvegarder ses résultats intermédiaires avant l'appel. La récursion terminale fait partie de la norme de Scheme.

Les listes

Comme son nom l'indique, la principale structure de données que Lisp manipule est la liste.

Dans sa première implémentation historique, Lisp utilisait deux registres de l'IBM 7046 qui étaient accessible par les macros assembleur car (contents of

address register) et cdr (contents of decrement register) pour manipuler ses données.

Une liste est donc une chaîne constituée par une série de doublets de pointeurs, chaque paire de pointeurs contient l'adresse d'un élément de la liste (le car) et celle de la paire suivante (le cdr), qui à son tour pointe sur l'élément suivant et sur la paire qui le suit... Le car désigne donc la tête de la liste et le cdr la liste constituée par les éléments suivants. Le dernier cdr de la liste contient une adresse particulière qui par convention désigne une liste vide (notée NIL ou () selon les dialectes).

La représentation en diagramme « boîtes-pointeurs » permet de visualiser les paires sous formes de boîtes avec, à gauche, les car qui pointent sur les

éléments de la liste et, à droite, les cdr qui pointent sur la boîte suivante. La barre oblique dans le dernier cdr représente l'adresse « NIL ».

Une liste est notée en Lisp en entourant les éléments par des parenthèses et en les séparant par des espaces. La liste ci-dessus se note donc (1 2 3). Elle est constituée de trois atomes. Les atomes peuvent être des nombres ou des symboles ; ils constituent avec les listes les objets de base du langage.

6 L'IBM 704 était un ordinateur à lampes, qui ne connaissait pas encore la technologie des transistors...

2 3

(9)

Néanmoins, la liste n'est pas la seule structure de donnée existant en Lisp. En effet, les paires (aussi appelées conses ou cellules cons) qui constituent les listes peuvent être agencées de plusieurs autres manières, pour construire des arbres ou toute autre structure complexe. Voici un exemple d'arbre simple noté (1 (2/7 3.14) A "foo") :

Cet exemple est en fait un cas particulier de liste à 4 éléments dont le 1er le

3ème et le 4ème sont des atomes mais où le deuxième élément est lui-même une

liste de 2 éléments atomiques.

Il est possible de créer des structures qui ne sont pas des listes, la plus simple étant la paire :

Cette paire d'éléments est notée (1 . 2), elle n'est pas considérée par Lisp comme une liste, puisque le cdr ne référence pas une liste.

Les expressions arithmétiques et les appels de fonctions sont construites sous forme de listes grâce à la notation préfixée parenthésée. En effet, l'expression

ex 1

x s'écrira (+ (exp x) (/ 1 (sqrt x))) et correspond à la liste arborescente ci-dessous. A "foo" 1 3.14 2/7 1 2

(10)

Notons au passage qu'il est inutile de donner des priorités aux opérateurs (source d'ennuis dans les langages classiques).

Nature d'un programme Lisp

Nous remarquons ci-dessus que l'appel d'une fonction se fait dans une liste dont le premier élément est la fonction elle-même et les suivants sont les paramètres :

(fonct param1 param2 ...)

et que les opérateurs arithmétiques sont considérés comme des fonctions : (+ 2 3) n'est autre qu'un appel à la fonction d'addition, notée +, avec les paramètres 2 et 3. Le résultat (la valeur renvoyée par la fonction) se

substituera à l'expression entière dans l'expression englobante.

Les programmes Lisp sont des fonctions, donc des listes, et leur exécution revient donc à l'évaluation de ces fonctions. Le programme source étant lui même une structure de donnée manipulable, il apparaît facile de faire de la métaprogrammation en Lisp (pour étendre la syntaxe du langage par

exemple). / 1 + x exp x sqrt

(11)

La boucle read-eval-print

L'exécution d'un programme Lisp passe par trois étapes dites « boucle read-eval-print »7.

read : Le programme source, qui est une liste parenthésée (une

S-expression), est transformé en une structure de liste. Si un compilateur est utilisé, cette transformation peut se faire en langage machine ou en bytecode.

eval : C'est l'exécution proprement dite. La structure de liste est évaluée,

retournant une autre structure comme résultat. Pour évaluer une fonction (qui est une liste, rappelons le), eval commence par évaluer chacun des arguments du cdr puis leur applique la fonction désignée par le car. L'évaluation d'un argument peut évidemment nécessiter d'appliquer les fonctions qui s'y trouvent.

print : L'affichage du résultat peut être simple, s'il s'agit par exemple d'un

simple atome. Si le résultat est une liste il nécessitera de parcourir la liste pour pouvoir l'afficher.

Le Garbage Collecting

Les structures de données avancées sont bien utiles mais elles ont

l'inconvénient d'occuper de la mémoire. La conception de Lisp nécessitait un mécanisme permettant de libérer la mémoire des éléments inutilisés. C'est ainsi que Mc Carthy pensa au Garbage Collector (« Glaneur de Cellules » ou « ramasse-miettes » en français). Il s'agit d'un processus qui se déclenche automatiquement pour supprimer de la mémoire toutes les informations qui ne sont plus référencées dans le programme à un instant donné.

Il permet de libérer le programmeur de la tâche fastidieuse de

« désallocation » qui est source d'erreurs et de problèmes dans de nombreux langages modernes (comme le C++). Cette idée novatrice fut reprise maintes fois, Sun l'a utilisée pour le langage Java par exemple.

(12)

Utilisation en Intelligence Artificielle

De par sa nature fonctionnelle et parce qu'il est adapté à la manipulation des listes et des arbres, Lisp est un langage de prédilection pour la plupart des domaines de l'Intelligence Artificielle, où la programmation exploratoire joue un rôle prépondérant. Lisp est utile quand il s'agit d'explorer une

arborescence. Il est particulièrement aisé de programmer des analyseurs syntaxiques (l'analyseur syntaxique du premier compilateur Lisp fut écrit en Lisp), des systèmes experts ; les réseaux de neurones et autres algorithmes génétiques sont souvent réalisés en Lisp, ainsi que les agents intelligents et les systèmes de gestion de connaissances.

Cependant, Lisp s'est avéré adapté à bien d'autres domaines dont on pourrait citer : l'animation et les graphismes, la bioinformatique, le e-commerce, le data mining, les applications pour EDA/Semiconducteurs, les finances, la CAO mécanique, la modélisation et la simulation, le langage naturel, l'optimisation, l'analyse des risques, l'ordonnancement, les télécommunications, et le Web Authoring...

Eléments de syntaxe en Lisp

Le dialecte de Lisp que nous utiliserons à titre d'illustration est Scheme. Les différences avec les autres dialectes (Common Lisp notamment) sont minimes, surtout quand il s'agit des notions de base.

Atomes

Les atomes sont des suites de caractères alphabétiques et/ou numériques, seuls certains caractères spéciaux ne peuvent être utilisés.

Exemples : 3.14, x, tlm13, 5doigts, 72+

Les atomes ayant une valeur numérique sont des nombres, les autres sont des symboles.

Les symboles peuvent servir d'identificateurs (mais ils peuvent être manipulés sans avoir de signification particulière).

(13)

majuscules. Tlemcen et TLeMcen désignent donc le même symbole.

Les constantes booléennes vrai et faux sont notées #t et #f en Scheme. Les chaînes de caractères sont entourées de guillemets (ex: "Salut!") mais les caractères sont notés d'une manière particulière – exemple : #\a (qui est différent de la chaîne "a").

Listes

Une liste est une suite d'atomes et/ou de listes séparés par des espaces et entourés par des parenthèses.

Exemples : (1 2 3 4), (1 (21 22) (31 (311 312)) 4 (51 52)), (x y z t), (+ (exp x) (/ 1 (sqrt x)))

Un cas particulier est celui de la liste vide () notée aussi NIL en Common Lisp (mais pas en Scheme).

Exemple de programme en Scheme

Voici un exemple de programme simple8 afin d'illustrer les principes de la

syntaxe de Scheme. Il s'agit du jeu « Morpion » (en anglais Tic-tac-toe). Le programme doit arbitrer une partie entre le joueur O et le joueur X, en désignant le vainqueur dès qu'il aligne trois symboles. Le jeu est nul si la grille est remplie sans qu'il y ait de gagnant.

Pour éviter de compliquer le programme, celui-ci ne comporte ni vérification pour tester la case jouée, ni « mode IA » (le programme ne sert que d'arbitre entre 2 joueurs humains).

(14)

(define (morpion) (boucle '((- - -) (- - -) (- - -)) 'X 'O)) (define (boucle matrice joueur1 joueur2)

(print "position pour le " joueur1 " ?") (let ((ligne (saisie "ligne: ")))

(if (eqv? ligne 'q) 'abandon

(let* ((colonne (saisie "colonne: "))

(new-matrice (nouveau matrice ligne colonne joueur1))) (newline)

(affiche-matrice new-matrice) (newline)

(cond ((gagne? new-matrice) (display "le gagnant est : ") joueur1) ((partie-nulle? new-matrice) (display "partie nulle !!! ") 'Fin) (else (boucle new-matrice joueur2 joueur1)))))))

(define (saisie message) (display message) (read)) (define (gagne? m)

(or (identiques? (car (car m)) (cadr (car m)) (caddr (car m))) (identiques? (car (cadr m)) (cadr (cadr m)) (caddr (cadr m))) (identiques? (car (caddr m)) (cadr (caddr m)) (caddr (caddr m))) (identiques? (car (car m)) (car (cadr m)) (car (caddr m)))

(identiques? (cadr (car m)) (cadr (cadr m)) (cadr (caddr m))) (identiques? (caddr (car m)) (caddr (cadr m)) (caddr (caddr m))) (identiques? (car (car m)) (cadr (cadr m)) (caddr (caddr m))) (identiques? (car (caddr m)) (cadr (cadr m)) (caddr (car m))))) (define (identiques? a b c)

(and (equal? a b) (equal? b c) (not (equal? A '-)))) (define (partie-nulle? m)

(and (not (member '- (car m))) (not (member '- (cadr m))) (not (member '- (caddr m))))) (define (nouveau m i j joueur)

(new-ligne m i (new-ligne (nieme m i) j joueur))) (define (new-ligne l i val)

(cond ((null? l) '())

((= i 1) (cons val (cdr l)))

(else (cons (car l) (new-ligne (cdr l) (- i 1) val)))))

(define (nieme l i)

(if (= i 1) (car l) (nieme (cdr l) (- i 1)))) (define (affiche-matrice m)

(print " " (caar m) " | " (cadar m) " | " (caddar m)) (print "---+---+---")

(print " " (car (cadr m)) " | " (cadr (cadr m)) " | " (caddr (cadr m))) (print "---+---+---")

(15)

Le programme est constitué de 10 fonctions définies grâce au mot-clé define. La structure de donnée utilisée ici est une matrice constituée d'une liste de 3 listes à 3 éléments atomiques. Ces éléments sont initialisés au symbole « – ». Ils seront remplacés au cours de la partie par des « O » et des « X ».

La fonction principale est morpion et n'a pas d'arguments. Elle ne fait

qu'appeler la fonction boucle avec pour arguments une « matrice vide » et les symboles représentant les 2 joueurs. On remarquera le caractère « ' »

(apostrophe, ou quote en anglais): il indique que l'élément qui le suit ne doit pas être évalué mais pris tel quel (l'évaluation de la liste matrice par exemple n'aurait pas de sens et retournerait une erreur puisque son premier élément n'est pas une fonction).

Nous avons utilisé l'interpréteur Scheme « Bigloo » pour Unix pour cet exemple. Le fichier du programme écrit avec un éditeur de texte dans le fichier « morpion.scm » est chargé par la fonction load de Bigloo.

Pour exécuter le programme, il suffit d'appeler la fonction (morpion) à l'invite de l'interpréteur (1:=>).

(16)

nh2@debian:~$ bigloo

---Bigloo (2.6e) ,--^, `a practical Scheme compiler' _ ___/ /|/ Tue Oct 19 12:13:41 CEST 2004 ,;'( )__, ) ' Inria -- Sophia Antipolis ;; // L__. email: bigloo@sophia.inria.fr ' \ / ' url: http://www.inria.fr/mimosa/fp/Bigloo ^ ^

---Welcome to the interpreter

1:=> (load "morpion.scm") morpion boucle saisie gagne? identiques? partie-nulle? nouveau new-ligne nieme affiche-matrice morpion.scm 1:=> (morpion) position pour le X ? ligne: 2 colonne: 2 | | - | X | - | | -position pour le O ? ligne: 1 colonne: 1 O | | - | X | - | |

(17)

-Les autres fonctions peuvent être appelées individuellement :

1:=> (gagne? ((O O O) (X - X) (- - -))) *** ERROR:bigloo:eval:

Unbound variable -- O #unspecified

L'oubli du quote entraîne l'évaluation de la matrice et donc une erreur de Bigloo.

1:=> (gagne? '((O - X) (- O X) (X X O)) ) #t

La fonction gagne? retourne une valeur booléenne.

1:=> (affiche-matrice '((O - X) (- O X) (X X O)) ) O | - | X - | O | X X | X | O O

La fonction affiche-matrice utilise la fonction print de Bigloo (qui n'existe pas dans le standard de Scheme) pour afficher la matrice donnée en

paramètre. Elle retourne le dernier symbole affiché (ici « O ») mais cette valeur n'est pas utilisée dans le programme.

1:=> (nouveau '((O - X) (- O X) (X X -)) 3 3 'O) ((O - X) (- O X) (X X O))

La fonction nouveau permet de générer une nouvelle matrice à partir d'une autre, en remplaçant un symbole par un autre (ici « O » remplace « – » à la 3ème ligne, 3ème colonne).

1:=> (nieme '((O - X) (- O X) (X X -)) 2) (- O X)

nieme est une fonction récursive simple qui, appelée avec pour arguments une liste-matrice m et un indice n, retourne le nième élément de m.

(18)

Conclusion

Pour conclure cet exposé, on citera le programmeur Eric Raymond :

Lisp is worth learning for the profound enlightenment experience you will have when you finally get it; that

experience will make you a better programmer for the rest of your days, even if you never actually use Lisp itself a lot.

Eric S. Raymond, "How to Become a Hacker"

(Lisp vaut la peine d'être appris pour l'expérience profondément enrichissante que l'on obtient quand on finit par l'assimiler. Cette expérience là fera de vous un meilleur programmeur pour le restant de vos jours, même si vous n'utilisez jamais vraiment Lisp lui-même énormément.)

(19)

Références bibliographiques

● J.P. Barthès « Intelligence artificielle » Supports de cours au

Département de Génie Informatique, Université de Technologie de Compiègne, 1998. http://www.utc.fr/_barthes/IA01

● Laurent Bloch « Initiation à la programmation avec Scheme » Ed.

Technip, Paris, 2001.

● Louis Gacôgne « 512 problèmes corrigés Pascal, C++, Lisp, Prolog » Ed.

Ellipses, 1996.

● Denis Howe « Free On-line Dictionary of Computing » 1993.

http://www.foldoc.org

● Jean-François Perrot « Introduction à l'intelligence artificielle » Notes de

cours au Laboratoire d'informatique de Paris-6, Université Pierre et Marie Curie, 2004. http://www-poleia.lip6.fr/_jfp/insia

● Eric S. Raymond « The on-line hacker Jargon File » 2005.

Références

Documents relatifs

multiplier test statistic for causality in variance between two univariate time series using multivariate GARCH models is studied in Hafner and Herwartz (2006) and these authors

Dans un premier temps, l’usage général de la notion de résilience est présenté puis un ensemble de modèles de dynamique de systèmes en situation de crise sont étudiés

As a conclusion on the importance of inter- laminar fracture toughness on the impact behavior, two distinct modes of failure were reported in the literature: a cleavage

Les découvertes de la Tène sur le plateau du Braden se situent dans un c ontexte chronologique plus vaste mais représen~ nt l'aspect essentiel des. gisements

The objective of this paper is to offer an insight as to why many previous studies are not directly applicable when considering class imbalance, data heterogeneity and data vo- lume

After six years of practising professional interest inventory tests in a career preparation program, implemented by a public French higher engineering

Enfin dans trois cas, le vase principal était accompagné d'un petit vase acces- soire situé à l'intérieur. Un mobilier très important, céramique, métallique,

Luc Bourgeois, « Claustrum versus castrum : réalités et ambiguïtés des enclos monastiques », Bulletin du centre d’études médiévales d’Auxerre | BUCEMA [En ligne], Hors-série