DRLINIT.WS4 (="Dr. Logo Initiation") ----------- - "Initiation au Logo" par Daniel MARTIN "Amstrad Magazine", Mars-Avril-Mai 1986 (Retapé par Emmanuel ROCHE.) Première partie: Amstrad Magazine, No.8, Mars 1986, p.26 ---------------- Nous allons consacrer trois cours à l'étude du langage Logo. Pourquoi étudier le langage Logo? Les raisons sont multiples. Tout d'abord, c'est le langage le plus répandu sur Amstrad après le BASIC, puisqu'il est offert en même temps que l'AMSDOS et le CP/M à tout acquéreur d'une extension disque, d'un CPC-664 ou d'un 6128. Ensuite, le langage Logo est aussi riche et aussi performant que le BASIC. Cependant, il nécessite un raisonnement fondamentalement différent de celui adopté pour le BASIC. Le but de cette série d'articles sera donc d'aider le lecteur à "faire ses premiers pas" en Logo et d'en découvrir les infinies possibilités. Enfin, le Logo est en passe de devenir aussi important dans le monde de la micro-informatique que ne l'était le BASIC en 1979. Amstrad a très vite compris que l'utilisation d'une machine dans un but pédagogique est devenue totalement dépendante de l'existence du Logo dans la configuration proposée. C'est pourquoi il fournit le Logo à tout acquéreur d'un lecteur de disque. Quel Logo? ---------- Le Logo proposé par Amstrad est un des meilleurs Logo du marché. Il a été écrit par les concepteurs du CP/M, autrement dit Digital Research (D.R.). Il est fourni sous deux versions, la version 1 destinée aux possesseurs de CPC- 464 et CPC-664, et la version 2 destinée aux possesseurs de CPC-6128 et PCW- 8256. La version 1 est complètement compatible, de façon ascendante, avec la version 2. Autrement dit, la version 2 comprend tous les mots-clés de la version 1, et y apporte quelques compléments. Si vous possédez un CPC-464 ou CPC-664, ne désespérez pas. Vous découvrirez au cours de ces articles que la plupart des nouvelles fonctions apportées par la version 2 sont relativement faciles à émuler en version 1. Pour clôturer le point de vue de la compatibilité, vous devez savoir que le Logo de l'Atari 520ST (Jackintosh) est la version 2 du Dr. Logo légèrement adaptée aux caractéristiques de cette machine. La plupart des programmes écrits sur Atari fonctionnent donc sur Amstrad, et vice-versa. Le seul reproche que l'on puisse faire au Dr. Logo est d'être anglo-saxon. En effet, le Logo est un langage "à mettre entre toutes les mains", et surtout celles des enfants. La nécessité d'employer des mots anglais est donc un sérieux handicap. Cependant, il existe déjà en France une version francisée du Dr. Logo pour Amstrad, et il n'est pas difficile, comme vous le découvrirez, de le transformer vous-même en Français. Un peu d'histoire et de considérations -------------------------------------- Le Logo n'est pas un nouveau langage. Il existe depuis longtemps. Il est né de la collaboration fructueuse entre un informaticien-mathématicien de génie, Seymour PAPERT, considéré comme l'un des précurseurs de l'intelligence artificielle, et un psycho-pédagogue de renom, le Suisse PIAGET. Quant à la version de Digital Research, elle n'existe que depuis deux ans. Le Logo est considéré comme un langage destiné aux enfants, et ne jouit pas, pour cette raison, de l'estime des professionnels de l'informatique. Cette réputation est due à l'existence des célèbres graphiques "Tortue", qui permettent de dessiner, et donc de visualiser directement les premiers concepts de programmation. Cette caractéristique, dont les qualités pédagogiques sont indiscutables, n'est qu'une toute petite partie du langage, qui se révèle, à l'usage, bien plus riche et bien plus complexe que le BASIC. Dans ce premier article, nous nous limiterons à l'utilisation des graphiques ("Turtle Graphics"), et à la description des fonctions de base du Logo. Dans le numéro suivant, nous aborderons l'étude des caractéristiques plus complexes du Logo (gestion des listes et des propriétés). En effet, elles constituent la vraie force de ce langage, et permettent ainsi de le rapprocher du langage PROLOG et des langages de la cinquième génération. Premières notions de Logo ------------------------- Chargement ---------- Le chargement du Logo se fait sous CP/M. Il est donc nécessaire d'appeler le CP/M au moyen de la commande |CPM. Après l'initialisation du système d'exploitation, il est nécessaire de reconfigurer le clavier, afin d'assurer le fonctionnement convenable des flèches de déplacement du curseur, qui sont indispensables en mode édition. Cette fonction est réalisée par la commande DRLKEYS. Ensuite, le lancement du langage proprement dit est réalisée par la commande LOGO, LOGO2, ou LOGO3, suivant les versions. Remarque: une procédure SUBMIT existe, elle est identifiée par l'extension SUB dans le répertoire de la disquette. Elle se lance par la commande SUBMIT LOGO2 ou SUBMIT LOGO3. Elle se charge d'exécuter la commande DRLKEYS et la commande LOGO. A l'issue de la commande de chargement, un message de bienvenue, suivi d'un point d'interrogation ("?"), apparaît. Vous êtes dès lors en contact direct avec l'interpréteur Logo, et prêt à réaliser vos premiers essais. La première instruction en Logo ------------------------------- L'instruction la plus simple et la plus utile est certainement celle qui vous permet de revenir au CP/M. Pour cela, il vous suffit de taper à la suite du point d'interrogation les trois lettres BYE ("au revoir", en anglais). Remarque: vous pouvez taper indifféremment cet ordre en majuscules ou en minuscules. Pour des raisons d'uniformité, nous vous invitons à utiliser les minuscules pour les ordres donnés au Logo. L'instruction BYE est donc comprise par le Logo. Cette instruction est appelée une primitive du langage. Une primitive est donc un ordre compris par le Logo dans sa version de base (avant tout chargement complémentaire). Découvrons la Tortue -------------------- Nous allons donc découvrir la fameuse tortue qui a fait couler beaucoup d'encre. Par la même occasion, découvrons une nouvelle primitive du langage: st, abbréviation anglaise de "Show Turtle" (montrer tortue). Remarque: l'ordre st doit absolument être orthographié en minuscules. Autrement, un message d'erreur: "I don't know how to ST" (qui signifie littéralement: "Je ne sais pas comment faire ST") est affiché sur l'écran. Si ce n'est déjà fait, rechargez le langage Logo, et tapez st. Un petit triangle plus ou moins équilatéral présentant une base horizontale apparaît au centre de l'écran. C'est la Tortue. Elle est, en effet, très stylisée. Sa pointe, orientée vers le haut de l'écran, indique la direction qu'elle suivra lorsqu'on la fera avancer. Trois modes écran sont possibles en Dr. Logo -------------------------------------------- 1) Le mode texte complet sans tortue, qui s'obtient en tapant la primitive ts ("Text Screen", ou écran texte). 2) Le mode mixte, où le haut de l'écran est occupé par la tortue et les cinq lignes du bas par le mode texte. Ce mode s'obtient à l'aide de la primitive ss ("Split Screen", ou écran coupé). 3) Le mode graphique seul, obtenu à l'aide de la primitive fs ("Full Screen", ou écran complet). Remarque: en mode fs, le texte n'apparaît plus à l'écran. Soyez donc attentif à ce que vous encodez dans ce mode. Déplaçons la tortue ------------------- Une série de primitives permettent de déplacer la tortue. On distingue essentiellement: les primitives de rotation rt et lt ("RighT" et "LefT", pour droite et gauche), et les primitives de déplacement fd et bk ("ForwarD" et "BacK", pour avant et arrière). Contrairement aux premières primitives étudiées, ces quatre primitives (rt, lt, fd et bk) demandent un argument. Pour rt et lt, l'argument est l'angle de rotation exprimé en degrés. Pour fd et bk, l'argument est le nombre de points du déplacement à effectuer. L'utilisation d'une de ces primitives sans argument produira le message d'erreur: "Not enough inputs to...", qui signifie "Il n'y a pas assez d'entrées (arguments) pour...". Essayez donc les instructions suivantes: rt 30 : la tortue tourne de 30 degrés à droite. rt 30 : la tortue tourne encore de 30 degrés. lt 60 : la tortue tourne de 60 degrés à gauche, et reprend sa position de départ. fd 50 : la tortue avance de 50 points dans le sens de sa pointe, et la 'trace' de la tortue est visible sous la forme d'un trait. bk 30 : la tortue recule de 30 points dans le sens inverse de sa pointe, et repasse sur ses traces sans effacer. A l'aide de ces quatre primitives, vous êtes capables de tracer n'importe quel polygone convexe ou concave. Remarques: l'argument doit être séparé de la primitive par au moins un espace. Plusieurs primitives peuvent se succéder sur une même ligne de commande. Le séparateur de primitives est aussi l'espace. Théorème du polygone convexe: pour tracer un polygone convexe, il faut que la somme des angles de rotation soit égale à 360°. Corollaire: pour tracer un polygone régulier convexe à n côtés, il suffit de prendre comme angle de rotation 360 divisé par n. Exemples: traçage d'un triangle équilatéral: rt 120 fd 100 rt 120 fd 100 rt 120 fd 100. traçage d'un hexagone régulier: rt 60 fd 50 rt 60 fd 50 rt 60 fd 50 rt 60 fd 50 rt 60 fd 50 rt 60 fd 50. Voici une dernière primitive très utile pour terminer cette section. La primitive cs ("Clear Screen", ou efface écran) équivaut à l'instruction BASIC CLS, et efface la totalité de l'écran en le laissant dans le modegraphique courant (ss ou fs). Le mode texte (ts) passe en mode ts après une commande cs. La primitive de répétition -------------------------- La primitive repeat ("répète") est suffisamment importante pour qu'on lui consacre une section propre. Vous avez remarqué pendant la programmation de l'hexagone de l'exemple ci-dessus que l'utilisation des primitives graphiques peut vite se révéler harassante lors du traçage de figures complexes. Si une répétition systématique d'une ou d'un ensemble de primitives est nécessaire, il est beaucoup plus simple d'utiliser l'instruction repeat. Syntaxe: repeat n [primitive primitive ...] - n est un numérique qui représente le nombre de répétitions. - Les crochets font partie de l'instruction, et permettent de délimiter le corps de la répétition. La programmation de l'hexagone de l'exemple précédant peut dès lors s'écrire: repeat 6 [rt 60 fd 50] Remarquez la concision de l'écriture, et l'avantage de l'instruction lors du traçage d'un cercle, par exemple. repeat 360 [rt 1 fd 1] L'analogie entre la commande repeat et le couple d'instructions BASIC FOR-- NEXT est évidente. Le crochet ouvert '[' joue le rôle du FOR, et le crochet fermé ']' le rôle du NEXT. Les boucles repeat sont cependant moins souples que les boucles FOR--NEXT. La variation du pas d'itération est toujours unitaire dans le cas du repeat (l'instruction STEP n'a pas d'équivalent). Théorème de l'étoile -------------------- Pour tracer une étoile régulière à n branches, il suffit de diviser 360 par n, et de prendre comme angle de rotation le premier multiple de n qui ne soit pas diviseur entier de 360. Si, arrivé à un nombre supérieur à 180, on n'a pas découvert de nombre qui ne soit pas diviseur entier de 360, le problème n'a pas de solution simple. Examples: A) Etoile à cinq branches: n = 5 alors: 360 / 5 = 72. Premier multiple de 72 = 144. 144 n'est pas diviseur entier de 360, d'où angle de rotation = 144. repeat 5 [fd 100 rt 144] B) Etoile à six branches: n = 6 alors 360 / 6 = 60. Multiples: 120, 180, 240. Il n'y a pas de solution simple. En vérité, une étoile régulière à six branches est constituée de deux triangles têtes-bêches. Les procédures -------------- Avec les procédures, nous pénétrons au coeur du Logo. En effet, l'aspect procéduriel du langage constitue certainement sa caractéristique la plus importante. Une procédure se compose d'une suite d'instructions mémorisées sous un nom bien précis et choisi par l'utilisateur. La procédure ainsi déterminée devient une nouvelle commande du langage dont l'utilisation ne se distingue en rien de celle des primitives. La procédure est appelée uniquement par son nom et sa liste d'arguments éventuels. Et les programmes en Logo, me direz-vous: Comment les écrit-on? Comment numérote-t-on les lignes? La réponse est simple. En Logo, il n'y a ni numéro de ligne, ni programme. Une application se constitue simplement au moyen d'un ensemble de procédures s'enchaînant les unes aux autres. Définition d'une procédure -------------------------- La définition d'une procédure est réalisée par l'utilisation de la primitive to suivie d'un mot de votre choix. Pour des raisons évidentes, vous ne devez pas choisir un nom de primitive comme nom de procédure. A la suite de l'instruction de définition, le signe de sollicitation change. Le point d'interrogation se transforme en '>' qui signifie en quelque sorte "vous êtes en train de définir une procédure". Il vous reste à encoder les instructions que doit réaliser votre procédure, et à terminer le tout par une instruction end qui indique au langage que vous avez terminé la définition de la procédure. A l'issue de la définition, un message du style 'machin defined' apparaît. Example: définissons une procédure permettant le traçage d'un hexagone. ?to hexagone >repeat 6 [rt 60 fd 50] >end hexagone defined Remarque: les signes ? et > ne doivent pas être encodés. Le lancement de la procédure se fait simplement en tapant "hexagone". Paramètrisation d'une procédure ------------------------------- La procédure hexagone définie ci-dessus est intéressante, mais manque de souplesse. En effet, la taille d'un côté de l'hexagone est fixée à l'intérieur de la procédure. Il serait pour le moins intéressant que la taille du côté de l'hexagone constitue un paramètre de la procédure. Cette fonction est simple à réaliser. Il suffit, lors de sa définition par la primitive to, de faire suivre le nom de la procédure du nom du paramètre à utiliser. Ce nom doit absolument être précédé du signe ':' et aucun espace ne peut figurer entre le signe ':' et le nom du paramètre. Ce paramètre peut alors être utilisé au sein de la procédure. Exemple: ?to hexa :cote >repeat 6 [rt 60 fd :cote] >end La procédure se lancera par une commande du style de: hexa 80. Si la commande est utilisée sans argument, un message d'erreur "Not enough inputs to hexa in hexa :cote" apparait à l'écran. Un paramètre dans une procédure est donc identifié par le langage à l'aide du signe ':' qui le précède. Il est évident qu'une même procédure peut utiliser plusieurs paramètres. Exemple: traçage d'un rectangle avec deux paramètres, le petit côté et le grand côté. ?to rectangle :petit :grand >repeat 2 [fd :petit rt 90 fd :grand rt 90] >end L'appel se faisant par une commande du style de: rectangle 60 100. Remarques: 1) Le signe '!' qui apparaît en fin d'écran indique simplement que la commande continue à la ligne suivante. 2) Le système ne reconnaît évidemment pas les sens des mots petit et grand. Ainsi, rectangle 100 60 fonctionnera parfaitement avec un petit côté de 100 et un grand de 60. La récurrence ------------- La définition de procédures en Logo est récurrente. Autrement dit, une procédure peut faire appel à elle-même, et ainsi contenir sa propre définition. Exemple: la procédure hexamul produit une infinité d'hexagones imbriqués, car elle se répète en faisant appel à elle-même. ?to hexamul >repeat 6 [fd 50 rt 60] >fd 25 >hexamul >end Lancez la procédure en tapant hexamul. Cette procédure ne s'arrête jamais. Pour en sortir, enfoncez la touche ESC. En effet, l'appui sur la touche 'ESC' vous permet de quitter de façon violente une procédure en cours ou une boucle infinie en cours d'exécution. L'éditeur --------- Si vous avez commis une erreur lors de l'écriture d'une procédure, il n'est pas nécessaire de la réécrire entièrement. Elle peut être corrigée au moyen de l'éditeur. L'appel d'édition se fait par la primitive ed suivie du nom de la procédure précédé du signe guillement ("). Ce signe est indispensable pour spécifier que c'est le contenu du mot qui suit qui doit être édité. En l'absence du signe ", le Logo comprendra que vous voulez éditer quelque chose et exécuter votre procédure ensuite. Exemple: ed "hexagone Remarque: en cas d'erreur système, l'utilisation de la primitive ed seule vous permettra d'éditer la procédure en erreur. Après l'appel de l'éditeur, l'écran s'efface et le texte complet de la procédure apparaît à l'écran. Dès lors, vous pouvez utiliser les quatre flèches pour vous déplacer dans le texte, les touches 'DEL' et 'CLR' pour effacer des caractères, et la touche 'COPY' pour sortir de l'éditeur en sauvegardant les modifications. L'insertion de texte se fait de manière simple en tapant simplement le texte à l'endroit choisi, le reste du texte se déplaçant automatiquement. L'appui sur 'ESC' permet de sortir de l'éditeur sans sauvegarder les modifications. Remarque importante: une suite de caractères précédée du signe " s'appelle un mot. C'est une nouvelle classe d'objets pour le Logo. Nous connaissons actuellement trois classes d'objets: 1) Les primitives et les procédures, qui sont des mots normaux. 2) Les paramètres, qui sont précédés du signe ':'. 3) Les mots, qui sont précédés du signe ". Le Logo calcule --------------- Le propre de tout langage est de pouvoir effectuer des opérations mathématiques plus ou moins complexes. Le Logo n'est pas le langage le plus riche en fonctions mathématiques, il possède seulement quelques primitives de base. Malheureusement, l'absence de fonctions comme les exponentielles et les logarithmes, ou même l'extraction de racines carrées, se fait cruellement sentir. Les opérations classiques + - * / --------------------------------- Les quatre opérations fondamentales utilisent des conventions similaires à celles du BASIC. La priorité des opérations est identique (le * et le / sont exécutés en premier). Une opération peut s'écrire en mode arithmétique normal (exemple: 12 * 13), ou en notation préfixée (exemple: * 12 13). L'utilisation des parenthéses avec les mêmes règles qu'en BASIC est admise. Exemple: (3 * 4) + (5 * 6) fournira 42, et 3 * (4 + 5) * 6 fournira 162. Attention en notation préfixée: + * 3 4 * 5 6 équivaut à + ( (* 3 4) * 5) 6 c'est-à-dire à + 60 6, et donc à 66. * 3 4 + * 5 6 équivaut à * 3 (4 + (* 5 6) ) c'est-à-dire à * 3 34, et donc à 102. Les fonctions mathématiques --------------------------- En version 1, ces fonctions sont peu nombreuses, il n'y a que les fonctions sin (sinus), cos (cosinus), int (integer) et random (générateur aléatoire). Ces quatre fonctions nécessitent un et un seul argument. En version 2, la fonction arctan (arc tangente) s'ajoute aux précédentes. Remarque: soyez prudent lors de l'utilisation combinée des fonctions arithmétiques et trigonométriques. Entourez chacune des fonctions trigonométriques d'une parenthèse pour éviter tout problème de précédence d'opérateur. Exemple: tout le monde sait que la somme des carrés des sinus et cosinus d'un angle est égale à 1. Essayez: sin 45 * sin 45 + cos 45 * cos 45. Le résultat est étonnant, n'est-ce-pas? En vérité, vous avez écrit: sin (45 * sin 45) + cos (45 * cos 45). Pour obtenir un résultat correct, il convient donc d'utiliser: (sin 45) * (sin 45) + (cos 45) * (cos 45). Les formules sont bien sûr utilisables dans les procédures. Exemple: procédure d'élévation d'un nombre au cube: ?to cube :nombre >pr :nombre * :nombre * :nombre >end Cet exemple nous a permis de découvrir une nouvelle primitive. pr (print) imprime sur écran le contenu d'une fonction ou d'un objet. Exercice: arrivé à ce point, vous devez être capable d'écrire une procédure qui trace un polygone convexe régulier d'un nombre de côtés quelconque avec une longueur quelconque. Appelez cette procédure polygone, et utilisez les paramètres :nombre et :côtés. Rappel: la somme des rotations d'un polygone convexe régulier vaut 360 degrés. Solution en fin d'article. Les affectations de variables ----------------------------- Le titre n'est peut-être pas bien choisi, mais il se rapproche de ce que vous connaissez en BASIC lorsque vous écrivez une expression comme toto = 5. Vous affectez la valeur 5 à la variable toto et, par la suite, toto équivaut à 5. En Logo, l'affectation se fait au moyen de la primitive make. Cette primitive est suivie du nom de la variable sous forme de mot (précédé par "), suivi de la valeur à affectuer. A la différence du BASIC, le Logo accepte indifféremment des valeurs numériques ou alphanumériques. Exemple: make toto "5 : affecte la valeur 5 au mot toto. make "tutu "hello : affecte la valeur hello au mot tutu. Pour récupérer la valeur d'une variable, il faut faire précéder son nom du signe ':'. Exemple: :toto ou :tutu. Une formule BASIC du style de toto=toto+5 s'écrira: make "toto :toto + 5. Voici une des merveilleuses caractéristiques du Logo. Faites: make "toto "hello make "hello 1234 :toto produira hello :hello produira 1234. Comme toto contient hello et que hello contient 1234, il y a peut-être moyen de récupérer 1234 en partant de toto? Le Logo le permet (essayez toujours de faire ça en BASIC). En version 2, il suffit d'écrire thing thing "toto. Ce qui signifie: fournissez-moi ce qui à l'intérieur de l'objet figurant dans la variable toto. Hélas, en version 1, la primitive thing n'existe pas. Ne pleurez plus, Amstrad Magazine vous fournit la solution! Il suffit de créer la procédure thing de la façon suivante: ?thing :nom >op gprop :nom ".APV >end Remarques: ne cherchez pas à comprendre la procédure, elle sera expliquée lors du prochain article. Orthographiez bien APV en majuscules. Variables locales et variables globales --------------------------------------- En Logo, il existe deux sortes de variables: 1) Les variables globales qui correspondent à la notion de variable telle qu'elle est définie en BASIC. Ces variables sont connues et passées d'une procédure à l'autre. 2) Les variables locales. Elles sont définies dans le corps d'une procédure, et n'ont aucune valeur en dehors de celle-ci. La définition d'une variable locale est réalisée grâce à la primitive local, qui doit être suivie du nom de la variable sous forme de mot. Exemple: local "toto Les tests et les sauts ---------------------- Un langage digne de ce nom doit nécessairement être doté d'instructions permettant les tests et les sauts. Le Logo possède ces deux types de commandes. Instruction de test ------------------- L'instruction de test est constituée par l'instruction if. Elle est fort proche de l'instruction BASIC IF--THEN--ELSE. Syntaxe: if condition [commande si vrai] [commande si faux]. La primitive if permet donc l'exécution d'une série de commandes pour le cas où la condition spécifiée est vrai (clause THEN) et une autre série de commandes pour le cas où la condition est fausse (clause ELSE). Cette dernière partie est optionnelle. La condition est une opération de comparaison. Les opérateurs de comparaison sont <, > et =. A l'instar du BASIC qui fournit 0 si une comparaison est fausse et -1 si elle est vrai, Logo fournit une variable système particulière qui vaut TRUE si la comparaison est vraie, et FALSE si elle est fausse. Les opérateurs peuvent être utilisés en notation préfixée. L'utilisation des parenthèses est conseillée pour lever les cas litigieux. Exemples: 3 = 4 : réponse FALSE. 3 = 3 : réponse TRUE. > 3 4 : réponse FALSE, 3 n'est pas plus grand que 4. Vous pouvez bien entendu utiliser les opérateurs de comparaison avec des mots. Dans ce cas, c'est la position de la lettre dans l'alphabet (ou son code ASCII) qui permet de déterminer si un mot est plus grand ou plus petit qu'un autre. "toto = "tata : réponse FALSE. "toto > "salut : réponse TRUE. En plus des opérateurs de comparaison, le Logo comprend trois opérateurs logiques: and, or et not. Ces trois opérateurs doivent être utilisés en notation préfixée: Table de vérité --------------- A B and A B or A B not A ----- ----- ------- ------ ----- TRUE TRUE TRUE TRUE FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE TRUE TRUE FALSE FALSE FALSE TRUE TRUE Exemples: and (3 = 4) (> 5 2) : réponse FALSE. or (3 = 4) (5 > 2) : réponse TRUE. Remarque: vous pouvez utiliser les valeurs directes FALSE et TRUE en les indiquant sous forme de mots ("TRUE et "FALSE). Instruction de saut et étiquette -------------------------------- L'instruction de saut est utilisable uniquement à l'intérieur d'une procédure. La primitive go permet d'effectuer un saut à un endroit quelconque de la procédure. Cet endroit doit être identifié par la primitive label (étiquette) suivi d'un mot quelconque. L'instruction go doit être suivie de cette même étiquette. Exemple: ?to demo >make "i 0 >label "ici >make "i :i + 1 >pr :i >go "ici >end Ce programme affiche les nombres successifs, en partant de 1. N'oubliez pas de taper sur la touche 'ESC' pour sortir. Instruction d'arrêt ------------------- Lors d'utilisation de boucles, il est souvent nécessaire d'interrompre la procédure après un test. La primitive stop réalise cette fonction sans problème. Exercice: réalisez un programme qui trace une spirale carrée (une spirale dont chaque côté est à 90 degrés du précédent) dont le plus petit côté vaut 5 et qui grandit de 3 points à chaque itération. Le tracé doit s'arrêter seul lorsque la taille d'un côté dépasse 100 points. Vous pouvez utiliser la récurrence ou la primitive go. Solution en fin d'article. Affichage et saisie ------------------- L'affichage d'un mot est réalisé simplement par la primitive pr suivie du mot (précédé du signe "). L'affichage d'une phrase est plus complexe. Une phrase est considérée en Logo comme une liste de mots. Nous reviendrons sur cette notion dans le prochain article. Pour l'instant, il suffit de savoir que l'impression d'une phrase se fait à l'aide de la primitive pr suivie de la phrase entre crochets. Exemple: pr [Ceci est une phrase de démonstration.] L'affichage mixte contenant une variable et du texte est réalisé en entourant l'ensemble de la fonction pr et de ses arguments par des parenthèses. Exemple: make "an 29 (pr [Vous avez] :an "ans.) Saisie ------ Pour saisir une entrée au clavier, le Logo possède trois primitives différentes: 1) rc lit un et un seul caractère au clavier. On peut comparer cette primitive à la fonction INKEY$ du BASIC. 2) rq lit une suite de caractères, et la retourne au Logo sous forme d'un mot. 3) rl lit une série de caractères, et la retourne au Logo sous forme d'une liste (comprise entre crochets). Exemple: if rc = "o [pr [Vous avez dit oui.]][pr [Vous n'avez pas dit oui.]]. Essayez aussi: make "a rl, make "a rq, et make "a rc, et consultez la valeur de a après l'entrée, au moyen de la fonction :a. Les principales instructions disque ----------------------------------- Il est évident que, comme dans tout langage, le programmeur en Logo désire sauvegarder le fruit de ses efforts. Pour cela, il doit pouvoir effectuer une sauvegarde de ses procédures et de ses variables. Cette action est réalisée facilement grâce à la primitive load suivie d'un mot indiquant le nom sous lequel l'ensemble des procédures et des variables s'inscrira dans le répertoire de la disquette. Exemple: save "demo. Remarques: - Le nom est mémorisé en majuscules dans le répertoire, et suivi de l'extension LOG. - On ne peut pas sauvegarder une seconde fois sous le même nom. Il faut d'abord effacer le premier en retournant au CP/M et en tapant ERA MACHIN.LOG (où MACHIN représente le nom sous lequel vous avez mis en mémoire vos procédures). Le chargement se réalise de manière identique à l'aide de la primitive load. Exemple: load "demo. Enfin, une dernière primitive appelée dir vous fournit la liste de tous vos fichiers Logo sous la forme d'une liste (au sens Logo du terme, c'est-à-dire comprise entre crochets). Programme --------- En guise de conclusion à ce premier article, je vous propose d'écrire votre premier jeu en Logo. C'est le célèbre jeu de la fourchette. L'ordinateur choisit un nombre entier compris entre 1 et 100, et vous propose de le retrouver au moyen de plusieurs propositions. L'ordinateur vous indiquera si le nombre que vous avez proposé est trop grand ou trop petit. ?to fourchette >make "coups 0 >make "nombre random 100 >label "boucle >make "coups :coups + 1 >pr [Proposition ?] >make "choix rq >if :choix = :nombre > [(pr [Bravo! Trouvé en] :coups "coups.) stop] >if :choix < :nombre > [pr [Trop petit.]] > [pr [Trop grand.]] >go "boucle >end Résumé des primitives rencontrées dans cet article -------------------------------------------------- * + - / : opérateurs arithmétiques. < = > : opérateurs logiques. and : Permet d'effectuer des ET logiques. Si on désire plus de deux opérateurs, il faudra mettre toute l'opération entre parenthèses. ?and "TRUE "TRUE TRUE ?and ("TRUE "TRUE "FALSE) FALSE arctan : arctan nombre exprime, en degrés, l'arc dont la tangente vaut le nombre spécifié. bk : bk x. Instruction ayant pour effet de faire reculer la tortue de x unités. bye : Rend la main au CP/M. cos : cos a. Fournit le cosinus de l'angle A exprimé en degrés. cs : Efface l'écran graphique et replace la tortue à sa position de départ (centre de l'écran graphique, tête tournée vers le haut). ed : ed "nom de procédure. Edite la procédure désignée, afin de permettre au programmeur d'y apporter différentes modifications. Les quatre flèches permettent le déplacement du curseur, l'insertion est automatique, la touche DEL efface le caractère précédent, et la touche CLR efface le caractère situé sous le curseur. Pour sortir du mode éditeur: - en conservant les modifications apportées à la procédure: enfoncer la touche COPY. - sans conserver ces modifications: enfoncer la touche ESC. end : Figure en dernière ligne d'une procédure. end signale la fin de la procédure, et renvoit l'utilisateur au signal de sollicitation Logo ("?"). FALSE : Valeur système = faux. ?2 = 5 FALSE fd : fd x. Permet de faire avancer la tortue de x unités. fs : Réserve l'entièreté du moniteur vidéo à l'écran graphique. go : go "nom. Permet, au sein d'une procédure, de passer le contrôle à la ligne portant le label "nom désigné. ?to essai >label "punition >pr [Je ne dois pas m'endormir au cours de Logo.] >go "punition >end if : if proposition [action 1][action 2]. Dans le cas où la proposition est vraie, l'action 1 sera effectuée. Si elle est fausse, l'action 2 sera effectuée. ?if 6 = 4 [pr "Faut pas rêver!][pr "Pas fou, non?] Pas fou, non? int : int x. Fournit la valeur entière de x, c'est-à-dire tout ce qui se trouve devant la virgule. ?int -5,7 -5 label : label "nom. Permet de disposer une étiquette à l'intérieur d'une procédure, en vue d'y revenir au moyen de l'instruction go. Voir cette dernière pour l'exemple. load : load "nom de fichier. Permet de charger dans l'espace de travail Logo un fichier préalablement sauvegardé au moyen de l'instruction save. local : local "variable. Permet de rendre le contenu de la variable spécifiée valable uniquement à l'intérieur de la procédure en cours. ?to essai >local "var >make "var "coucou >pr :var >end essai defined ?make "var "salut ?essai coucou ?:var salut lt : lt x. Fait tourner la tortue de x degrés d'angle vers la gauche. make : make "nom de la variable "contenu Permet d'assigner une valeur à une variable. ?make "d 1/2 ?:d 0.5 not : Inverse la valeur de vérité. ?not "TRUE FALSE pr : pr "mot ou pr [liste]. Affiche à l'écran le mot ou la liste spécifiés, suivis d'un retour chariot. Des parenthèses seront nécessaires si on désire faire suivre pr de plusieurs objets. ?(pr "a "b "c) a b c random : random x. Fournit un nombre pseudo-aléatoire compris entre 0 et x-1. rc : Attend un caractère frappé au clavier et l'affiche. Cette fonction s'apparente au INKEY$ du BASIC, et est très utile dans les procédures où on attend une réponse de l'utilisateur. ?if rc = "0 [pr "oui][pr "non] repeat : repeat x [action]. Effectue x fois l'action spécifiée. repeat 4 [fd 50 rt 90] dessinera un carré. rl : Attend que l'utilisateur frappe au clavier une série de caractères suivie d'un retour chariot, et la retourne sous forme de liste. ?rl Coucou, me voici! [Coucou, me voici!] rq : Attend que l'utilisateur frappe au clavier une série de caractères suivie d'un retour chariot, et la retourne sous forme de mot. ?rq Il neige ici. Il neige ici. rt : rt x. Fait tourner la tortue de x degrés d'angle vers la droite. save : save "nom de fichier. Permet de sauvegarder l'espace de travail Logo sur disque, dans le but de le réutiliser par la suite. Voir load. sin : sin x. Fournit la valeur du sinus de l'angle x exprimé en degrés. ss : Réserve au texte les 5 lignes situées au bas de l'écran graphique. st : Fait apparaître la tortue sur l'écran graphique. stop : Utilisé dans une procédure pour en arrêter l'exécution. Dans le cas où stop figure dans une procédure appelée par une autre, le contrôle sera rendu à la procédure appelante. Un 'throw "TOPLEVEL' par contre stoppera l'exécution de toute procédure, et reviendra au signal de sollicitation Logo ("?"). thing : thing "nom. Fournit la valeur de la variable spécifiée sous forme de nom. to : ?to nom de procédure. Signale qu'on va définir une procédure. TRUE : Valeur système = vrai. ?5 < 9 TRUE ts : Met l'écran en entier à la disposition du texte. Solution des exercices ---------------------- 1 - Polygone ?to polygone :nombre :cote >repeat :nombre > [rt 360 / :nombre fd :cote] >end 2 - Spirale Méthode du saut: ?to spirale >make "c 5 >label "ici >fd :c rt 90 >make "c :c + 3 >if :c > 100 > [stop] >go "ici >end Méthode récurrente: ?to spirale >fd :c rt 90 >make "c :c + 3 >if :c > 100 > [stop] >spirale >end Dans ce dernier cas, il est nécessaire d'affecter la valeur 5 à la variable c avant de lancer la procédure (make "c 5). Deuxième partie: Amstrad Magazine, No.9, Avril 1986, p.102 ---------------- Lors du précédent article, nous avons découvert les principes de base du langage Logo. Les principales primitives de dessin, de saisie d'affichage et de manipulation de données ont été étudiées avec suffisamment de détails pour vous permettre de créer vos propres premiers programmes en Logo. Au cours de cette deuxième partie, nous allons parfaire notre connaissance des primitives graphiques, étudier les primitives sonores, et aborder l'étude des listes, des propriétés et des opérations sur les objets. A l'issue de cet article, vous pourrez comprendre la plupart des programmes écrits en Logo. Le troisième et dernier article étudiera la structure système du Logo, et vous permettra de réaliser une version française du Logo, et de réaliser votre premier programme d'intelligence artificielle. Complément sur les fonctions graphiques --------------------------------------- Nous n'allons pas abandonner notre chère tortue dès le début du deuxième article. Je vous propose donc de découvrir quelques primitives moins usitées mais non moins intéressantes, permettant la manipulation de la tortue et des graphiques. Gestion de la tortue et de l'écran ---------------------------------- Pour commencer, étudions quelques primitives qui portent exclusivement sur le mode de fonctionnement de la tortue et de l'écran graphique. - ht est une primitive très simple, elle permet de cacher la tortue (Hide Turtle). C'est la primitive complémentaire à st que nous avons étudiée dans le premier article. Pourquoi cacher la tortue? C'est très simple, lors du traçage d'un graphique, le système redessine la tortue après chaque déplacement. Si la tortue est cachée, la quantité de travail à effectuer par le système est moins importante, ce qui permet une accélération de son exécution. Analysons à présent ce qui se produit lorsque la tortue 'sort' des limites de l'écran: tapez cs fd 250. La tortue se dirige vers le haut de l'écran, et se bloque au sommet de celui- ci. Ce mode est appelé mode fenêtre (window). C'est le mode par défaut lors de l'initialisation du Logo. La primitive window permet de revenir à ce mode. Tapez cs wrap fd 250: la tortue se dirige vers le haut de l'écran, et continue son tracé en partant du bas. Dans ce mode, l'écran peut être considéré comme un rouleau cylindrique. C'est la primitive wrap qui permet de sélectionner ce mode, appelé mode rouleau. Tapez cs fence fd 250: la tortue refuse de se déplacer, et un message d'erreur 'turtle out of bounds' (tortue hors des limites) apparaît. C'est la primitive fence qui permet de sélectionner ce mode. Revenez au mode normal en tapant cs window. En résumé, la tortue possède trois modes de déplacement, le mode fenêtre (window), le mode rouleau (wrap), et le mode limité ou frontière (fence). - La primitive clean efface l'écran, mais, à l'inverse de la primitive cs, elle laisse la tortue en place. - Jusqu'à présent, nous étions capables de tourner la tortue d'un certain angle à l'aide des primitives rt et lt. Cet angle s'ajoutait à l'angle déjà pris par la tortue, et il était très difficile de positionner la tortue vers une direction précise si on ne connaissait pas la direction précédente. La primitive seth permet de contourner cet obstacle. Elle nécessite comme argument l'angle vers lequel la tortue doit être orientée. Attention, l'angle spécifié n'est pas à prendre au sens du cercle trigonométrique. Dans le cas de seth, l'angle est compté positivement dans le sens des aiguilles d'une montre, et l'angle 0 correspond à l'orientation de la tortue vers le haut de l'écran. - La primitive setpos suivie d'une liste de deux arguments (entre crochets) permet de déplacer la tortue vers le point spécifié. Exemple: setpos [100 100] déplace la tortue vers le point de coordonnées 100, 100. Le centre de l'écran est situé aux coordonnées 0, 0. Voici un schéma qui vous fournira les limites maximales pour les coordonnées. Ces limites sont données pour un écran graphique total (fs). (-320,199) (319,199) +-------------------+ | | | (0,0) | | | +-------------------+ (-320,-200) (319,-200) - La primitive dotc suivie d'une liste de deux paramètres permet d'afficher un point aux coordonnées indiquées par la liste sans modifier la position de la tortue. - Nous avons appris que l'écran peut se présenter sous trois modes différents (fs, ts, ss). Le dernier de ces modes, appelé mode mixte, permet d'afficher 5 lignes de commandes en bas d'écran et des graphiques sur le reste de l'écran. La primitive setsplit permet de modifier le nombre de lignes réservées aux commandes. L'argument qui suit setsplit doit être un nombre compris entre 1 et 25 (d'une ligne à la totalité de l'écran). Exemple: setsplit 10 ss. Enfin, pour terminer l'étude des primitives de gestion d'écran et de la tortue, il existe deux primitives très utiles qui fournissent une série de paramètres concernant l'état courant de l'écran ou de la tortue sous la forme d'une liste. Nous verrons plus loin dans cet article comment isoler un élément précis de la liste. La primitive sf fournit une liste de 5 paramètres identifiant les caractéristiques courantes de l'écran. Le premier donne le nombre correspondant à la couleur du fond. Le deuxième indique le mode graphique courant de l'écran (ss, ts, ou fs). Le troisième indique le nombre de lignes de texte en mode ss (ce nombre est modifié par la primitive setsplit). Le quatrième indique le type d'écran (wrap, fence, ou window). Le cinquième indique le rapport de proportion de l'écran. Ce nombre vaut 1. Exemple: au départ, sf fournit: [0 TS 5 WINDOW 1]. La primitive tf fournit une liste de 6 paramètres identifiant les caractéristiques de la tortue. Le premier indique la position horizontale de la tortue (coordonnée X). Le deuxième indique la position verticale de la tortue (coordonnée Y). Le troisième indique la valeur de l'angle de la tortue (voir seth). Le quatrième indique l'état du crayon (voir ci-dessous). Le cinquième indique la couleur courante du crayon (voir ci-dessous). Le sixième vaut TRUE si la tortue est visible (st), et FALSE si elle est invisible (ht). Gestion du crayon et des couleurs --------------------------------- La tortue peut être considérée comme un petit dispositif qui tient un crayon. Jusqu'à présent, le crayon était toujours appuyé sur le papier (l'écran), et contenait toujours la même couleur. Un certain nombre de primitives permettent de modifier cet état du crayon et d'en changer la couleur. 1) pu (Pen Up) permet de relever le crayon, et ainsi de déplacer la tortue sans dessiner. 2) pd (Pen Down) permet de redescendre le crayon, et de remettre la tortue dans son état normal. 3) pe (Pen Eraser) permet de transformer le crayon en gomme. Dans ce mode, le crayon dessine dans la couleur du fond d'écran. Il permet donc l'effacement de morceaux de dessin préalablement affichés. 4) px (Pen Xor) permet d'inverser logiquement (xor) la couleur de chacun des points sur lesquels la tortue passe. Ainsi, en monochrome, si la tortue passe sur le fond, elle trace le point et, si elle passe sur un trait déjà tracé, elle le gomme. 5) setpc permet de choisir son crayon. Il y a 4 crayons possible (0, 1, 2, et 3). Exactement comme le mode 1 du BASIC. Le crayon 0 correspond à la couleur du fond. 6) setpal permet d'affecter une couleur précise à un crayon. Cette primitive nécessite 2 arguments. (Les spécialistes disent que la primitive est dyadique.) Le premier représente le numéro du crayon (0 à 3), et le second est une liste de 3 nombres compris entre 0 et 2, qui représentent la densité de chacune des 3 couleurs fondamentales (rouge, vert, et bleu) permettant de composer la couleur choisie. Exemple: crayon 1 noir : setpal 1 [0 0 0] crayon 2 rouge : setpal 2 [2 0 0] crayon 3 jaune vif : setpal 3 [2 2 0] crayon 3 jaune pastel : setpal 3 [1 1 0] 7) pal fournit la liste des densités des 3 couleurs fondamentales pour le crayon spécifié. Exemple: pal 1 fournit en standard: [2 2 0]. Modification dynamique des caractéristiques de l'écran ------------------------------------------------------ Le sujet abordé ici relève plus des trucs et astuces que de la programmation en Logo. Cependant, il va nous permettre de découvrir 2 nouvelles primitives très importantes. Si vous possédez convenablement le BASIC de votre Amstrad, vous devez savoir que l'envoi de certains caractères spéciaux, appelés codes de contrôle, produit des effets surprenants, qui vont de l'activation de la sonnette (BELL) à l'extinction du curseur texte. Ces codes de contrôle peuvent être envoyés en Logo par l'intermédiaire de la primitive char, qui correspond à la fonction CHR$ du BASIC. Elle affiche donc le caractère correspondant au code ASCII spécifié. Ainsi, char 7 déclenche l'émission de la sonnette. Table des codes de contrôle intéressants ---------------------------------------- Valeur Nb de param. Effet ------ ----------- ----- 02 0 Efface le curseur texte. 03 0 Affiche le curseur texte. 04 1 Positionne le mode écran (0, 1, ou 2). 07 0 Active la sonnette. 28 3 Positionne une encre dans une paire de couleurs. Le premier paramètre représente le numèro de la couleur (0 à 15), le deuxième et le troisième, la paire de couleurs (0 à 31). 29 2 Positionne le bord de l'écran dans une paire de couleurs précises (0 à 31). Certaines commandes nécessitent l'émission de plusieurs caractères successifs. On ne peut pas utiliser une formulation du type char x char y, car, après chaque char, le système transmet un retour chariot et un saut de ligne. (Essayez char 65 char 66, par exemple.) Il faut donc trouver un moyen d'accoler le résultat de 2 char successifs. La primitive word (mot) réalise cette performance. Ainsi, word char 65 char 66 fournira le mot composé des lettres A et B, en l'occurrence AB. Exercice: dès à présent, nous sommes capables d'écrire une nouvelle procédure qui nous permettra de modifier la couleur du bord de l'écran en la couleur de notre choix. ?to bord :valeur >pr (word char 29 char :valeur char :valeur) >end Remarques: notez la présence des parenthèses qui permettent de faire porter la primitive word sur plus de 2 arguments (ici 3). Cette procédure peut aisément être modifiée en vue d'afficher un bord clignotant grâce à l'utilisation de 2 paramètres. Nous laissons au lecteur le soin d'écrire cette nouvelle procédure. De tout... un peu ----------------- Terminons cette partie de l'étude du Logo par un patchwork de primitives diverses et de petits programmes d'illustrations. La gestion de la manette de jeu ------------------------------- La primitive paddle, suivie du numéro de la manette, permet de lire la position de la manette de jeu. En fonction de la position de la manette, elle fournit un nombre compris entre 0 et 7 et ce, suivant le diagramme figurant ci-après. Si la manette est au repos, elle retourne la valeur 255. 0 7 1 6 2 5 3 4 Pour vos essais, utilisez la suite d'instructions suivantes: repeat 32767 [pr paddle 0]. La primitive buttonp suivie du numéro de la manette fournit la valeur TRUE si le bouton de la manette est enfoncé, et FALSE sinon. Voici un petit programme qui peut constituer le début d'un exercice plus important de création d'un télécran. Son but consiste à permettre à l'utilisateur de dessiner sur l'écran en déplaçant simplement la manette de jeux. ?to telecran >cs >ht >label "ici >make "sens paddle 0 >if :sens < 8 > [seth :sens * 45] >if :sens = 255 > [] > [fd 4] >go "ici >end Remarque: la lenteur du Logo pour ce type de traitement est particulièrement visible dans ce programme. Retour à la récursivité ----------------------- La récursivité a été introduite lors du premier article. Cette notion est fondamentale, elle mérite que l'on y revienne par l'intermédiaire d'un petit programme destiné à dessiner un cristal de neige. ?to cristal :x >repeat 3 > [fvk :x rt 120] >end cristal defined ?to fvk :x >if :x < 5 > [fd :x stop] >fvk :x / 3 lt 60 >fvk :x / 3 rt 120 >fvk :x / 3 lt 60 >fvk :x / 3 >end fvk defined Lancez le dessin du flocon par la commande 'cristal 60'. La procédure fvk est un modèle de récursivité, elle s'appelle quatre fois elle-même. (Pour ceux qui seraient désireux de briller en société, signalons que la figure dessinée par la procédure fvk n'est pas choisie au hasard; il s'agit d'une courbe mathématique bien précise, qui répond au doux nom de "Fractale de von Koch".) Jusqu'à présent, la récursivité était toujours terminale. Autrement dit, la dernière ligne de la procédure récursive était toujours un appel récursif de la procédure. Il existe cependant des procédures à récursivité non-terminale, comme en témoigne l'exemple suivant: ?to motif :x >if :x < 5 > [stop] >fd :x rt 90 >motif :x - 10 >lt 90 bk :x >end motif defined ?to frise >cs pu setpos [-300 0] pd >repeat 6 > [motif 70 rt 90 fd 80 lt 90] >end frise defined Essayez la procédure motif en tapant cs motif 80. Remarquez le trajet inverse de la tortue, dû à la récursivité non-terminale. Lancez la procédure frise, qui dessine une série de motifs. Les primitives sonores ---------------------- Les primitives sonores ne sont pas, à proprement parler, des primitives Logo. Elles sont constituées par une extension du langage adaptée spécialement au générateur sonore de l'Amstrad. Ces primitives sont semblables aux instructions BASIC qui portent le même nom. Les décrire complètement dépasse le cadre de cet article. Les primitives sont au nombre de 4: sound, env, ent, et release. Reportez-vous à votre manuel BASIC pour une description complète de ces commandes. Il faut cependant noter que les différents paramètres se passent sous la forme d'une liste. Exemple: sound [canal période-tonale durée volume enveloppe-de-volume enveloppe-de-tonalité période-de-bruit]. La primitive op --------------- La primitive op (OutPut) permet de sortir d'une procédure en retournant la valeur en cours au programme appelant. op nécessite un argument. Une application pratique de la primitive op sera vue par la suite. Les opérateurs Logo ------------------- Nous quittons à présent définitivement notre tortue et les procédures graphiques pour aborder l'autre face du Logo, la manipulation des objets et des propriétés. Nous avons vu que Logo peut manipuler différents objets: - Les primitives et les procédures. Celles-ci sont identifiées par leur nom, qui n'est précédé d'aucun symbole particulier. - Les nombres, qui ne sont précédés d'aucun caractère particulier, mais qui commencent évidemment par un chiffre. - Les mots (chaînes de caratères), qui sont précédés du signe guillemet ("). - Les contenus de variables, qui sont précédés du symbole deux points (:). - Les listes, qui sont constituées par une série de mots ou de chiffres entourés de crochets, et séparés par un caractère espace. Ainsi: [martin jules 123 paris] constitue une liste de quatre éléments distincts qui sont martin, jules, 123, et paris. Les opérateurs sur les mots et les listes ----------------------------------------- Il existe un grand nombre d'opérateurs qui portent sur les mots (les nombres peuvent être considérés comme des mots) et sur les listes. Certains portent sur un seul type, d'autres portent sur les deux types avec des effets parfois différents. Remarque: les éléments d'une liste sont des mots, les éléments d'un mot sont des caractères, les éléments d'un nombre sont des chiffres. - word (déjà décrit) unit 2 mots pour ne plus en former qu'un seul. Cette primitive ne porte que sur les mots et les nombres. Exemple: word "bonjour 1233. - char qui transforme un nombre en lettres, et son inverse ASCII qui transforme une lettre en nombre. Exemple: ASCII "A fournit 65. - bf (But First) rend tous les éléments de l'objet spécifié, sauf le premier. Cette primitive fonctionne aussi bien avec les mots qu'avec les listes. Exemples: bf [daniel jean denis martin] fournit [jean denis martin]. bf "hello fournit ello. bf 1234 fournit 234. - bl (But Last) rend tous les éléments de l'objet spécifié, sauf le dernier. Exemples: bl [daniel jean denis martin] fournit [daniel jean denis]. bl "hello fournit hell. bl 1234 fournit 123. - first fournit le premier élément de l'objet spécifié. Exemples: first [daniel jean denis martin] fournit daniel. Attention: daniel n'est plus une liste, mais un mot. first "hello fournit h. first 1234 fournit 1. - last fournit le dernier élément de l'objet spécifié. Cette procédure n'est disponible qu'en Logo version 2 (CPC-6128). Cependant, elle est facile à simuler grâce à l'utilisation des primitives item et count décrites ci- dessous. Cette procédure nous permet, par ailleurs, de découvrir un exemple pratique de l'utilisation de la primitive op. ?to last :objet >op item count :objet :objet >end Exemples: last [daniel jean denis] fournit denis. last "hello fournit o. last 1234 fournit 4. - count compte le nombre d'éléments contenus dans l'objet spécifié. Exemples: count [daniel jean denis martin] fournit 4. count "hello fournit 5. count 1234 fournit 4. - item fournit l'élément de rang spécifié d'un objet donné (syntaxe: item rang objet). Exemples: item 2 [daniel jean denis martin] fournit jean. item 3 "hello fournit l. item 2 1234 fournit 2. Arrivé à ce point, essayez de comprendre la procédure last décrite ci-dessus, ensuite devinez ce que donneront les propositions suivantes. Vérifiez vos réponses au moyen de votre système. last first [daniel jean denis] first last [daniel jean denis] first bf [daniel jean denis] first bf "coucou first item 2 [daniel jean denis] count bl "Amstrad - fput (First PUT) est une primitive dyadique qui compose un nouvel objet en ajoutant le premier objet en tête du second. Exemples: fput "bonjour "rené fournit bonjourrené. fput "daniel [anne sophie] fournit [daniel anne sophie]. fput [anne sophie] "daniel ne peut pas fonctionner. On ne peut pas ajouter une liste à un mot. fput [a][c b] fournit [[a] c b] (liste qui contient une liste). - list forme une liste à l'aide des objets qui suivent. Le type des objets est préservé. (Si un des objets est une liste, alors la liste contiendra une autre liste.) Exemples: list "daniel "albert fournit [daniel albert]. (list "coco "tutu "toto "tito) fournit [coco tutu toto tito]. list "a [b c] fournit [a [b c]]. - se (SEntence) est identique à liste, mais le type des objets n'est pas préservé. Exemple: se "a [b c] fournit [a b c]. Exercice -------- Pour concrétiser ces nouvelles notions, étudions le mécanisme d'un générateur automatique de poésie digne des auteurs français de la nouvelle vague. ?to poesie >make "sujet [[Le chat][Dieu][L'Amstrad][Arthur]] >make "verbe [aime prend mange reçoit] >make "compl [[la souris.][les ordres.][la lune.][le matin.]] >repeat 10 [(pr item 1 + random 4 :sujet item 1 + random 4 :verbe item 1 + random 4 :compl)] >end Remarque: lors de la définition, le Logo refuse obstinément d'accepter plus de 80 caractères par ligne de procédure, et ce en émettant un signal de refus. Qu'à cela ne tienne, nous sommes plus malins que lui. En effet, il suffit à cet instant d'enfoncer la touche ENTER, d'encoder les lignes de procédure ainsi définies. A ce moment, il est tout-à-fait possible d'ajouter des caractères à une ligne. On sort alors de l'éditeur par la touche COPY, et le tour est joué! (ROCHE> Encore plus simple... Il suffit d'indenter! Exemple: >repeat 10 > [(pr item 1 + random 4 :sujet > item 1 + random 4 :verbe > item 1 + random 4 :compl)] >end Le Dr. Logo est un des rares Logo permettant d'indenter les procédures.) La gestion des propriétés ------------------------- Généralités ----------- La gestion des propriétés est certainememt la caractéristique qui sépare le plus le Logo des langages conventionnels comme le BASIC, le Pascal, le COBOL, ou même le C. La possibilité de définir des propriétés associées aux objets rapproche le Logo des langages dits 'd'intelligence artificielle', dont PROLOG est certainement l'exemple le plus connu en France. La notion de propriété n'est pas évidente pour les personnes habituées aux langages classiques. Cependant, une fois maîtrisée, cette notion vous permettra de réaliser des programmes d'une performance inégalable en programmation conventionelle. Pour définir une propriété, 3 éléments sont indispensables: 1) Un objet principal, appelé sujet de la propriété, et constitué par un mot. 2) Un deuxième objet, appelé nom de la propriété, et constitué par un mot. 3) Un troisième objet, appelé contenu de la propriété, et qui peut être un mot ou une liste. Principales primitives ---------------------- - La primitive pprop (Put PROPerty) permet de définir une propriété. Elle utilise les 3 éléments définis ci-dessus. Exemples: pprop "test "egal "martin pprop "test "vaut [le petit 123 cadeau] pprop "var "egal "daniel Le premier exemple assigne la propriété 'egal' au mot 'test', et assigne à la propriété 'egal' le contenu 'martin' (mot) pour le mot 'test'. Le deuxième exemple assigne la propriété 'vaut' au mot 'test', et assigne à la propriété 'vaut' le contenu 'le petit 123 cadeau' (liste) pour le mot 'test'. Le troisième exemple assigne la propriété 'egal' au mot 'var', et assigne à la propriété 'egal' le contenu 'daniel' (mot) pour le mot 'var'. - La primitive gprop (Get PROPerty) permet de rechercher le contenu d'une propriété pour un mot particulier. Exemples: si vous avez encodé les exemples de pprop, alors: gprop "test "egal affichera martin. gprop "test "vaut affichera la liste [le petit 123 cadeau]. gprop "var "egal affichera daniel. - La primitive plist (Put property LIST) fournira une liste des propriétés de l'objet désigné. Exemples: si vous avez encodé les exemples de gprop, alors: plist "test affichera [vaut [le petit 123 cadeau] egal martin]. plist "var affichera [egal daniel]. plist "martin affichera [] (pas de propriété). - La primitive glist (Get property LIST) fournit une liste avec les objets qui sont les sujets de la propriété désignée. Exemples: si vous avez encodé les exemples de pprop, alors: glist "test affichera [] (test n'est pas une propriété). glist "egal affichera [test var]. glist "vaut affichera [test]. - La primitive remprop (REMove PROPerty) s'utilise avec 2 arguments, et enlève le contenu de la propriété spécifiée comme second argument de l'objet spécifié comme premier argument. Exemple: remprop "test "egal enlève le contenu (martin) de la propriété (egal) de l'objet (test). Les propriétés internes ----------------------- Le Logo connaît un certain nombre de propriétés internes. En version 1, il y a 3 propriétés internes définies en standard. Attention: pour les propriétés internes, respectez les majuscules et le point ("."). .APV Propriété correspondant à l'affectation d'une valeur à un mot (par l'intermédiaire de la primitive make). Cette propriété est valable uniquement pour les variables globales. L'affectation de variable est donc un cas particulier de la gestion des propriétés. Exemples: plist "demo fournit []. make "demo 12345 plist "demo fournit [.APV 12345]. :demo fournit 12345. glist ".APV fournit demo. :essai fournit 'essai has no value'. pprop "essai ".APV "etonnant :essai fournit etonnant. .DEF Cette propriété caractérise une procédure définie. Ainsi, définir une procédure en Logo (par l'intermédiaire de to) ne correspond à rien d'autre qu'à l'affectation d'une propriété spéciale à un objet. Exemples: ?to triangle >repeat 3 > [fd 100 rt 120] >end triangle defined gprop "triangle ".DEF fournit [[][repeat 3 [fd 100 rt 120]]]. glist ".DEF fournit [triangle]. pprop "carre ".DEF [[][repeat 4 [fd 100 rt 90]]] est équivalent à: ?to carre >repeat 4 > [fd 100 rt 90] >end .PRM Cette propriété caractérise les primitives du système. Ainsi, glist ".PRM vous fournit une liste de toutes les primitives du langage. Remarque: le contenu d'une propriété .PRM est un nombre qui représente l'adresse interne de la primitive dans la mémoire centrale. Exemple: plist "fput fournit [.PRM 3608] en version 1 (en version 2, l'adresse peut être différente). Traduction du Logo dans une autre langue ---------------------------------------- La traduction des primitives du Logo dans une langue autre que l'anglais ne pose aucun problème lorsque la gestion des propriétés et l'utilisation de la propriété .PRM sont parfaitement comprises. La procédure suivante vous permettra de créer votre propre vocabulaire Logo. ?to traduit :anglais :français >pprop :français ".PRM gprop :anglais ".PRM >end traduit defined Explications: la fonction gprop :anglais ".PRM fournit l'adresse de la primitive anglaise. La fonction pprop affecte cette adresse au mot français avec la propriété .PRM. Vous trouverez à la fin de cet article une liste des mots-clés du Logo anglais de Digital Research, avec des propositions de traduction en français. Simulation en version 1 de primitives de la version 2 ----------------------------------------------------- La version 2 du Logo (CPC-6128) est plus riche que la version 1. Quelques primitives sont très faciles à simuler grâce à la gestion des propriétés. - pops affiche toutes les procédures contenues dans l'espace de travail. ?to pops >po glist ".DEF >end - pons affiche tous les noms et toutes les valeurs des variables globales. ?to pons >po glist ".APV >end - poall affiche à la fois le contenu de pops et celui de pons. ?to poall >po glist ".DEF >po glist ".APV >end - edall permet l'édition de toutes les procédures à la fois. ?to edall >ed glist ".DEF >end - define permet de définir une procédure en mode direct. ?to define :nom :liste >pprop :nom ".DEF :liste >end Autres primitives Logo ---------------------- Pour terminer cet article, nous vous proposons de découvrir quelques primitives importantes passées sous silence jusqu'à présent. - catch permet de lancer une procédure jusqu'à la rencontre d'un throw suivi du nom de l'étiquette spécifiée. Exemple: catch "coucou [suite] lance la procédure suite. Lors de la rencontre d'un throw "coucou dans la procédure suite, le retour à la procédure appelante est réalisé. - throw (voir ci-dessus). - emptyp fournit la valeur TRUE si le paramètre spécifié est vide, et FALSE sinon. Exemple: emptyp "coucou fournit FALSE. Atention: 0 n'est pas vide! - er efface la procédure spécifiée ou la liste de procédures spécifiées. Exemple: er "carre - ern efface la valeur de la variable spécifiée. Exemple: make "toto 1234 :toto 1234 ern "toto :toto - po affiche à l'écran le contenu de la procédure spécifiée. - pots affiche à l'écran la première ligne de toutes les procédures contenues dans l'espace de travail. Programme de conclusion ----------------------- Programme permettant la recherche dans une liste. Cette procédure fournit l'élément qui suit l'élément recherché dans une liste. Si l'élément recherché n'est pas dans la liste, la procédure fournit 0. Cette procédure doit être utilisée avec une liste formée de paires d'élément. ?to recherche :liste :nom >if emptyp :liste > [op 0] >if = :nom first :liste > [op item 2 :liste] >op recherche bf bf :liste :nom >end Application ----------- La procédure de recherche décrite ci-dessus peut être utilisée pour générer une note de musique. ?to note :valeur >local "octave >make "octave [DO 478 DO# 451 RE 426 RE# 402 MI 379 FA 358 FA# 338 SOL 319 SOL# 301 LA 284 LA# 268 SI 253] >sound (se 1 recherche :octave :valeur 200) >end L'utilisation est très simple: il suffit d'écrire note "DO ou note "SOL#. Liste alphabétique et syntaxe des primitives de la deuxième partie ------------------------------------------------------------------ .APV .DEF .PRM ascii : ascii "mot bf : bf "mot bf [liste] bf nombre bl : bl "mot bl [liste] bl nombre catch : catch variable procédure char : char x, où x est un nombre représentant un code ASCII. clean count : count "mot count [liste] count nombre cs define : define "nom [[][...]] dot : dot [x y], où x et y représentent les coordonnées (x,y) d'un point de l'écran. edall emptyp : emptyp "mot emptyp [liste] emptyp nombre ent : ent [NE A B C A' B' C' ...] où NE représente le numéro d'enveloppe de ton (de 0 à 15), A le nombre de pas, B la taille du pas en fréquence, et C le temps de pause. 5 sections ABC peuvent être décrites. env : env [NE A B C A' B' C' ...] où NE représente le numéro d'enveloppe de volume (de 0 à 15), A le nombre de pas, B la taille en volume du pas, et C le temps consacré à chaque pas. 5 sections ABC peuvent être décrites. er : er "nom de procédure er [liste de noms de procédures] ern : ern "nom de variable ern [liste de noms de variables] fence first : first "mot first [liste] first nombre fput : fput élément1 élément2 glist : glist "propriété gprop : gprop "nom "espèce ht : ht. Rend la tortue invisible. item : item x yyy, où x est un nombre, et yyy un mot, une liste, ou un nombre. last : last "mot last [liste] last nombre op pal : pal x, où x est le numéro du crayon dont on désire la description. pd pe plist : plist "mot po : po "nom d'une procédure poall pons pops pots pprop : pprop "objet "propriété pu px release : release x. Libère le canal sonore numéro x en attente. remprop : remprop "nom "nature se : se élément1 élément2 setpal : setpal x [a b c], où x est un numéro de crayon, et a, b, et c les intensités des composantes couleurs. setpc : setpc x, où x est le numéro de crayon à utiliser. setpos : setpos [x y], où x et y représentent un point de coordonnées (x,y). setsplit : setsplit x, où x est un entier compris entre 1 et 23. tf throw sound : sound [poids canal période durée volume enveloppe-de-volume enveloppe- de-ton] window word : word "mot1 "mot2 wrap Equivalent anglais-français des primitives ------------------------------------------ Dans cette section, nous vous proposons un exemple de traduction des différents termes utilisés par le Logo de votre Amstrad. Ces termes français sont inspirés du Logo utilisé par une machine bien française, le Thomson TO7. Nous n'avons bien entendu pas tout traduit, les termes manquants sont laissés à l'appréciation du lecteur. Anglais Français ------- -------- and et ascii ascii bf sp (Sauf Premier) bk re (REcule) bl sd (Sauf Dernier) buttonp bouton? char car (CARactère) clean nettoie co co (COntinue) count compte cs vg (Vide Graphique) ct vt (Vide Texte) dir catalogue dot point ed ed (EDite) emptyp vide? end fin ent ent (ENveloppe de Ton) env env (ENveloppe de Volume) er efp (EFface Procédure) ern efn (EFface Nom de variable) fd av (AVance) fence clos first premier fput mp (Met Premier) go va ht ct (Cache Tortue) if si int entier keyp touche? label étiquette last dernier list liste load charge lt ga (GAuche) make donne not non paddle manette pal palette pd bc (Baisse Crayon) pe gomme pr ecris pu lc (Lève Crayon) px ic (Inverse Crayon) random hasard rt liscar (LIS un CARactère) repeat répète rl lisliste rq lismot rt dr (DRoite) save sauve seth fcap (Fixe CAP) setpal fpal (Fixe PALette) setpc fcc (Fixe Couleur Crayon) setpos fpos (Fixe POSition) sound son st mt (Montre Tortue) to pour window fenêtre word mot wordp mot? wrap rouleau Troisième partie: Amstrad Magazine, No.10, Mai 1986, p.? ----------------- Voici la troisième et dernière partie de notre initiation au langage Logo. Si vous avez suivi avec attention les deux premières parties de cette série d'articles, vous êtes capable de comprendre ou de réaliser la majeure partie des applications écrites en Logo. L'objectif de cette troisième partie est de vous permettre de mieux connaître les primitives en contact direct avec le système et le matériel, ainsi que de découvrir différentes techniques spécifiques, au moyen de procédures plus performantes. Les primitives spéciales ------------------------ Le Logo possède une série de primitives qui permettent le contact direct avec le matériel ou avec la structure interne du langage. Ces primitives sont comparables aux instructions et fonctions PEEK, POKE, INP, et OUT du BASIC. Certaines d'entre elles sont précédées d'un point (".") qui rappelle à l'utilisateur l'aspect particulier et le danger relatif de l'utilisation de ces primitives. A l'instar du BASIC, la mémoire disponible pour les développements propres à l'utilisateur ne se mesure pas en octets mais bien en étapes. (C'est le mot français qui se rapproche le plus du mot anglais "nodes".) Le nombre d'étapes disponibles au départ dépend du système utilisé. Il diminue au fur et à mesure de l'utilisation de la mémoire centrale. La primitive 'nodes' permet de connaître le nombre d'étapes disponibles à un moment précis. Essayez la séquence suivante: ?nodes. Notez le résultat obtenu. Recommencez: ?nodes. Notez le nouveau résultat. Vous aurez remarqué que le second résultat est inférieur au premier, et ce de 3 unités. Et cependant, aucune mémoire n'a été affectée par une procédure ou une variable. Le simple fait d'introduire des commandes consommerait-il l'entier de la mémoire? Pour le savoir, lancez la séquence suivante: ?repeat 1000 [pr nodes]. Nous observons un défilement d'une suite de nombres décroissants. A l'approche de 100, la séquence s'arrête. Après un court instant, le programme reprend avec un nombre de 'nodes' proche de celui du départ. Que s'est-il passé? Chaque instruction introduite réside un court instant en mémoire, et affecte un pointeur positionné sur l'espace disponible. Lorsque l'espace disponible devient trop faible, un processus de "collecte des déchets" (garbage collection) se produit. La procédure en cours s'arrête et le processus de récupération entre en fonction. Un phénomène similaire se produit en BASIC lorsque de trop nombreuses opérations sur des chaînes de caractères ont eu lieu. La primitive 'recycle' permet de forcer le processus de collecte de déchets. - .deposit est la primitive équivalente à l'instruction POKE du BASIC. C'est une primitive dyadique qui permet d'écrire à l'adresse spécifiée par le premier argument la valeur spécifiée par le second. Le premier argument étant une adresse, il doit être compris entre 0 et 65535. Le second argument est une valeur à inscrire à l'adresse spécifiée: elle doit être comprise entre 0 et 255. Nous attirons votre attention quant à l'utilisation de cette primitive. En effet, elle peut planter le système ou même détruire la disquette. Son utilisation est donc réservée aux spécialistes. - .examine est la primitive équivalente à la fonction PEEK du BASIC. Elle nécessite un seul argument qui représente l'adresse à lire, et fournit comme résultat le contenu de l'adresse lue. L'argument étant une adresse, il doit être compris entre 0 et 65535. La valeur retournée par cette primitive est toujours comprise entre 0 et 255. Contrairement à la primitive .deposit, l'utilisation de cette primitive est absolument sans danger. Exemple: voici une petite procédure de démonstration de la primitive .examine. Elle permet d'afficher un message particulier du Logo qui ne se produit que si la mémoire disponible sur l'ordinateur n'est pas suffisante. Il n'y a évidemment pas lieu de vous inquiéter, la procédure se contente d'extraire le message de la mémoire (30 caractères à partir de l'adresse 13713) et n'a aucune influence sur la mémoire centrale disponible. ?to message >make "i 0 >repeat 30 > [type char .examine 13713 + :i make "i :i + 1] >end message defined Remarquez au passage l'utilisation de la fonction type, qui permet d'afficher les différents caractères de manière consécutive, c'est-à-dire sans produire de retour chariot et de saut de ligne comme l'aurait fait la primitive pr. La version 2 du Logo disponible sur CPC-6128 offre la possibilité d'utiliser les primitives .in et .out. Elles sont comparables aux instructions BASIC INP et OUT, et permettent d'adresser directement les ports d'entrées/ sorties du système. Cependant, la structure particulière de l'Amstrad ne permet pas l'utilisation de ces deux primitives. En effet, elles ne peuvent adresser que 256 ports différents et, grâce à une astuce technique, l'Amstrad en possède 65535. Oublions donc ces deux primitives totalement inutiles. - .contents est la dernière primitive précédée du signe "." que nous étudierons. Elle fournit sous forme de liste (au sens Logo du terme) la totalité des mots constituant l'espace de travail du système (primitives, procédures...). Extensions du langage --------------------- Ce chapitre est réservé aux utilisateurs des CPC-464 et 664, ainsi qu'aux utilisateurs de CPC-6128 utilisant la version 1 du Logo. (ROCHE> Sous CP/M 2.2, pas CP/M Plus.) Si vous possédez une bonne expérience du langage machine et de l'écriture de programmes en Assembleur, vous pourrez étendre votre Logo en lui ajoutant de nouvelles primitives, ou en modifiant le fonctionnement des primitives existantes. Rappel: pour obtenir l'adresse de la routine de traitement interne d'une primitive, il suffit d'écrire: gprop "primitive ".PRM, où primitive représente le nom de la primitive à rechercher. Pour affecter une nouvelle adresse à une primitive (ancienne ou nouvelle), il suffit d'écrire: pprop "primitive ".PRM adresse, où primitive représente le nom de la primitive à modifier ou à créer, et adresse la valeur de la nouvelle adresse (un nombre). Exemples: gprop "make ".PRM fournit une adresse qui dépend de la version utilisée. pprop "make ".PRM 45000 réaffecte la routine de traitement de la primitive make à l'adresse 45000, où il n'y a rien pour l'instant. Pour allouer l'espace supérieur de la mémoire et le protéger des accès intempestifs de votre CP/M, il faut procéder à une reconfiguration de votre Système d'Exploitation de Disques au moyen de la commande MOVCPM. MOVCPM.COM est un utilitaire standard du CP/M fourni par Digital Research. En standard, le CP/M est configuré pour utiliser 179 blocks de 256 octets soit 45824 octets, et l'adresse supérieure de la mémoire utilisable vaut 0AD32H. Il faut tout d'abord déterminer la taille exprimée en blocs de votre routine. Exemple: votre routine occupe 1500 octets. Elle est donc mémorisable sur 1500/256 blocs, soit 5.86 blocs. Comme on ne peut configurer que des blocs entiers, votre routine occupera donc 6 blocs. Le nombre de blocs utilisables par le CP/M sera donc amené à 179-6, ou 173 blocs. Cette réduction de l'espace adressable est réalisé grâce à la commande CP/M: MOVCPM 173. L'adresse supérieure du CP/M est dès lors portée à 0A732H, et votre routine Assembleur doit commencer à partir de cette adresse. La commande SYSGEN permet de sauvegarder de façon permanente la nouvelle configuration du CP/M. Simulation des primitives de la version 2 ----------------------------------------- Au cours des deux premiers articles, nous avons eu l'occasion de simuler quelques primitives de la version 2 à l'aide de primitives de la version 1. Ainsi, les possesseurs de CPC-6128 ne sont plus les seuls à pouvoir utiliser thing, last, pops, pons, poall, edall, et define. Et, pour compléter votre collection, voici: - erall qui supprime toutes les procédures et toutes les variables de l'espace de travail. Cette procédure pose un problème car, comme erall est elle-même une procédure, elle s'efface d'elle-même, ce qui a pour effet de 'planter' le Logo. Il faut donc se résoudre à effacer tout, sauf erall elle-même. ?to erall >if count glist ".APV = 0 > [] > [ern glist ".APV] >local "prc >make "prc glist ".DEF >label "boucle >if count :prc = 0 > [op "] >if first :prc = "erall > [make "prc bf :prc] > [remprop first :prc ".DEF make "prc bf :prc] >go "boucle >end - home qui ramène la tortue à sa position de départ au centre de l'écran. ?to home >seth 0 >setpos [0 0] >end - listp qui fournit la valeur TRUE si l'objet spécifié est une liste, et la valeur FALSE sinon. ?to listp :mot >if wordp :mot > [op "FALSE] > [op "TRUE] >end - lput qui ajoute le premier argument à la suite du second pour former un nouvel objet. ?to lput :obj1 :obj2 >if wordp :obj2 > [op word :obj2 :obj1] >if wordp :obj1 > [op se :obj2 :obj1] > [op se :obj2 (list :obj1)] >end Exemples: lput "toto "tutu fournit tututoto. lput "toto [titi tata] fournit [titi tata toto]. lput [1 2][3 4] fournit [3 4[1 2]]. - memberp qui nécessite deux arguments et fournit la valeur TRUE si le premier est un élément du second. Si ce n'est pas le cas, la primitive fournit évidemment la valeur FALSE. ?to memberp :element :liste >local "lg >make "lg count :element >if and (:lg > 1) (wordp :liste) > [go "special] >if 0 = count :liste > [op "FALSE] >if :element = first :liste > [op "TRUE] > [op memberp :element bf :liste] >label "special >local "l2 >make "l2 :liste >repeat (count :liste) - 1 > [if :element = piece 1 :lg :l2 > [op "TRUE] > [make "l2 bf :l2]] >op "FALSE >end Remarque: cette définition utilise la procédure piece définie ci-dessous. - piece qui fournit les parties d'objet situées entre le début et la fin spécifiés. Cette instruction est comparable à la fonction BASIC MID$. Syntaxe: piece debut fin objet. ?to piece :debut :fin :objet >local "nb >make "nb (count :objet) - :fin >repeat :debut - 1 > [make "objet bf :objet] >repeat :nb > [make "objet bl :objet] >op "objet >end Exemples: piece 3 5 "balthasart fournit lth. piece 3 5 [1 2 3 4 5] fournit [3 4 5]. - quotient qui fournit le quotient entier de la division du premier argument par le second. ?to quotient :n1 :n2 >op int (:n1 / :n2) >end Exemple: quotient 22 7 fournit 3. - remainder qui fournit le reste de la division entière du premier argument par le second. ?to remainder :n1 :n2 >op :n1 - ( (int :n1 / :n2) * :n2) >end Exemple: remainder 22 7 fournit 1 (3 * 7 + 1). - round qui arrondit le nombre spécifié. ?to round :n >op int (:n + 0.5) >end Exemples: round 2.3 fournit 2, et round 2.7 fournit 3. - where qui fournit l'emplacement du premier paramètre dans le second. Cette primitive est comparable à la fonction BASIC INSTR. ?to where :element :liste >(local "lg "cpt "l2) >make "lg count :element >make "cpt 1 >make "l2 :liste >if and (:lg > 1) (wordp :liste) > [go "special] >label "boucle >if 0 = count :l2 > [op 1] >if :element = first :l2 > [op :cpt] > [make "cpt :cpt + 1 make "l2 bf :l2 go "boucle] >label "special >repeat (count :liste) - 1 > [if :element = piece 1 :lg :l2 > [op :cpt] > [make "l2 bf :l2 make "cpt :cpt + 1]] >op 0 >end Remarque: cette primitive utilise la primitive piece définie ci-dessus. Exemples: where "a "local fournit 4. where 34 [22 25 45 35 67 58 34 67] fournit 7. Il reste quelques primitives à simuler. Alors, amateurs à vos claviers! Les meilleures seront publiées dans cette revue. Pour terminer cette section, voici une primitive qui ne fait pas partie de la version 3, mais dont l'utilité est indéniable. Elle permet de réaliser les boucles contrôlées. La primitive repeat réalise des boucles dont la durée est fixe et déterminée par le nombre d'itérations spécifié. La procédure loop que nous vous proposons permet de réaliser des boucles beaucoup plus fines. Sa syntaxe est relativement complexe. Il faut préciser la variable qui est utilisée comme indice de boucle, la valeur de départ de la boucle, la valeur de fin de boucle, et la liste d'instructions à réaliser dans le coeur de la boucle. Remarque: cette procédure utilise une nouvelle primitive (run) qui exécute le contenu d'une liste en considérant que cette liste est une suite d'instructions. ?to loop :varbouc :valdep :valfin :instruction >if :valdep > :valfin > [stop] >make :varbouc :valdep >run :instruction >loop :varbouc :valdep + 1 :valfin :instruction >end Exemple: loop "i 5 15 [pr :i] Il est évidemment possible de réaliser une boucle qui décompte (STEP -1). ?to deloop :varbouc :valdep :valfin :instruction >if :valdep < :valfin > [stop] >make :varbouc :valdep >run :instruction >deloop :varbouc :valdep - 1 :valfin :instruction >end Initiation au calcul matriciel ------------------------------ Comme chacun le sait, chaque médaille présente un revers et, si le Logo est particulièrement riche et flexible, il est aussi très gourmand en mémoire et d'une lenteur navrante. Les langages classiques (BASIC, Pascal, COBOL) abhorrent les opérations de calcul matriciel. En effet, celles-ci nécessitent un très grand nombre d'opérations. Dans la panoplie des langages, il n'y a guère que l'APL, et dans une moindre mesure le FORTRAN, qui se sentent à l'aise dans de telles opérations. Nous vous proposons donc une série de procédures dont les concepts sont directement inspirés du langage APL. Elles vous permettront de réaliser des opérations sur les matrices avec des temps de réponse raisonnables. Notions de matrice ------------------ Une matrice est un tableau de nombres, et constitue une table d'entrée. Pour simplifier les procédures et le raisonnement, nous nous limiterons aux tableaux à deux dimensions au plus. Voici quelques exemples de matrices: a) 1 5 7 9 2 3 14 45 6 12 24 36 b) 1 2 3 4 5 7 2 1 0 c) 1 4 5 9 13 d) 4 La matrice a est une matrice à deux dimensions de trois lignes et de quatre colonnes (matrice de 3 sur 4). La matrice b est une matrice à deux dimensions de trois lignes et de trois colonnes (matrice de 3 sur 3). Cette matrice est appelée matrice carrée. La matrice c est une matrice à une dimension de cinq colonnes. Une matrice à une dimension est aussi appelé vecteur. La matrice d est aussi une matrice à une dimension d'un élément. C'est un vecteur, et c'est aussi une matrice carrée. Considérons les deux matrices suivantes: a) 1 2 3 4 5 6 b) 1 4 2 5 3 6 Ces deux matrices sont semblables (matrice 2 sur 3), mais a est une matrice de deux lignes et trois colonnes, et b est une matrice de trois lignes et deux colonnes. a et b sont dite transposées l'une de l'autre. Dans la plupart des langages informatiques (comme le BASIC), la notion de matrice n'existe pas, et seule la notion d'éléments de tableau existe [DIM a(10,10) et a(5,3)]. On ne peut pas se référer à la matrice au moyen de la seule lettre génératrice (a), il faut spécifier l'élément choisi [a(x,y)]. Approche du problème en Logo ---------------------------- En Logo, l'utilisation des listes va nous permettre de simuler la gestion matricielle. Une des premières idées qui vient à l'esprit est d'affecter (make) à une variable une liste dont chacune des lignes est une liste. La matrice a de l'exemple précédent s'écrira alors: ?make "a [[1 2 3][4 5 6]] La matrice b s'écrira: ?make "b [[1 4][2 5][3 6]] Cette méthode simple n'est cependant pas très efficace, et ne mène pas très loin. Une méthode bien meilleure réside dans l'utilisation des propriétés. Dans ce cas, il suffit d'encoder la matrice comme une seule liste d'éléments, et de définir une propriété appelée dim (pour dimension) qui spécifie le format de la matrice (ligne et colonne) et ce, au moyen d'une procédure appelée taille. Ainsi, les matrices a et b s'encoderont de la même façon: make "a [1 2 3 4 5 6] make "b [1 2 3 4 5 6] Seule l'affectation de la taille permettra de les différencier. taille "a [2 3] : 2 lignes et 3 colonnes. taille "b [3 2] : 3 lignes et 2 colonnes. La procédure taille se définissant de la façon suivante: ?to taille :nom :dimension >pprop :nom ".dim :dimension >end Le premier paramètre de la procédure taille est le nom de l'objet qui doit recevoir la propriété. Le second paramètre est la valeur de cette propriété, et la primitive pprop définit la propriété comme étant .dim. La procédure complète de définition de matrice peut s'écrire: ?to mat :nom :apv :dim >make :nom :apv >taille :nom :dim >end Exemple: pour définir la matrice demo qui contient deux lignes et quatre colonnes: 1 4 8 16 3 6 9 27 Il suffit d'écrire: mat "demo [1 4 8 16 3 6 9 27][2 4]. Quelques procédures de manipulations matricielles ------------------------------------------------- Voici quelques procédures qui vous permettront d'effectuer vos premières manipulations de matrices. Vous pourrez par la suite y ajouter vos propres procédures d'addition, de multiplication, ou même d'inversion. Remarque: les procédures utilisent les commandes loop, deloop, et remainder décrites au début de cet article. Commençons par une procédure simple qui permet de copier une matrice dans une autre. ?to mategal :nouvelle :ancienne >make :nouvelle gprop :ancienne ".APV >taille :nouvelle gprop :ancienne ".dim >end La première ligne affecte à la nouvelle variable le contenu de l'ancienne pour la propriété système .APV (autrement dit, le contenu de l'affectation par make). La seconde ligne affecte à la nouvelle variable le contenu de la propriété .dim de l'ancienne variable. Exemple: pour copier la matrice demo définie ci-dessus dans la matrice image, il suffit d'écrire: mategal "image "demo. La procédure elem permet 'extraire un élément quelconque d'une matrice en spécifiant sa ligne et sa colonne. ?to elem :nom :lig :col >(local "ctr "lc "apv) >make "apv gprop :nom ".APV >make "lc item 2 gprop :nom ".dim >make "ctr 1 + remainder ( (:lig - 1) * :lc + :col -1)(count :apv) >op item :ctr :apv >end Le premier make affecte le contenu de la liste de la matrice spécifiée dans la variable apv. Le deuxième make affecte le nombre de colonnes de la matrice dans la variable lc. Le troisième affecte la position réelle de l'élément recherché dans la variable ctr. Exemple: elem "demo 2 2 fournit la valeur 6. A l'aide de la procédure elem, nous pouvons définir deux nouvelles procédures. La procédure colonne permet d'extraire une colonne précise d'une matrice, et la présente sous forme de liste. La procédure ligne réalise la même fonction, mais pour une ligne précise de la matrice. ?to colonne :nom :col >(local "ctr "temp) >make "ctr first gprop :nom ".dim >make "temp [] >deloop "ctr :ctr 1 [make "temp se (elem :nom :ctr :col) :temp] >op :temp >end ?to ligne :nom :lign >(local "ctr "temp) >make "ctr item 2 gprop :nom ".dim >make "temp [] >deloop "ctr :ctr 1 [make "temp se (elem :nom :lign :ctr) :temp] >op :temp >end La procédure matout permet d'afficher la matrice à l'écran en fonction de sa taille (de sa propriété .dim). Elle utilise la procédure loop qui a été définie dans la section précédente. Grâce à toutes ces procédures, nous pouvons réaliser une nouvelle procédure qui permet de représenter une matrice dans un format classique. ?to matout :nom >(local "lign "var) >make "lign first gprop :nom ".dim >loop "var 1 :lign [pr ligne :nom :var] >end Pour terminer, voici trois procédures qui permettent de réaliser la transposée d'une matrice. ?to transval :nom >(local "rc "temp) >make "rc gprop :nom ".dim >make "temp [] >loop "rc 1 (item 2 :rc [make "temp se :temp colonne :nom :rc] >op :temp >end ?to transtail :nom >local "var >make "var gprop :nom ".dim >op se item 2 :var item 1 :var >end ?to transpose :nouvelle :ancienne >make :nouvelle transval :ancienne >taille :nouvelle transtail :ancienne >end Exemple: mat "normal [1 2 3 4 5 6][2 3] matout "normal transpose "inv "normal matout "inv Cette notion aura permis au lecteur de découvrir à quel point une application Logo est constituée de procédures qui s'enchevêtrent les unes aux autres, ainsi que le nombre important de procédures nécessaires pour réaliser des fonctions relativement simples. Approche de l'intelligence artificielle --------------------------------------- Nous voici au terme de cette série d'articles sur le langage Logo et ses applications. Comme promis dès le premier chapitre, nous terminerons par une introduction aux langages de la cinquième génération. Voici donc quelques procédures qui permettront de rapprocher le Logo du langage PROLOG. Avant tout, faisons quelques remarques. Une relation est définie par trois éléments: le premier est appelé sujet de la relation, le second caractérise la relation et est appelé relation ou verbe, et le troisième est appelé objet. Pour des raisons évidentes de commodité et de simplification des procédures, il est indispensable d'utiliser un langage "petit nègre" pour définir une relation. Les verbes doivent toujours être à la même personne. Nous ne pouvons que vous conseiller l'emploi de l'infinitif ou de la troisième personne du singulier. De plus, ils doivent se composer d'un seul mot. Si vous désirez utiliser plusieurs mots, il faut les relier entre eux par un caractère quelconque (nous vous recommandons l'utilisation du signe souligné, qui se trouve sur la même touche que le zéro). Le sujet doit lui aussi être composé d'un seul mot. L'objet ne subit pas de restriction, mais nous vous invitons à utiliser le moins d'articles inutiles possibles. Exemple de relation correcte: Anne aimer Jean, Daniel mange frites. Premier pas ----------- Définir une relation telle qu'elle est décrite ci-dessus ne pose aucun problème. Il suffit d'utiliser la primitive pprop. Nous allons donc construire pas-à-pas une procédure conversationnelle appelée creer, qui permettra de saisir les relations. ?to creer >(local "sujet "verbe "objet) >pr [Entrez le sujet de la relation] >make "sujet rq >pr [Entrez le verbe de la relation] >make "verbe rq >pr [Entrez l'objet de la relation] >make "objet rl >pprop :sujet :verbe :objet >pr [Relation enregistrée] >end Exemple: ?creer Entrez le sujet de la relation anne Entrez le verbe de la relation aimer Entrez l'objet de la relation les enfants Relation enregistrée ?plist "anne fournira [aimer [les enfants]]. Ajoutez une nouvelle relation: anne manger pomme. ?plist "anne fournira [manger [pomme] aimer [les enfants]]. Améliorations ------------- La procédure décrite est très simple, mais elle présente cependant un gros défaut. En effet, si vous encodez une relation existante (couple sujet-verbe) portant sur un nouvel objet, la relation avec l'objet précédent est perdue. Exemple: creer anne manger des frites détruira l'objet pomme de l'exemple précédent. Pour palier cet inconvénient, il suffit de modifier la procédure creer de la façon suivante: ?to creer >(local "sujet "verbe "objet "ancien) >pr [Entrez le sujet de la relation] >make "sujet rq >pr [Entrez le verbe de la relation] >make "verbe rq >pr [Entrez l'objet de la relation] >make "objet rl >make "ancien gprop :sujet :verbe >if emptyp :ancien > [pprop :sujet :verbe list ":objet] > [pprop :sujet :verbe fput :objet :ancien] >pr [Relation enregistrée] >end Exemple: Créez les relations suivantes: Amstrad possède trois modes graphiques Amstrad possède un écran Amstrad est beau Amstrad est bon marché Amstrad est génial gprop "Amstrad "est fournira [[génial][bon marché][beau]]. Autrement dit, une liste dont chaque objet est lui-même une liste. plist "Amstrad fournira [est [[génial][bon marché][beau]] possède [[un écran][trois modes graphiques]]]. Autrement dit, une liste composée de mots (les verbes) associés à une liste d'objets, chaque objet étant lui-même une liste. Arrivé à ce point, il existe toujours un problème. En effet, si, par inadvertance, vous encodez deux fois la même relation, la procédure créera deux objets. Pour palier à ce problème, il suffit d'ajouter une ligne de test entre la ligne make "ancien et la ligne if emptyp :ancien. >make "ancien gprop :sujet :verbe >if memberp :objet :ancien > [pr [Relation déjà connue] stop] >if emptyp :ancien > [pprop :sujet :verbe list ":objet] > [pprop :sujet :verbe fput :objet :ancien] Remarque: la primitive memberp ne fait pas partie de la première version du Logo (CP/M 2.2), mais vous trouverez une procédure de simulation de cette primitive dans une des sections précédentes. Lorsqu'une relation est définie, il existe souvent une relation inverse, si A aime B alors B est aimé par A. Notre Prologo, si on peut l'appeler comme ça, doit pouvoir construire la relation inverse en permutant le sujet et l'objet, et en modifiant le verbe. Remarque: le langage ne peut pas, à ce stade, traiter tout seul la création de l'inverse. Pour assurer une création cohérente de la relation inverse, ajoutez les lignes suivantes à la fin de la procédure creer. La procédure permet alors de créer les relations inverses. Si votre assertion n'a pas d'inverse, il suffit de répondre par un simple retour chariot à la question "Entrez le verbe inverse". >pr [] >pr [Création de la relation inverse] >pr [Entrez le verbe inverse] >make "verbe rq >if emptyp :verbe > [stop] >pr [] >if count :objet > 1 > (pr [Quel est le mot principal dans l'objet] :objet) > [make "objet rq] > [make "objet first :objet] >make "ancien gprop :objet :verbe >if memberp :sujet :ancien > [pr [Relation déjà connue] stop] >if emptyp :ancien > [pprop :objet :verbe list ":sujet] > [pprop :objet :verbe fput :sujet :ancien] >pr [Relation inverse enregistrée] Exploitation ------------ Grâce à la procédure creer, nous sommes en mesure de saisir des relations. Il faut maintenant créer une procédure générale de recherche dans la base de donnée. La recherche doit pouvoir se faire en connaissant le couple sujet et verbe (relation), le verbe seul, ou le sujet seul. 1) Procédure générale d'affichage d'une série de propositions en fonction d'une liste d'objets fournie. ?to affichage :sujet :verbe :listobj >local "premobj >if emptyp :listobj > [stop] >make "premobj first :listobj >if emptyp :premobj > [] > [(pr :sujet :verbe :premobj)] >affichage :sujet :verbe bf :listobj >end Exemple: ?affichage "anne "aime [[les frites][les pommes][jules]] fournit: anne aime les frites anne aime les pommes anne aime jules La procédure de recherche de toutes les relations lorsque un couple sujet- verbe est connu s'écrit très facilement. ?to rechsuve >(local "sujet "verbe) >ct >pr [Entrez le sujet de la relation] >make "sujet rq >pr [Entrez le verbe de la relation] >make "verbe rq >affichage :sujet :verbe gprop :sujet :verbe >end La recherche de toutes les relations portant sur un verbe est un peu plus complexe. Elle nécessite la création d'une procédure récurrente intermédiaire appelée traitement. Rappel: la primitive glist "verbe fournit une liste de tous les sujets qui portent sur le verbe précisé. ?to recverb >(local "verbe "liste) >ct >pr [Entrez le verbe de votre relation] >make "verbe rq >make "liste glist :verbe >traitement :verbe :liste >end ?to traitement :verbe :liste >(local "sujet "objet) >if emptyp :liste > [stop] >make "sujet first :liste >make "objet gprop :sujet :verbe >affichage :sujet :verbe :objet >traitement :verbe bf :liste >end La recherche de toutes les relations portant sur un même sujet est encore plus complexe et, dans un but de simplification, nous limiterons la procédure aux verbes se composant d'un seul mot. Si vous avez établi des relations dont le verbe est composé de plusieurs mots, la procédure sortira en erreur. Rappel: la primitive plist "sujet fournit une liste composée de tous les verbes et tous les objets en rapport avec le sujet spécifié. ?to recsuj >(local "sujet "liste) >ct >pr [Entrez le sujet de votre relation] >make "sujet rq >make "liste plist :sujet >traitsuj :sujet :liste >end ?to traitsuj :sujet :liste >(local "verbe "objet) >if emptyp :liste > [stop] >make "verbe first :liste >make "objet gprop :sujet :verbe >affichage :sujet :verbe :objet >traitsuj :sujet bf bf :liste >end Voilà, il reste à regrouper ces procédures dans un menu général appelé recherche. ?to recherche >local "choix >ct >pr [Recherche dans la base de données] >pr [] >pr [1 -- Recherche par sujet] >pr [2 -- Recherche par verbe] >pr [3 -- Recherche par couple verbe-sujet] >pr [4 -- Sortie] >pr [] >pr [Votre choix] >make "choix rc >if :choix = 1 [recsuj] >if :choix = 2 [recverb] >if :choix = 3 [rechsuve] >if :choix = 4 [stop] >recherche >end Nous voici arrivés au terme de cette étude du Logo. Nous espérons vous avoir donné un bon aperçu de la puissance et des limites de ce langage. Nous vous invitons à compléter les programmes décrits dans cette série d'articles. Les notions vues sur le Prologo ne sont que la toute première partie d'une série de procédures qui vous conduiront vers un système de gestion de cinquième génération. Alors, amateurs à vos claviers! Nous publierons les meilleures réalisations. Daniel MARTIN EOF