Maple Règles et fonctions essentielles
Springer Paris Berlin Heidelberg New York Hong Kong Londres Milan Tokyo
Nico...
210 downloads
1726 Views
1MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
Maple Règles et fonctions essentielles
Springer Paris Berlin Heidelberg New York Hong Kong Londres Milan Tokyo
Nicolas Puech
Maple Règles et fonctions essentielles
Nicolas Puech TELECOM ParisTech Département INFRES 46, rue Barrault 75013 Paris
ISBN 13 : 978-2-287-25201-3 Springer Paris Berlin Heidelberg New York © Springer-Verlag France 2009 Imprimé en France Springer-Verlag France est membre du groupe Springer Science + Business Media
Cet ouvrage est soumis au copyright. Tous droits réservés, notamment la reproduction et la représentation, la traduction, la réimpression, l’exposé, la reproduction des illustrations et des tableaux, la transmission par voie d’enregistrement sonore ou visuel, la reproduction par microfilm ou tout autre moyen ainsi que la conservation des banques données. La loi française sur le copyright du 9 septembre 1965 dans la version en vigueur n’autorise une reproduction intégrale ou partielle que dans certains cas, et en principe moyennant les paiements des droits. Toute représentation, reproduction, contrefaçon ou conservation dans une banque de données par quelque procédé que ce soit est sanctionnée par la loi pénale sur le copyright. L’utilisation dans cet ouvrage de désignations, dénominations commerciales, marques de fabrique, etc., même sans spécification ne signifie pas que ces termes soient libres de la législation sur les marques de fabrique et la protection des marques et qu’ils puissent être utilisés par chacun. La maison d’édition décline toute responsabilité quant à l’exactitude des indications de dosage et des modes d’emplois. Dans chaque cas il incombe à l’usager de vérifier les informations données par comparaison à la littérature existante.
Maquette de couverture : Jean-François MONTMARCHÉ Mise en page : Nicolas PUECH
Collection IRIS Dirigée par Nicolas Puech Ouvrages parus : – Méthodes numériques pour le calcul scientifique. Programmes en Matlab A. Quarteroni, R. Sacco, F. Saleri, Springer-Verlag France, 2000 – Calcul formel avec MuPAD F. Maltey, Springer-Verlag France, 2002 – Architecture et micro-architecture des processeurs B. Goossens, Springer-Verlag France, 2002 – Introduction aux mathématiques discrètes J. Matousek, J. Nesetril, Springer-Verlag France, 2004 – Les virus informatiques : théorie, pratique et applications É. Filiol, Springer-Verlag France, 2004 – Introduction pratique aux bases de données relationnelles. Deuxième édition A. Meier, Springer-Verlag France, 2006 – Bio-informatique moléculaire. Une approche algorithmique P.A. Pevzner, Springer-Verlag France, 2006 – Algorithmes d’approximation V. Vazirani, Springer-Verlag France, 2006 – Techniques virales avancées É. Filiol, Springer-Verlag France, 2007 – Codes et turbocodes C. Berrou, Springer-Verlag France, 2007 – Introduction à Scilab. Deuxième édition J.P. Chancelier, F. Delebecque, C. Gomez, M. Goursat, R. Nikouhah, S. Steer, Springer-Verlag France, 2007 – Les virus informatiques : théorie, pratique et applications. Deuxième édition É. Filiol, Springer-Verlag France, 2009
À Brigitte, Catriona, Charles, Guido, Nathalie, Sophie, Sylvie et tous les autres
Avant-propos « Rien de plus original, rien de plus “soi” que de se nourrir des autres. Mais il faut les digérer. Le lion est fait de mouton assimilé. » Paul Valéry, Choses tues
Proposer un nouvel ouvrage traitant du logiciel Maple peut paraître présomptueux. En effet, la consultation du site web de la société Maplesoft1 — qui développe et diffuse le logiciel — permet de dénombrer, toutes langues de publication confondues, une bibliographie riche de plus de trois cents titres en rapport avec Maple. Ces livres, surtout rédigés en anglais, abordent principalement des applications de Maple à un domaine scientifique particulier. En revanche, fort peu présentent les principes fondamentaux de l’utilisation du logiciel, du fonctionnement de l’interpréteur et du langage de programmation. Depuis la commercialisation de Maple, de nombreuses versions du logiciel se sont succédé. Si une nouvelle version ne change pas fondamentalement la pratique du calcul formel, elle apporte de nouvelles bibliothèques de fonctions, des améliorations des interfaces utilisateur et parfois de nouvelles règles syntaxiques. Au fil du temps, les changements se sont accumulés. De nouveaux types de données plus efficaces sont apparus. Des commandes ont été étendues ou modifiées. La syntaxe et certaines règles de programmation ont évolué. Maple propose désormais un langage de programmation impératif moderne et complet, dont les règles et le fonctionnement sont conformes aux standards en la matière (typage et filtrage des paramètres, règles de visibilité des variables globales et locales, délimiteurs de structures de contrôle, outils pour le débogage), le tout s’accompagnant de types et de structures de données souples et puissants. En général, une version de Maple est capable de lire un document (feuille de travail) réalisé dans une autre version plus ancienne et de le convertir au format le plus récent. En revanche, la plupart des ouvrages présentent, par la force des choses, 1
http://www.maplesoft.com.
x
Maple : règles et fonctions essentielles
des exemples à la syntaxe partiellement obsolète, ou qui n’exploitent pas les avantages de la syntaxe actuelle. L’environnement de travail et le langage de programmation très souples proposés par Maple procurent un agrément d’utilisation appréciable. En particulier, l’interpréteur n’oblige pas à déclarer ou à typer les variables utilisées. Écrire des instructions ou un programme syntaxiquement corrects mais dont la sémantique n’est pas conforme aux intentions de l’utilisateur, s’avère ainsi (trop) facile. De même, Maple met à disposition des structures de données puissantes dont certaines, comme les séquences ou les tables, suivent des règles de fonctionnement souvent mal maîtrisées. La méconnaissance de quelques principes peut provoquer des erreur ou empêcher l’utilisateur de traiter certaines questions d’apparences simples. Il convient donc de détailler explicitement les principes et les règles de fonctionnement de Maple, afin de battre en brèche certaines idées reçues — en particulier celle, assez répandue, selon laquelle Maple ne permettrait pas d’écrire convenablement des programmes. Cet ouvrage a pour objectif d’exposer, en français, et de manière synthétique, les règles et principes essentiels qui régissent le fonctionnement de l’interpréteur et du langage de programmation de Maple ; les exemples proposés utilisent les règles syntaxiques de la version 12 du logiciel, soit la plus récente à la date de la rédaction. La présentation adoptée dans ce livre se nourrit de l’expérience pédagogique de l’auteur auprès de différents publics : classes préparatoires aux grandes écoles scientifiques, IUT, Grande École, formation continue. L’exposé introduit progressivement les notions élémentaires nécessaires à la compréhension des thèmes abordés ; ainsi s’adresse-t-il aussi bien aux débutants qu’aux utilisateurs familiers de l’informatique ou de Maple. Le premier chapitre livre les informations essentielles à la prise en main du logiciel. Il décrit les caractéristiques de l’interface classique (classic interface), de la feuille de travail et de l’interpréteur de commandes. Le lecteur pourra faire ses premiers pas avec Maple grâce à quelques exemples simples, et aborder les notions de variable et d’affectation, l’aide en ligne ainsi que l’organisation de Maple en bibliothèques. Le débutant consultera avec profit la liste des erreurs fréquentes. Le chapitre 2 présente les types fréquemment utilisés et dont il convient de connaître les propriétés fondamentales. Sont étudiées les constantes, les différents types de nombres, les booléens, les types collectifs pouvant décrire des énumérations d’objets — notamment les séquences qui présentent des propriétés particulières, les ensembles et les listes —, les chaînes de caractères et enfin les tables avec les tableaux et les matrices qui en dérivent.
Avant-propos
xi
L’évaluation de variables se produit en maintes circonstances : affectation, appel de fonction ou de commande Maple, demande explicite de l’utilisateur ou occurrence lors de l’exécution d’un programme. Dans le chapitre 3, nous détaillons les différentes règles d’évaluation de Maple et les illustrons par des exemples. La connaissance de ces règles est indispensable pour la maîtrise du logiciel. Le chapitre 4 présente les éléments de syntaxe et les règles essentielles qui concernent la programmation avec Maple. Nous commençons par décrire les structures de contrôle, les possibilités de typage et de filtrage des paramètres et les règles de visibilité régissant les variables locales ou globales. Nous insistons sur le processus mis en œuvre lors de l’appel d’une fonction, dont la compréhension est essentielle pour éviter certaines erreurs dans l’écriture de fonctions. Nous abordons aussi la gestion d’un nombre variable d’arguments et le passage d’arguments par variable ainsi que l’écriture de fonctions récursives et de fonctions capables de produire un retour non évalué. Le chapitre se termine par une présentation des commandes permettant le traitement des entrées/sorties au sein d’une fonction. Le cinquième et dernier chapitre détaille la représentation interne d’une expression Maple. La structure arborescente sous-jacente à toute variable est détaillée pour les expressions algébriques usuelles ainsi que pour la plupart des types courants. La connaissance de cette représentation interne permet de maîtriser les outils que Maple met à disposition pour effectuer des transformations d’expressions (substitution de sous-expressions, conversion de types) ; elle permet aussi une utilisation particulièrement efficace de certaines commandes.
La description des commandes les plus courantes et de leur bonne utilisation selon un regroupement thématique (analyse, algèbre linéaire, arithmétique, géométrie, représentations graphiques, évocation des packages existants, création de nouveaux packages, entrées/sorties et formats de fichiers Maple, programmation avancée et débogage. . .) n’est pas abordée dans ce volume. Elle pourrait faire l’objet d’un ouvrage qui accompagnerait utilement celui-ci. Nous ne détaillons pas non plus les interfaces et outils (interface « standard », Maplets. . .) que propose Maple dans ses versions récentes. Ils permettent d’intégrer des applications réalisées avec Maple aux environnements les plus modernes, mais ces questions relèvent davantage de la présentation et de l’intégration logicielle que du calcul formel à proprement parler. Compte tenu de l’importance croissante qu’ils acquièrent dans le développement du logiciel et des possibilités de diffusion qu’ils procurent, ces nouveaux outils mériteraient qu’on leur consacre aussi un ouvrage à part entière.
xii
Maple : règles et fonctions essentielles
Comme nous l’avons indiqué en préambule, de nombreuses publications traitent, de près ou de loin, de l’utilisation de Maple. Il nous paraît essentiel qu’un public, fût-il composé d’anglophones éclairés, puisse accéder à des ouvrages scientifiques rédigés dans sa langue maternelle. Aussi, la bibliographie de ce présent livre, destiné à une collection en langue française, contient-elle principalement des titres francophones.
Afin de permettre au lecteur de mettre en œuvre les exemples proposés et de découvrir plus complètement Maple, l’acquisition de cet ouvrage donne accès à une licence pour utiliser le logiciel. Le CD qui accompagne le livre fournit les moyens d’installer une version de Maple 12 Student qui sera utilisable pour une durée d’un an à partir de sa date de validation2 .
Pour terminer, je voudrais remercier toutes les personnes qui ont nourri ma réflexion. Merci donc aux développeurs du logiciel qui ont répondu à mes premières questions, à ceux qui ont fait connaître ce logiciel par le biais de polycopiés ou de présentations, à une époque où Maple commençait à peine à être reconnu en France. Merci aux auteurs des ouvrages qui figurent dans la bibliographie, à ceux qui ont mis en ligne sur Internet des exposés décrivant certains thèmes. Merci aux différents collègues et étudiants que j’ai côtoyés, qu’ils aient proposé un exercice intéressant que j’ai pu reprendre à mon compte ou qu’ils aient posé une question pertinente, m’obligeant ainsi à approfondir ma compréhension des règles de fonctionnement du logiciel. Je suis bien incapable aujourd’hui de dresser la liste précise de mes dettes. J’espère que j’ai suffisamment bien digéré les idées qui m’ont inspiré, de sorte que le présent ouvrage puisse à son tour en nourrir d’autres ou, à défaut, fasse de ses lecteurs des utilisateurs avisés de Maple.
J’exprime ma profonde gratitude aux inlassables et fidèles soutiens de mes projets éditoriaux que sont Sylvie Ballestra, Patricia Chwat et Bruno Petazzoni. Ils m’ont permis d’améliorer considérablement la qualité de cet ouvrage. Je remercie Benoît Vidalie et Nicolas Gachadoit de Maplesoft France pour leur disponibilité et pour l’assistance technique qu’ils m’ont apportée lors de la rédaction de cet ouvrage. Je tiens à exprimer ma reconnaissance à l’École nationale supérieure des télécommunications (ENST, alias TELECOM ParisTech dans sa dénomination moderne) et sa tutelle, l’Institut TELECOM, de m’avoir accordé 2
Se reporter aux instructions d’installation, page 205.
Avant-propos
xiii
un congé pour études qui m’a permis de rédiger ce livre. À cet égard, je remercie particulièrement Henri Maître, Directeur adjoint de l’École et Michel Riguidel, chef du département Informatique et Réseaux, pour leur confiance et le soutien qu’ils m’ont apporté. J’aimerais aussi saluer les collègues qui, à l’ENST, m’entourent de leur humour et de leur compétence au quotidien, m’offrant un environnement humain chaleureux si précieux : Laurent Decreusefond, Jean Leneutre, Philippe Martins, Alain Maruani, Myriam Nora-Davidovici, Tullio Tanzi, Sylvie Vignes. . . Je remercie aussi l’INRIA, unité de Sophia-Antipolis, notamment l’équipe MASCOTTE qui m’a accueilli lors de mon congé pour études, fournissant un cadre agréable et stimulant à mon séjour. Ma gratitude va, entre autres, à Jean-Claude Bermond, responsable de l’équipe qui a rendu ce séjour possible ainsi qu’à David Coudert, Olivier Dalle, Patricia Lachaume, Hervé Rivano et Michel Syska avec qui j’ai pu avoir de nombreux échanges amicaux. Les éditions Springer me font confiance et soutiennent mes projets éditoriaux depuis de nombreuses années. À l’occasion de la sortie de cet ouvrage, j’éprouve un vif plaisir à rendre hommage aux personnes de Springer France avec qui j’ai travaillé ces derniers temps, notamment Guido ZosimoLandolfo, Nathalie Huilleret, Charles Ruelle, Sophie Guillemot, Sylvie Kamara et Brigitte Juelg, ainsi qu’à Catriona Byrne de Springer Verlag (Heidelberg), qui m’a fait connaître cette grande maison. Je les remercie pour leur patience, leur compétence et leur amitié.
Nicolas Puech, Sophia-Antipolis et Paris, août 2008.
Sommaire Avant-propos
ix
Conventions typographiques 1
Prise en main 1.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . 1.2 En quoi consiste le calcul formel . . . . . . . . . . . . . 1.2.1 Calcul formel et calcul numérique . . . . . . . . . 1.2.2 La puissance du calcul formel . . . . . . . . . . . 1.3 Composants du logiciel . . . . . . . . . . . . . . . . . . 1.3.1 La feuille de travail . . . . . . . . . . . . . . . . . 1.3.2 L’interpréteur . . . . . . . . . . . . . . . . . . . . 1.3.3 Le langage de programmation . . . . . . . . . . . 1.3.4 Les différentes interfaces . . . . . . . . . . . . . . 1.3.5 Versions successives du logiciel . . . . . . . . . . 1.4 Premiers pas avec Maple . . . . . . . . . . . . . . . . . 1.4.1 L’invite . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 Caractères de fin de saisie . . . . . . . . . . . . . 1.4.3 Groupement d’instructions . . . . . . . . . . . . 1.4.4 Commentaires . . . . . . . . . . . . . . . . . . . 1.4.5 Enregistrer son travail . . . . . . . . . . . . . . . 1.5 Variables et affectation . . . . . . . . . . . . . . . . . . 1.5.1 Noms de variables . . . . . . . . . . . . . . . . . 1.5.2 Affectation . . . . . . . . . . . . . . . . . . . . . 1.5.3 Noms de variables fondés sur des lettres grecques 1.5.4 Commandes inertes . . . . . . . . . . . . . . . . 1.5.5 Opérateur dito . . . . . . . . . . . . . . . . . . . 1.5.6 Nettoyage de la mémoire de travail . . . . . . . . 1.5.7 Désaffecter une variable . . . . . . . . . . . . . . 1.6 Utilisation de l’aide en ligne . . . . . . . . . . . . . . . 1.6.1 L’opérateur “ ?” . . . . . . . . . . . . . . . . . . . 1.6.2 Recherche par sujet . . . . . . . . . . . . . . . .
1 . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
3 3 4 4 5 8 8 9 10 10 11 12 12 12 13 15 15 15 16 17 18 21 22 23 24 25 25 25
xvi
Maple : règles et fonctions essentielles 1.6.3 Recherche plein texte . . . . . . . . . . . . . . . . 1.6.4 Autres ressources . . . . . . . . . . . . . . . . . . . Les bibliothèques de fonctions (packages) . . . . . . . . . Erreurs classiques du débutant . . . . . . . . . . . . . . . 1.8.1 Oubli du caractère de fin d’instruction . . . . . . . 1.8.2 Confusion entre égalité et affectation . . . . . . . . 1.8.3 Confusion entre pi et Pi . . . . . . . . . . . . . . . 1.8.4 Confusion entre e et exp(1) . . . . . . . . . . . . . 1.8.5 Confusion entre i et I . . . . . . . . . . . . . . . . 1.8.6 Oubli du caractère “*” dans un produit . . . . . . 1.8.7 Signe d’une constante . . . . . . . . . . . . . . . . 1.8.8 Chronologie d’exécution et présentation matérielle
. . . . . . . . . . . .
25 26 26 29 29 30 31 31 32 32 33 34
Types fondamentaux 2.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . . 2.2 L’arborescence des types de Maple . . . . . . . . . . . . . 2.3 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . 2.3.1 Constantes numériques célèbres connues de Maple 2.3.2 Autres constantes numériques usuelles . . . . . . . 2.3.3 Introduire une nouvelle constante dans le système 2.4 Booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5 Nombres . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.5.1 Nombres entiers relatifs . . . . . . . . . . . . . . . 2.5.2 Nombres rationnels . . . . . . . . . . . . . . . . . . 2.5.3 Radicaux . . . . . . . . . . . . . . . . . . . . . . . 2.5.4 Nombres complexes . . . . . . . . . . . . . . . . . 2.5.5 Nombres à virgule flottante . . . . . . . . . . . . . 2.5.6 Le type numeric . . . . . . . . . . . . . . . . . . . 2.5.7 Maple pratique la surcharge . . . . . . . . . . . . . 2.6 Séquences, listes et ensembles . . . . . . . . . . . . . . . . 2.6.1 Séquences . . . . . . . . . . . . . . . . . . . . . . . 2.6.2 Ensembles . . . . . . . . . . . . . . . . . . . . . . . 2.6.3 Listes . . . . . . . . . . . . . . . . . . . . . . . . . 2.7 Chaînes de caractères . . . . . . . . . . . . . . . . . . . . 2.8 Tables, tableaux et matrices . . . . . . . . . . . . . . . . 2.8.1 Tables . . . . . . . . . . . . . . . . . . . . . . . . . 2.8.2 Tableaux . . . . . . . . . . . . . . . . . . . . . . . 2.8.3 Matrices . . . . . . . . . . . . . . . . . . . . . . . . 2.9 Quelques autres types utiles . . . . . . . . . . . . . . . . 2.9.1 Le type équation . . . . . . . . . . . . . . . . . . . 2.9.2 Le type intervalle . . . . . . . . . . . . . . . . . . . 2.9.3 Le type procédure . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
35 35 36 37 37 39 40 41 43 43 47 49 50 52 56 58 58 59 63 66 70 74 75 77 82 88 88 89 90
1.7 1.8
2
Sommaire 3
4
xvii
Évaluation 3.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Évaluation et affectation . . . . . . . . . . . . . . . . . . 3.3 Évaluation différée . . . . . . . . . . . . . . . . . . . . . . 3.4 Niveaux d’évaluation . . . . . . . . . . . . . . . . . . . . 3.5 Règles d’évaluation . . . . . . . . . . . . . . . . . . . . . 3.5.1 Vocabulaire . . . . . . . . . . . . . . . . . . . . . . 3.5.2 Évaluation des variables de type simple . . . . . . 3.5.3 Évaluation des variables de type agrégé . . . . . . 3.5.4 Évaluation d’une variable locale ou d’un paramètre 3.5.5 En résumé . . . . . . . . . . . . . . . . . . . . . . . 3.6 Évaluation et appel de fonction . . . . . . . . . . . . . . . 3.7 Commandes liées à l’affectation . . . . . . . . . . . . . . 3.7.1 Une autre façon de procéder à une affectation . . . 3.7.2 État d’une variable . . . . . . . . . . . . . . . . . . 3.7.3 Désaffecter une variable . . . . . . . . . . . . . . . 3.8 La commande eval et ses dérivées . . . . . . . . . . . . . 3.8.1 La commande eval . . . . . . . . . . . . . . . . . 3.8.2 Commandes spécifiques . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
91 91 91 92 94 97 97 98 100 101 104 105 108 108 109 111 112 112 113
Programmation 4.1 Préambule . . . . . . . . . . . . . . . . . . . . . . 4.2 Structures de contrôle . . . . . . . . . . . . . . . . 4.2.1 Branchement conditionnel . . . . . . . . . . 4.2.2 Itération non conditionnelle . . . . . . . . . 4.2.3 Itération conditionnelle . . . . . . . . . . . 4.3 Fonctions . . . . . . . . . . . . . . . . . . . . . . . 4.3.1 Déclaration d’une fonction . . . . . . . . . . 4.3.2 Appel d’une fonction . . . . . . . . . . . . . 4.3.3 Valeur produite en sortie . . . . . . . . . . 4.3.4 Utilisation du “ ;” . . . . . . . . . . . . . . 4.3.5 Insertion de commentaires . . . . . . . . . . 4.4 Filtrage des arguments . . . . . . . . . . . . . . . 4.5 Variables locales - Variables globales . . . . . . . . 4.6 Séquence des arguments effectifs . . . . . . . . . . 4.7 Nombre variable d’arguments . . . . . . . . . . . . 4.8 Fonctions récursives . . . . . . . . . . . . . . . . . 4.9 Retour non évalué . . . . . . . . . . . . . . . . . . 4.10 Passage d’un argument par variable . . . . . . . . 4.11 Entrées/Sorties . . . . . . . . . . . . . . . . . . . . 4.11.1 La commande print . . . . . . . . . . . . . 4.11.2 Commandes d’entrées/sorties fondamentales
. . . . . . . . . . . . . . . . . . . . .
115 115 116 116 120 121 122 122 123 124 125 128 129 131 135 135 139 146 147 151 151 154
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . .
xviii 5
Maple : règles et fonctions essentielles
Structure d’une expression Maple 5.1 Préambule . . . . . . . . . . . . . . . . . . . . . . . . 5.2 Type d’une expression . . . . . . . . . . . . . . . . . . 5.3 Représentation arborescente d’une expression Maple . 5.3.1 Expressions algébriques . . . . . . . . . . . . . 5.3.2 Représentation interne des types fondamentaux 5.4 Transformation d’expressions . . . . . . . . . . . . . . 5.4.1 Substitution . . . . . . . . . . . . . . . . . . . . 5.4.2 Conversion de type . . . . . . . . . . . . . . . . 5.4.3 Utilisation de la commande map . . . . . . . . . 5.4.4 Utilisation de la structure for . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
161 161 161 164 167 172 186 186 190 190 191
Bibliographie
193
Index
195
Maple 12 – Installation, activation et aide en ligne
205
Conventions typographiques Nous avons utilisé les conventions typographiques suivantes : les commandes, mots clés du langage et instructions Maple sont présentés en caractères droits (exemple : for). Le nom des touches du clavier est encadré (exemple : Entrée). Les symboles constitués d’un ou plusieurs caractères spéciaux (qui, souvent, désignent un opérateur) sont écrits en caractères droits et délimités par des guillemets anglo-saxons (exemple : “&*”). Les exemples qui illustrent notre propos sont délimités par des frises qui permettent de les distinguer du texte. Ils ont été conçus avec l’interface « classic » de la version 12 de Maple. Nous les reproduisons tels quels (à quelques aménagements de mise en page près, notamment lorsque les réponses sont trop longues). Chaque exemple doit en principe être précédé de l’exécution d’une instruction restart (afin d’éviter une confusion entre les variables de même nom susceptibles d’intervenir dans des exemples différents, voir la partie 1.5.6, page 23). Afin de limiter la présentation au contenu utile, nous ne faisons presque jamais apparaître cette instruction. Le lecteur devra s’en souvenir lorsqu’il essaiera de reproduire sur sa machine les exemples que nous proposons.
Chapitre 1
Prise en main 1.1
Préambule
L’objectif de ce chapitre est de donner rapidement au béotien un aperçu des possibilités du calcul formel (partie 1.2), et de présenter les informations essentielles permettant de prendre en main le logiciel Maple afin de l’utiliser avec l’interface classique (parties 1.3 à 1.7). Les interfaces1 utilisateur de Maple (rapidement présentées en partie 1.3.4) sont foisonnantes et en perpétuelle évolution au fil des versions successives du logiciel et leur présentation pourrait faire l’objet d’un ouvrage à part entière (mais qu’il faudrait trop fréquemment mettre à jour ou compléter. . .). Si leur connaissance facilite l’accès aux ressources du logiciel et confère un confort d’utilisation accru dans certaines situations, elles n’en sont pas pour autant essentielles pour mener à bien des calculs formels ou pour programmer avec Maple. Nous avons donc délibérément choisi de ne pas présenter de manière détaillée les caractéristiques et multiples possibilités ou gadgets des différentes interfaces que Maple met à la disposition de l’utilisateur. Nous limitons notre propos à la présentation des caractéristiques essentielles de l’interface classique2 (classic interface) 1
Selon le dictionnaire Larousse, une interface est « un module matériel ou logiciel permettant la communication d’un système avec l’extérieur ». Notons que l’expression « interface utilisateur » est une traduction littérale de l’expression anglaise user interface ; certains préfèrent utiliser en français la notion d’ « interface homme-machine » (IHM), qui permet de préciser quelles sont les deux entités auxquelles cette interface permet de dialoguer. À l’évolution des formats d’affichage informatique, qui sont passés d’un mode de présentation uniquement textuel à des modes de présentation graphiques, a correspondu une évolution du mode d’interaction avec la machine et de cette interface homme-machine. On trouve ces différents modes dans Maple. 2 L’interface classique a, entre autres, le mérite d’être une interface graphique évoluée existant dans toutes les versions de Maple depuis la version V.4 et dont le fonctionnement a depuis subi peu de modifications essentielles.
4
Maple : règles et fonctions essentielles
et supposons que le lecteur est familier de l’utilisation d’un environnement multifenêtres, avec menus déroulants ou contextuels déclenchés à l’aide de la souris. Nous évoquerons pour commencer les différents composants du logiciel Maple (partie 1.3) et, en particulier, l’interaction entre le logiciel et l’utilisateur qui se fait principalement avec une feuille de travail (partie 1.3.1) à travers un interpréteur de commandes (partie 1.3.2). Après ces préliminaires, le lecteur pourra faire ses premiers pas avec Maple (partie 1.4) grâce à quelques exemples simples. Nous pourrons alors aborder les notions essentielles de variable et d’affectation (partie 1.5) avant d’évoquer l’aide en ligne (partie 1.6), l’organisation de Maple en bibliothèques (partie 1.7) et avant de dresser la liste des erreurs couramment commises par l’utilisateur débutant (partie 1.8).
1.2 1.2.1
En quoi consiste le calcul formel Calcul formel et calcul numérique
Maple est un logiciel commercial qui propose un environnement de calcul formel. Le calcul formel, encore appelé calcul symbolique (le terme anglais computer algebra est, comme souvent, plus synthétique et mieux ciblé. . .) donne les moyens de manipuler les nombres, d’effectuer des calculs ou de réaliser des représentations graphiques sophistiquées, mais aussi et surtout de mener des calculs algébriques, de représenter symboliquement des objets mathématiques complexes ou élaborés comme des fonctions, des équations et leurs solutions algébriques, des matrices, et d’obtenir, le cas échéant, des formes closes3 pour les solutions. Les logiciels de calcul formel, tels Maple, Mathematica ou Mupad, s’opposent ainsi à des logiciels comme Scilab ou MatLab4 dont la vocation est de permettre la réalisation de calculs efficaces sur un plan strictement numérique. L’émergence du calcul formel a été rendue possible par le considérable accroissement 3 Il est admis selon un consensus général que certaines formules sont sous forme close si leur écriture ne fait intervenir qu’un nombre fini de symboles comme les opérations arithmétiques usuelles “+”, “-”, “*”, “/” ou de fonctions numériques usuelles comme les fonctions trigonométriques circulaires ou hyperboliques directes et réciproques, les fonctions puissances entières, les fonctions exponentielles et logarithmes, les factorielles, etc. Le consensus est moins large pour admettre parmi les formes closes les formules faisant intervenir des sommes ou des produits infinis ou des fonctions plus exotiques comme la fonction zeta de Riemann, des fonctions obtenues comme primitives, des fonctions solutions d’équations différentielles célèbres comme les fonctions de Bessel ou les fonctions hypergéométriques, ou des fonctions définies par récurrence. 4 Maple et Maplesoft sont des marques déposées par Waterloo Maple, Inc.. Maplesoft est une division de Waterloo Maple, Inc.. Mathematica est une marque déposée par Wolfram Research, Inc. MuPAD est une marque déposée par Sciface Software GmbH & Co. Scilab est une marque déposée par l’INRIA. Matlab est une marque déposée par The Math Works, Inc.
1. Prise en main
5
des performances des ordinateurs. Initialement développés pour traiter des situations ne relevant pas spécifiquement du calcul numérique, les logiciels de calcul formel ont connu une évolution consistant à intégrer aussi en leur sein des capacités de calcul numérique. C’est le cas de Maple qui incorpore désormais des logiciels spécifiquement dédiés au calcul numérique, afin de proposer à l’utilisateur un environnement permettant de traiter toute la gamme des situations que l’on peut rencontrer lors de la résolution d’un problème.
1.2.2
La puissance du calcul formel
Avant de passer à une présentation des composants du logiciel Maple et de donner les premiers éléments concrets permettant au lecteur d’utiliser Maple pour traiter ses propres exemples, nous souhaitons montrer rapidement les possibilités offertes par le calcul formel. Nous espérons que les premiers exemples qui suivent illustreront de manière suffisamment spectaculaire ces possibilités et inciteront le lecteur à lire les autres parties de cet ouvrage. La première série d’exemples montre la capacité à faire des calculs symboliques (par opposition à numériques) que tout utilisateur ayant acquis un certain niveau en mathématiques est capable de mener à bien lui-même, mais dont le caractère fastidieux et pénible est incontestable. Les exemples sont emblématiques de situations dans lesquelles il convient de savoir utiliser judicieusement un formulaire et des techniques adaptées au contexte du calcul que l’on doit effectuer. L’obtention du résultat en une fraction de seconde devrait convaincre de l’intérêt d’un logiciel de calcul formel. Commençons par un calcul classique de limite5 : >
Limit((sin(tan(x))-tan(sin(x)))/x^7,x=0)=limit ((sin(tan(x))-tan(sin(x)))/x^7,x=0); −1 sin(tan(x)) − tan(sin(x)) = lim x→0 30 x7
5
Dans cet exemple, comme dans les suivants, la réponse de Maple présente une écriture proche de l’écriture mathématique usuelle pour le membre de gauche. Celui-ci résulte d’un effet du pretty-printer à travers l’utilisation des commandes Limit, Diff et Int, homologues inertes des commandes limit, diff et int (voir la partie 1.5.4, page 21).
6
Maple : règles et fonctions essentielles Poursuivons par un calcul de dérivée tout aussi éloquent : >
Diff(arcsin(ln(x^3)),x)=diff(arcsin(ln(x^3)),x); 3 d 3 dx arcsin(ln(x )) = x 1 − ln(x3 )2
Soulignons d’emblée que l’intérêt du calcul formel ne se limite pas à la possibilité, certes très spectaculaire, d’obtenir des expressions littérales et des formes closes quasi instantanément pour des calculs fastidieux de dérivées, de primitives, de développements limités et la résolution d’équations algébriques ou d’équations différentielles. La deuxième capacité essentielle d’un logiciel de calcul formel consiste à représenter de manière exacte des objets mathématiques, principalement des nombres, que les logiciels de calcul numérique ne peuvent représenter que de manière approchée, donc avec une certaine erreur qui empêche l’exploitation ultérieure de leurs éventuelles propriétés. Dans le calcul d’intégrale suivant : >
Int(arctan(x),x=0..1)=int(arctan(x),x=0..1); 1 1 π arctan(x) dx = − ln(2) 2 4 0
Maple représente le résultat obtenu de manière exacte à l’aide de constantes mathématiques célèbres (au lieu de se limiter à donner une valeur décimale approchée du résultat). Considérons enfin un exemple issu de l’algèbre, où il s’agit de déterminer les racines cubiques de l’unité : >
S:=solve(x^3-1=0); 1 √ 1 1 √ 1 S := 1, − + I 3, − − I 3 2 2 2 2
On remarque que Maple propose d’emblée une représentation exacte des trois racines cubiques de l’unité sous forme de nombres complexes (l’unité imaginaire étant notée I). Intéressons-nous à la deuxième racine trouvée :
1. Prise en main
7
> u:=S[2]; 1 √ 1 u := − + I 3 2 2 pour en demander une valeur numérique approchée : > v:=evalf(u); v := −0.5000000000 + 0.8660254040 I On peut désormais comparer le résultat symbolique au résultat numérique en élevant chacun d’eux au cube : > w:=evalc(u^3); w := 1 Lorsque l’on part de la forme exacte, le résultat affiché n’est pas 1., comme l’aurait proposé un logiciel effectuant des calculs numériques, mais 1 (sans point). Le résultat est donné de manière exacte et l’on peut vérifier qu’il est en outre reconnu comme étant de type entier : > type(w,integer); true En revanche, un calcul analogue à partir de la valeur approchée de la racine cubique en question conduit à une valeur approchée de 1 : > evalf(v^3); 1.000000001 − 0.3233738859 10−9 I Le dernier exemple que nous proposons illustre la capacité d’un logiciel de calcul formel à représenter un nombre à virgule flottante avec une précision arbitraire. Ici, on obtient une valeur décimale approchée de la racine
8
Maple : règles et fonctions essentielles
carrée de 2 avec 40 chiffres significatifs6 (alors qu’un logiciel effectuant des calculs numériques aurait, sur la même plateforme, une précision a priori limitée à 15 chiffres significatifs) : >
evalf[40](sqrt(2)); 1.414213562373095048801688724209698078570
1.3
Composants du logiciel
Depuis la version V de Maple, le logiciel offre un ensemble d’outils qui se déclinent en trois composants principaux : • la feuille de travail ; • l’interpréteur de commandes ; • le langage de programmation. Nous allons détailler ces différents composants ainsi que certaines de leurs caractéristiques.
1.3.1
La feuille de travail
La feuille de travail (Maple worksheet ) est le composant auquel l’utilisateur est confronté dès qu’il a lancé le logiciel. Son environnement peut prendre différentes formes comme on le verra un peu plus loin. Dans les modes évolués, c’est-à-dire autres que le mode ligne de commande, l’interface présente des menus, des barres d’outils, des menus contextuels, ou encore des fenêtres graphiques permettant à l’utilisateur d’interagir avec le système. Le logiciel n’existe que dans une version anglaise et donc tous les menus sont en anglais7 . Au fur et à mesure des interventions de l’utilisateur, la feuille de travail, initialement vierge, va afficher les traces des interactions entre l’utilisateur et le logiciel. Dans la rubrique Save du menu File, figurent différentes manières d’enregistrer ou d’exporter le travail effectué. En particulier, en choisissant de sauvegarder la feuille de travail selon le mode Maple classic worksheet, on se ménage la possibilité de retrouver les calculs que l’on vient d’effectuer au cours d’une session ultérieure, par exemple en recourant à la rubrique Open du menu File. 6
L’instruction evalf[40](sqrt(2)) ; peut aussi être écrite evalf(sqrt(2),40) ;. Bien que la première syntaxe soit celle préconisée par l’aide en ligne, la deuxième syntaxe est bien plus souvent utilisée dans les manuels. 7 De manière plus générale, les commandes du langage ont toutes des consonances anglosaxonnes et, à la date de la rédaction du présent ouvrage, l’aide en ligne est disponible uniquement en anglais.
1. Prise en main
1.3.2
9
L’interpréteur
L’interpréteur va déclencher des calculs et afficher des résultats selon les instructions saisies par l’utilisateur. Les instructions font appel à des commandes8 , c’est-à-dire à des fonctions faisant partie du noyau ou des bibliothèques de Maple. Dans les modes évolués, les résultats mathématiques sont affichés via un pretty-printer 9 qui propose un rendu très proche des notations mathématiques usuelles. L’interaction directe avec Maple à travers cet interpréteur (utilisation dite au toplevel ) consiste à utiliser le logiciel comme une super-calculatrice symbolique et numérique. Il convient par ailleurs de prendre conscience du fonctionnement de l’interpréteur10 de Maple. Lorsqu’on commence une session, la mémoire de travail du système ne comporte aucune information. Au fur et à mesure des interactions entre l’utilisateur et le système, la mémoire de travail va s’enrichir des valeurs des variables calculées lors de l’exécution des instructions passées à l’interpréteur. Le lecteur saura bientôt que Maple accepte des variables de types les plus divers. Ainsi, les données passées sous forme de fichiers ou de tableaux, les fonctions programmées par l’utilisateur etc. sont naturellement placées dans des variables. Il suffit donc de décrire successivement les différents objets avec lesquels on souhaite travailler pour décrire un problème. Contenus de variables, ces objets vont s’ajouter de manière incrémentale11 à l’espace mémoire de travail de Maple qui peut être vu comme une sorte de base de connaissances. Maple tient compte des informations présentes dans cette base pour répondre aux sollicitations de 8
Nous ferons en sorte d’utiliser le terme de commande par opposition au terme de fonction pour distinguer entre les fonctions mises à disposition par Maple (qui peuvent être du code C compilé ou des fonctions écrites selon la syntaxe du langage de programmation) et les fonctions programmées par l’utilisateur. 9 Le verbe anglais to pretty-print (ou to prettyprint) signifie mettre en forme ou mettre en page un objet pour qu’il soit plus facile à un lecteur humain d’en percevoir la structure ou pour le rendre plus esthétique. Par extension, un pretty-printer est un programme informatique qui effectue ce type de mise en forme. En particulier, pour ce qui concerne les logiciels de calcul formel, un pretty-printer présente les résultats selon une typographie conforme aux usages en vigueur en mathématiques. Par exemple, un résultat comme x^2 + 3*x est présenté x2 + 3x. Certaines calculatrices graphiques comme les calculatrices de la série HP-49 ou TI-89 disposent de ce type de pretty-printer. 10 En termes de langages de programmation, un interpréteur s’oppose à un compilateur. En pratique, un compilateur sert le plus souvent à traduire un code source écrit une fois pour toutes dans un langage de programmation en un autre langage, habituellement un langage d’assemblage ou un langage machine. Le programme en langage machine produit par un compilateur est appelé code objet et c’est lui qui est effectivement exécuté lorsqu’on veut utiliser le programme constitué par le code source. Contrairement à un compilateur, un interpréteur exécute les instructions du programme (ou en évalue les expressions), au fur et à mesure de leur lecture pour interprétation. Du fait de cette phase sans traduction préalable, l’exécution d’un programme interprété est généralement plus lente que le même programme compilé. 11 Le terme incrémental caractérise une opération sur un ensemble de données qui ne concerne que les données qui ont été ajoutées depuis la dernière manipulation.
10
Maple : règles et fonctions essentielles
l’utilisateur. Toutes les variables créées pendant une session sont conservées avec leurs contenus jusqu’à ce qu’une instruction modifie leur valeur ou que l’on décide de procéder à une réinitialisation complète de la mémoire de travail (voir la partie 1.5.6, page 23).
1.3.3
Le langage de programmation
Le logiciel met à disposition de l’utilisateur un langage de programmation impératif qui permet d’écrire des fonctions nouvelles élaborées à partir de commandes Maple existantes. Ce langage présente de grandes similitudes dans ses primitives et ses principes de fonctionnement avec des langages comme Pascal ou C. Les mots clés, les structures de contrôle ainsi que les fondements de la programmation avec Maple sont abordés au chapitre 4.
1.3.4
Les différentes interfaces
L’utilisateur peut accéder aux fonctionnalités de Maple à travers des interfaces de différentes natures. • La feuille de travail standard (standard worksheet) permet un accès simple et rapide à Maple à l’aide de palettes, de menus contextuels ou encore de complétion automatique des mots-clés du langage. Le débutant y trouvera des outils qui l’aideront à formuler des problèmes simples. Par ailleurs, mais à la condition d’avoir une bonne connaissance des possibilités qu’offre Maple, cette interface permet de mettre en page et de présenter un document électronique dans lequel les calculs sont mis en valeur au moyen d’outils spécifiques, en masquant certains calculs intermédiaires, en intégrant des figures sur la feuille de travail, et en offrant la possibilité de faire figurer sur la feuille des calculs interactifs12 . Cette interface, apparue dans les versions récentes du logiciel (à partir de la version 9), est décrite dans [11]. • La feuille de travail classique (classic worksheet) est le mode avec interface graphique le plus ancien. S’il ne présente pas tous les gadgets disponibles dans le mode standard, il dispose néanmoins d’une interface graphique puissante ainsi que du pretty-printer qui affiche des réponses avec un rendu utilisant les notations mathématiques usuelles. Il présente à nos yeux l’avantage de séparer clairement les interventions de l’utilisateur de celles de l’interpréteur. Il sera souvent plus simple de développer des programmes avec cette interface, même s’il faut les intégrer ensuite dans une application beaucoup plus sophistiquée réalisée avec le mode standard. 12
Un calcul interactif offre au lecteur d’une feuille de travail la possibilité de modifier les paramètres qui interviennent dans certains exemples afin d’en constater les effets sur les calculs ou les figures.
1. Prise en main
11
Autre avantage de cette interface : elle requiert moins de mémoire que l’interface standard (elle utilise moins de ressources graphiques puisque ses possibilités de mise en page sont moindres). Tous les exemples et illustrations présentés dans cet ouvrage sont extraits de feuilles de travail réalisées avec l’interface classique. • La ligne de commande (command line) est un mode dépouillé, en particulier dépourvu d’interface graphique. Elle date des premières versions de Maple et permettait alors de répondre aux limitations de la mémoire. Ce mode subsiste dans les versions actuelles. Avec lui, on résout des problèmes dont la taille interdit qu’ils soient traités dans l’un des modes précédemment évoqués. Il autorise aussi une utilisation de Maple dans un traitement par lot (batch) avec des scripts. Avec ces trois interfaces, on accède à la plupart des commandes Maple et donc aux véritables capacités du calcul formel. Il existe encore deux autres interfaces aux buts très spécifiques. On ne peut pas les mettre sur le même plan que les trois précédents. • Le calculateur graphique (Maplesoft graphing calculator ) est une interface (disponible uniquement pour les systèmes d’exploitation Microsoft Windows et Microsoft Vista) qui permet d’effectuer des calculs simples ainsi que des représentations graphiques interactives. Cette interface met donc à disposition une sorte de supercalculatrice graphique. • Les Maplets (Maplet applications) sont des interfaces graphiques contenant des fenêtres, des champs ou encore des curseurs qui permettent de lancer des calculs et des représentations graphiques par simples clics de souris, sans avoir à accéder et à manipuler une feuille de travail. Les Maplets sont la partie visible de programmes ou d’objets Maple dont les structures sont masquées dans la présentation. Les Maplets sont apparus dans la version 8 de Maple.
1.3.5
Versions successives du logiciel
La version V.4 a été l’une des premières versions de Maple largement diffusée en France. Des changements majeurs sur le plan syntaxique apparaissent dès la version V.5. La version 6 apporte des modifications (salutaires) à la gestion des variables locales et introduit les modules en programmation ainsi qu’un nouveau type de matrices permettant une occupation mémoire plus efficace que l’ancien et donc mieux adapté au calcul numérique. Elle voit aussi l’intégration de la bibliothèque NAG13 (NAG library) de calcul numérique. Cette version marque un tournant ; le logiciel propose désormais des outils qui sortent du cadre strict du calcul formel. Les nouvelles versions se succèdent jusqu’à la version 12 utilisée pour la rédaction de cet ouvrage. Elles se distinguent des précédentes essentielle13
NAG est une marque déposée de The Numerical Algorithms Group Ltd.
12
Maple : règles et fonctions essentielles
ment par un accroissement des bibliothèques et par des transformations cosmétiques au niveau des interfaces utilisateur.
1.4 1.4.1
Premiers pas avec Maple L’invite
Après avoir lancé le logiciel Maple à travers l’interface classique, on obtient une feuille de travail vierge sur laquelle apparaît l’invite (prompt) qui, par défaut14 , est représentée par le symbole “ > ”. L’utilisateur attentif remarquera que l’invite est précédée par une sorte de symbole qui ressemble à un crochet ouvrant (“[”), appelé crochet d’exécution dont le rôle sera expliqué plus loin (voir la partie 1.4.3, page 13).
1.4.2
Caractères de fin de saisie
Lorsque l’on utilise Maple en interagissant directement avec l’interpréteur (utilisation dite au toplevel par opposition à la simple exécution d’un programme Maple), l’interaction avec le logiciel consiste en une succession d’instructions saisies par l’utilisateur et de réponses fournies par l’interpréteur. La feuille de travail présente les traces de cette interaction. L’utilisateur doit donc saisir une instruction à côté de l’invite et pour signifier la fin de sa saisie, il lui faut terminer la ligne de commande par le caractère “;” ou par le caractère “:”. L’instruction saisie sera interprétée et exécutée (si la syntaxe est correcte) dès que l’utilisateur aura pressé la touche Entrée (ou Enter sur un clavier anglo-saxon). Lorsqu’une instruction se finit par “;”, l’interpréteur de Maple exécute l’instruction et, si elle est syntaxiquement valide15 , affiche le résultat obtenu : > 2+3; 5 > exp(1.0); 2.718281828 14 Il est possible de modifier l’invite en ayant recours à la variable d’interface prompt. Ainsi, en saisissant l’instruction interface(prompt="->"), on obtient “->” comme nouveau symbole d’invite. 15 Si l’instruction n’est pas syntaxiquement valide, l’interpréteur affiche un message d’erreur, comme on le verra plus loin dans certaines situations. Dans les versions récentes du logiciel, les messages d’erreur sont assez explicites et donnent une bonne indication de la nature de l’erreur rencontrée.
1. Prise en main
13
En revanche, si l’instruction est terminée par “:”, Maple exécute l’instruction de façon muette, c’est-à-dire n’affiche pas le résultat : > >
2+3: exp(1.0):
Cette façon de procéder est utile pour éviter l’affichage de résultats volumineux comme les contenus de packages ou les objets graphiques sous-jacents aux représentations graphiques.
1.4.3
Groupement d’instructions
Pour saisir plusieurs instructions sur une même ligne et ainsi les passer simultanément à l’interpréteur, il suffit de les séparer par des “;” ou des “:”. Elles sont alors exécutées consécutivement, dans l’ordre où elles ont été présentées, et les résultats, s’il y a lieu, sont affichés l’un sous l’autre en une seule fois par l’interpréteur. >
1+2;2.0^(1/2);(2*3)^2; 3 1.414213562 36
On peut aérer la présentation des instructions et passer à la ligne entre chacune d’elles plutôt que de les écrire sur la même ligne. Pour passer à la ligne au sein du même crochet d’exécution sans provoquer l’exécution individuelle d’une instruction au moment du changement de ligne, il faut presser simultanément les touches Majuscule et Entrée (Shift et Enter sur un clavier anglo-saxon). On remarque qu’au moment où l’on change de ligne, le crochet d’exécution qui précède l’invite s’allonge de manière à englober les différentes lignes d’instructions. Ce repère visuel16 a pour but de mettre en évidence l’ensemble des instructions adressées simultanément à l’interpréteur lors de la prochaine validation. > 1+2; 2.0^(1/2); (2*3)^2; 16
Le crochet d’exécution n’apparaît pas sur les exemples reproduits dans cet ouvrage.
14
Maple : règles et fonctions essentielles 3 1.414213562 36
Cette façon d’aller à la ligne sans déclencher l’interpréteur est particulièrement utile lorsqu’on programme avec Maple. Elle permet d’écrire des fonctions Maple dont le contenu s’étend sur plusieurs lignes (voir chapitre 4). Pour écrire une instruction dont la longueur est supérieure à celle d’une ligne sur la feuille de travail, on peut formater la saisie en plaçant un caractère “\” chaque fois que l’on va à la ligne. Maple utilise aussi ce caractère pour signaler des réponses qui tiennent sur plusieurs lignes : >
1234567890\ 1234567890\ 1234567890\ 1234567890\ 1234567890\ 1234567890\ 1234567890\ 1234567890\ 1234567890+1; 123456789012345678901234567890123456789012345\ 678901234567890123456789012345678901234567891
On peut aussi plus simplement écrire l’instruction en la laissant se répartir sur plusieurs lignes. Maple a encore recours au caractère “\” pour l’affichage des réponses dont la longueur dépasse la longueur de ligne de l’interface : >
11111111111111111111111111111111111111111111111111111 11111111111111111111111111111111111111111111111111+1; 11111111111111111111111111111111111111111111111\ 11111111111111111111111111111111111111111111111\ 111111112
1. Prise en main
1.4.4
15
Commentaires
Le caractère “#” permet d’introduire un commentaire dans une feuille de travail. Ce qui se trouve entre ce caractère et la fin de ligne n’est alors pas pris en compte par l’interpréteur, comme le montre l’exemple suivant : > 1+2; # un premier calcul très simple # 2.0^(1/2); cette ligne n’est pas prise en compte (2*3)^2; 3 36
1.4.5
Enregistrer son travail
Comme dans la plupart des environnements logiciels présentant des menus, on a recours à la rubrique Save as (ou Save si l’on sauvegarde de manière incrémentale un document déjà enregistré) du menu Edit pour sauvegarder son document dans un fichier (ici la feuille de travail) pour une utilisation future. Pour retrouver sa feuille de travail enregistrée dans un fichier, on l’ouvre en choisissant la rubrique Open du menu Edit et en indiquant le nom du fichier dans lequel est sauvegardée la feuille considérée. On récupère la feuille de travail dans l’état où elle se trouvait au moment de son enregistrement. Toutefois, il ne s’agit que d’un effet d’affichage ; les instructions qui figurent à l’écran n’ont pas été exécutées par l’interpréteur à l’instant où la feuille de travail vient d’être ouverte. Si l’on souhaite que certains résultats soient présents dans la mémoire de travail, il faut expressément provoquer l’exécution des instructions correspondantes en les validant une par une17 .
1.5
Variables et affectation
Le lecteur l’aura compris, effectuer des calculs élaborés demande de conserver des résultats intermédiaires afin d’y accéder ultérieurement, d’où le besoin de variables. Une variable est un emplacement en mémoire auquel est attachée une étiquette (le nom de la variable) qui permet d’accéder à son contenu. L’affectation assigne une valeur à une variable : elle établit une correspondance entre une étiquette et une valeur. 17
Ou, plus efficacement, si l’on souhaite provoquer l’exécution d’une sélection d’instructions (respectivement de l’ensemble des instructions) de la feuille de travail que l’on vient de charger, dans l’ordre où elles se présentent, on peut recourir à la rubrique Execute Selection (respectivement Execute Worksheet) du menu Edit.
16
Maple : règles et fonctions essentielles
1.5.1
Noms de variables
Un nom de variable licite est un mot qui commence par une lettre suivie d’un nombre fini de caractères18 , lettres, chiffres ou “_” (underscore), autres que les caractères qui jouent un rôle particulier dans le langage (“%”, “#”, “ ?”, “&”, “:”, “;”, “=”, “$”. . .) de sorte que l’ensemble forme un mot n’apparaissant pas dans les mots réservés ou les mots protégés du langage Maple19 . >
abc; a1234; ABC; # sont des noms de variables licites abc a1234 ABC
sont des noms de variables licites, tandis que : >
1abc; # n’est pas un nom de variable licite
Error, missing operator or ‘;‘
est rejeté par l’interpréteur car le mot commence par un chiffre. De même, cos ne peut servir de nom de variable puisque ce mot est réservé par le langage pour désigner la fonction trigonométrique cosinus : >
cos:=25;
Error, attempting to assign to ‘cos‘ which is protected
Par ailleurs, Maple distingue les majuscules des minuscules ; ainsi, a et A ne désignent pas la même variable comme le montre l’exemple suivant :
18
La longueur maximale d’un nom de variable est de 524271 caractères sur une plateforme 32-bits. 19 La liste des mots clés réservés par le langage s’obtient en consultant la page d’aide en ligne ?reserved. Par ailleurs, ces mots clés sont à distinguer des mots protégés du langage dont on obtient la liste en consultant la page d’aide en ligne ?protect.
1. Prise en main
17
>
a:=10;
>
a;
>
A;
a := 10 10 A
1.5.2
Affectation
L’opérateur d’affectation est désigné par “:=”. L’instruction nom_de_variable:=expression; construit une case mémoire dont l’étiquette est nom_de_variable et dont le contenu est le résultat de l’évaluation de l’expression. La simple instruction nom_de_variable; provoque l’affichage du contenu de cette variable20 : >
a:=10+2*4; a := 18
>
a; 18
>
b:=x+y+2*x;
>
b;
b := 3 x + y 3x + y Il convient de connaître la règle fondamentale suivante : pour Maple, une variable vide s’évalue en son nom. En d’autres termes, le résultat de l’évaluation d’une variable non affectée est le nom de cette variable21 . Le cas échéant, un moyen de savoir si une variable est affectée ou non consiste précisément à constater si elle s’évalue en son nom. 20
En effet, l’invocation d’une variable déclenche son évaluation ; cette évaluation, à l’exception de certains cas particuliers (voir chapitre 3), provoque l’affichage de la valeur contenue dans la case mémoire associée à ce nom. 21 La connaissance de cette règle devrait alerter l’utilisateur chaque fois que l’interpréteur répond en recopiant exactement l’instruction qui lui a été soumise : c’est probablement qu’il figure dans l’instruction un objet que Maple ne connaît pas et qu’il traite donc comme une variable non affectée. En particulier, une faute de frappe de l’utilisateur fait souvent croire au système qu’il est en face d’une (nouvelle) variable non affectée.
18
Maple : règles et fonctions essentielles >
toto; toto
Pour terminer cette partie consacrée à l’affectation, nous attirons l’attention du lecteur sur l’erreur qui consiste à utiliser le symbole “=” au lieu du symbole “:=” pour désigner l’affectation (voir la partie 1.8.2, page 30).
1.5.3
Noms de grecques
variables
fondés
sur
des
lettres
Maple permet d’utiliser des noms de variables comportant des lettres grecques. Pour obtenir une lettre grecque, il faut saisir son nom22 , le prettyprinter la présentera selon la calligraphie usuelle : >
alpha:=1.345; α := 1.345
Pour obtenir une lettre grecque minuscule (respectivement majuscule), il suffit de saisir son nom avec l’initiale en minuscule (respectivement en majuscule) : >
beta;Beta;delta;Delta;omega;Omega; β B δ Δ ω Ω
Ce principe simple souffre toutefois trois exceptions. Il faut connaître les deux premières sous peine de déboires dans des situations courantes où interviennent ces constantes. 22
Le lecteur averti reconnaîtra un principe analogue à celui mis en œuvre dans LaTeX.
1. Prise en main
19
La première exception concerne π et Π. La notation π désigne habituellement la constante trigonométrique dont une valeur décimale approchée est 3, 14 ; cet usage a été adopté aussi dans Maple et la constante trigonométrique est désignée par le mot-clé Pi (voir la partie 2.3, page 37). En conséquence, une nouvelle convention est mise en place exceptionnellement : Π majuscule s’obtient par PI (tout en majuscules). L’inconvénient majeur de ces conventions réside dans la très grande proximité graphique du résultat présenté par le pretty-printer lors de l’invocation de Pi (constante trigonométrique) et de celui obtenu par invocation de pi (nom de variable Maple licite répondant aux règles de présentation des lettres grecques) : >
pi;Pi;PI; π π Π
Voici ce qui se produit lorsqu’on essaie d’affecter une valeur à ces différents identificateurs : >
pi:=10;Pi:=10;PI:=10; π := 10
Error, attempting to assign to ‘Pi‘ which is protected
Π := 10 On vérifie que pi et PI sont des noms de variables licites traités par le pretty-printer comme des lettres grecques. Par ailleurs, Pi est bien un nom protégé par Maple. Enfin, on peut demander une valeur décimale approchée du contenu de chacun de ces identificateurs à l’aide de la commande evalf (voir la partie 2.5.5, page 52) : >
evalf(pi);evalf(Pi);evalf(PI); 10. 3.141592654 10.
20
Maple : règles et fonctions essentielles
La deuxième exception concerne γ et Γ. En mathématiques, γ représente la constante d’Euler. Maple reprend cet usage si bien que gamma désigne cette constante et n’est donc pas un nom licite de variable Maple23 . Par ailleurs, Gamma constitue un nom de variable licite, normalement affiché Γ par le pretty-printer. Enfin, GAMMA désigne la fonction gamma d’Euler24 . On peut à nouveau déplorer la trop grande proximité graphique des résultats obtenus par invocations de Gamma (nom de variable licite) et de GAMMA (mot réservé désignant la fonction d’Euler) : >
gamma;Gamma;GAMMA; γ Γ Γ
Comme précédemment, en essayant d’affecter une valeur aux différents identificateurs on peut déceler ceux qui sont réservés par le langage Maple : >
gamma:=1;Gamma:=1;GAMMA:=1;
Error, attempting to assign to ‘gamma‘ which is protected
Γ := 1 Error, attempting to assign to ‘GAMMA‘ which is protected
Enfin, en demandant une valeur approchée des valeurs contenues dans chacun des identificateurs, on a confirmation des assertions qui précèdent : >
evalf(gamma);evalf(Gamma);evalf(GAMMA); 0.5772156649 1. Γ
23
γ ne peut donc pas, a priori, être un nom de variable choisi par l’utilisateur à moins que l’utilisateur ne décide de contourner la protection placée sur gamma avec la commande unprotect. 24 La fonction gamma d’Euler est définie par Γ(x) = ∞ e−t t(x−1) dt. 0
1. Prise en main
21
La troisième exception concerne X (« chi » majuscule) qui désigne la fonction cosinus hyperbolique intégral25 . Pour obtenir une variable de nom X avec Maple, il faut saisir CHI : >
chi;Chi;CHI; 2 Chi X
>
chi:=2;Chi:=3;CHI:=4; χ := 2
Error, attempting to assign to ‘Chi‘ which is protected
X := 4
1.5.4
Commandes inertes
Certaines commandes Maple existent sous deux formes : une forme active (nom de commande commençant par une minuscule) et une forme inerte (nom de commande identique mais commençant par une majuscule). La commande active conduit Maple à lancer un calcul pour proposer un résultat immédiatement. L’homologue inerte d’une commande active ne déclenche pas de calcul mais a pour effet d’activer le pretty-printer afin de produire un affichage du calcul en question selon une présentation conforme aux habitudes de la calligraphie mathématique. Cette fonctionnalité permet une présentation plus agréable des résultats et une description explicite des étapes intervenant dans la résolution d’un problème. Les commandes qui existent sous les deux formes sont diff (associée à Diff, qui sert à calculer la dérivée, éventuellement partielle, d’une expression algébrique), int (associée à Int, qui sert à calculer des primitives ou des intégrales), limit (associée à Limit, qui sert à calculer des limites), sum (associée à Sum, qui sert à calculer des sommes, éventuellement partielles, de séries) et product (associée à Product, qui sert à calculer des produits, éventuellement infinis). Des exemples qui illustrent l’utilisation de commandes inertes sont présentés dans la partie 1.2.2, page 5 ; d’autres figurent dans la partie 2.3.1, page 37. 25
La fonction cosinus hyperbolique intégral est définie par : x cosh(t) − 1 dt . chi(x) = γ + ln(x) + t 0
22
Maple : règles et fonctions essentielles
Il existe d’autres cas de commandes associées selon le même principe (le nom de l’une commençant par une minuscule, le nom de l’autre commençant par une majuscule). Cependant, l’association ne relève pas du schéma commande active/commande inerte que nous venons d’exposer. Il s’agit, en général, de commandes relevant de l’arithmétique dans des anneaux. La commande dont le nom commence par une minuscule désigne alors, en principe, une opération qui s’applique dans Z ou dans K[X] (avec K = R ou K = C) alors que la commande dont le nom commence par une majuscule désigne la même opération mais dans Fp ou dans Fp [X]. C’est ainsi que gcd (recherche du PGCD) est associée à Gcd, factor à Factor, expand à Expand, normal à Normal, etc.
1.5.5
Opérateur dito
Le symbole “%” désigne l’opérateur dito 26 qui permet d’obtenir le dernier résultat calculé par l’interpréteur (lorsqu’il existe) : >
a:=1+2*3; a := 7
>
%; 7
De même, les opérateurs “%%” et “%%%” permettent d’obtenir l’avantdernier et l’antépénultième27 résultats calculés par l’interpréteur (lorsqu’ils existent) : >
>
a:=2*3+4;b:=3*4+5;c:=4*5+6; a := 10 b := 17 c := 26 %%%; 10
26
Appelé ditto en anglais. Soulignons que l’on parle ici de dernier, avant-dernier, etc. au sens chronologique du terme. Or la chronologie des actions réalisées n’est pas nécessairement conforme à l’ordre dans lequel les calculs apparaissent sur la feuille de travail. Le désaccord entre ordre chronologique et ordre de présentation est parfois source de confusion pour les débutants. Voir à ce propos la partie 1.8.8, page 34.
27
1. Prise en main
23
Il convient de ne pas abuser du recours à l’opérateur dito. Il est à peine moins rapide et beaucoup plus recommandable d’affecter un résultat intermédiaire à une variable afin de pouvoir s’en servir autant de fois que nécessaire par la suite, en étant certain d’évoquer la valeur que l’on souhaite. En effet, une utilisation irréfléchie de l’opérateur dito peut produire un résultat différent de celui escompté. L’exemple suivant suffit à s’en convaincre : >
>
a:=2*3+4;b:=3*4+5;c:=4*5+6; a := 10 b := 17 c := 26 %;%%;%%%; 26 26 26
1.5.6
Nettoyage de la mémoire de travail
Lors d’une session assez longue, il peut s’avérer judicieux de nettoyer complètement la mémoire de travail afin de récupérer de l’espace mémoire et de reprendre proprement les définitions des objets et variables utilisés. Une telle réinitialisation s’obtient avec la commande restart : >
a:=1;b:=2;a;b; a := 1 b := 2 1 2
> >
restart:a;b; a;b; a b
24
Maple : règles et fonctions essentielles
Les variables a et b s’évaluent en leur nom après réinitialisation, preuve28 que la mémoire de travail a bien été purgée et que toute connaissance antérieure au restart a été oubliée. Nous souhaitons insister sur le fait que la présentation des instructions à l’écran n’est pas nécessairement fidèle à l’ordre de leur exécution. En effet, l’utilisateur constatera rapidement que l’interface classique lui permet de se déplacer dans la feuille de travail avec les touches ←, →, ↑ et ↓ ou avec la souris et de modifier une instruction et de la valider à nouveau, quel que soit son emplacement sur la feuille. En particulier, si restart provoque l’effacement des connaissances (variables ou fonctions définies par l’utilisateur) de la mémoire de travail, il demeure sans effet sur le contenu de la feuille de travail de sorte que l’utilisateur continue, après l’appel à restart, de voir s’afficher exactement la même chose qu’avant l’appel (alors que, juste après l’appel, la mémoire de travail est vierge de toute connaissance. . .). La feuille de travail contient une partie plus ou moins bien ordonnée des traces des interactions passées entre l’utilisateur et le système. L’utilisateur peut d’ailleurs supprimer une partie de ces traces simplement en sélectionnant la partie à effacer (par exemple, de manière usuelle avec la souris) puis en coupant ce texte. Voir [11] ou [5] pour en savoir davantage à propos du maniement de l’interface classique et des manipulations de mise en page possibles dans une feuille de travail.
1.5.7
Désaffecter une variable
Nous venons de voir comment réinitialiser toute la mémoire de travail du système et donc comment désaffecter toutes les variables utilisées au cours d’une session. Cependant on souhaite parfois désaffecter une seule ou quelques variables définies au cours de la session. Nous présentons sans explication un moyen de désaffecter une variable qui sera justifié ultérieurement (voir la partie 3.3, page 92) : >
a:=5; a := 5
>
a:=’a’; a := a
>
a; a
28
Une variable qui n’est pas affectée s’évalue en son nom, voir la partie 3.2, page 91.
1. Prise en main
1.6
25
Utilisation de l’aide en ligne
Il y a différentes façons d’utiliser l’aide en ligne mise à disposition par Maple.
1.6.1
L’opérateur “ ?”
Lorsque l’on travaille avec l’interface classique, le plus simple consiste à consulter une page d’aide portant sur une commande donnée en l’activant à l’aide de l’opérateur “ ?”. Par exemple, si l’on souhaite connaître le contenu de la page d’aide en ligne consacrée à la commande evalf, on va écrire directement ?evalf à côté de l’invite puis valider l’instruction29 , sans terminer celle-ci par un “;” ou un “:”. Une page d’aide en ligne comporte différents renseignements sur la commande présentée. Elle commence par son nom et la forme théorique que doit prendre un appel utilisant la commande en question, ainsi qu’une description des paramètres d’appel. Dans une deuxième partie, cette page décrit les fonctionnalités implémentées par la commande en question ainsi que les caractéristiques, cas particuliers ou limites d’utilisation. Une troisième partie présente des exemples que l’on peut copier et coller dans sa propre feuille de manière à les adapter à son propre problème. Enfin, la page d’aide propose une série de mots-clés. Ces termes, en rapport avec la commande présentée, sont des liens clicables qui dirigent vers d’autres pages d’aide.
1.6.2
Recherche par sujet
Bien évidemment, l’utilisation de l’aide en ligne à partir de l’opérateur “ ?” suppose que l’on connaisse exactement le nom de la commande sur laquelle on souhaite obtenir des précisions. Il existe un moyen de se documenter même si l’on est à la recherche (du nom) d’une commande pour traiter un problème donné. Il suffit de recourir à la rubrique Topic Search (recherche par sujet) du menu Help et de saisir dans la fenêtre qui s’ouvre alors un mot (en anglais) qui est central pour le problème considéré. Maple proposera alors des thèmes en rapport avec la clé de recherche et en cliquant sur l’un de ces thèmes, on sera conduit à la page d’aide correspondante.
1.6.3
Recherche plein texte
Le même type de recherche peut être effectué en ayant recours à la rubrique Full Text Search (recherche plein texte). Dans ce cas, toutes les pages d’aide en ligne comprenant la clé de recherche seront proposées en 29
C’est-à-dire presser la touche Entrée .
26
Maple : règles et fonctions essentielles
réponse, assorties d’un indice de pertinence (Maple estime dans quelle mesure la page d’aide en ligne proposée dans la liste des réponses concerne de manière pertinente la clé de recherche).
1.6.4
Autres ressources
Il existe encore bien d’autres ressources susceptibles d’apporter une aide à l’utilisateur. Parmi celles-ci, on compte : • Le Maple tour : ce tutoriel consiste en un survol interactif des fonctionnalités essentielles de Maple. On y accède par le menu Help en choisissant la rubrique New User’s Tour. L’utilisateur sera guidé à travers un certain nombre de feuilles de travail. • Les manuels en ligne : le guide en ligne intitulé Maple Getting Started Guide donne des renseignements utiles au débutant pour commencer à utiliser Maple et pour tirer profit des ressources en ligne disponibles sur le site de Maplesoft (http://www.maplesoft.com). • Le dictionnaire des mathématiques et des sciences de l’ingénieur : les rubriques de ce dictionnaire sont accessibles en demandant, par le biais de l’opérateur “ ?”, une page d’aide relative à une rubrique figurant dans le dictionnaire. On peut, par exemple, saisir ?Math pour voir apparaître l’index du dictionnaire ouvert à la lettre “M”.
1.7
Les bibliothèques de fonctions (packages)
Lorsqu’on lance Maple, débutant ainsi une nouvelle session, seul le noyau de Maple est effectivement chargé en mémoire. Ce noyau comporte les commandes fondamentales, celles qui sont d’utilisation courante. La plupart de ces fonctions, dites built-in, consistent en des exécutables (généralement du code C compilé) afin de privilégier la vitesse d’exécution. Les autres commandes disponibles sont regroupées en bibliothèques de fonctions appelées packages30 . Afin d’éviter de remplir inutilement l’espace mémoire de travail, les commandes des packages ne sont pas chargées lors du démarrage d’une session, mais seulement à la demande31 . Il y a trois façons d’invoquer une commande figurant dans un package. • Chargement de l’ensemble du package. La première façon consiste à charger le package dans son ensemble à l’aide de la commande 30
Nous maintenons le point de vue exprimé dans [5] et [8], relayé dans [7], et ne pouvons toujours pas nous résoudre à traduire package par paquetage. 31 Dans les anciennes versions de Maple, existait un ensemble de commandes qui figuraient dans une bibliothèque dite « secondaire » (en quelque sorte un package spécifique sans nom particulier), et qu’il fallait charger à l’aide de la commande readlib, désormais obsolète.
1. Prise en main
27
with. Ainsi dans l’exemple suivant, on charge le package LinearAlgebra qui met à disposition de l’utilisateur des commandes relatives à l’algèbre linéaire32 : > with(LinearAlgebra); [&x , Add , Adjoint , BackwardSubstitute, BandMatrix , Basis, BezoutMatrix , BidiagonalForm, BilinearForm, CharacteristicMatrix , CharacteristicPolynomial , Column, ColumnDimension, ColumnOperation, ColumnSpace, CompanionMatrix , ConditionNumber , ConstantMatrix , ConstantVector , Copy, CreatePermutation, CrossProduct, DeleteColumn, DeleteRow , ··· VectorMatrixMultiply, VectorNorm, VectorScalarMultiply, ZeroMatrix , ZeroVector , Zip] On lit le nom de toutes les commandes qui viennent d’être chargées. Dans l’exemple précédent, nous avons coupé la (longue) réponse de Maple et remplacé plusieurs lignes de la réponse par des points de suspension. On recourra au “:” plutôt qu’au “;” en fin d’instruction (voir la partie 1.4.2, page 12) pour ne pas voir figurer cette liste dans la feuille de travail. Le chargement d’un package peut s’accompagner d’un message d’avertissement lorsqu’une commande du package qui vient d’être chargé porte un nom identique à celui d’une commande du noyau ou à celui d’une commande déjà chargée par ailleurs. Toutes les commandes qui viennent d’être listées sont dorénavant accessibles directement, c’est-à-dire par invocation directe de leur nom33 : > A:=Matrix(3,3,[1,2,3,1,0,0,0,1,0]); ⎡ ⎤ 1 2 3 A := ⎣ 1 0 0 ⎦ 0 1 0 > Rank(A); 3 32
Voir aussi partie 2.8, page 74 à propos du package LinearAlgebra On parle dans ce cas d’invocation par le nom court de la commande par opposition à une invocation par son nom long (voir page 28).
33
28
Maple : règles et fonctions essentielles
On peut décharger un package à l’aide de la commande unwith pour libérer de l’espace dans la mémoire de travail de Maple : > >
unwith(LinearAlgebra); Rank(A); Rank(A)
Une fois le package LinearAlgebra déchargé, la commande Rank n’est plus accessible c’est pourquoi Maple répond en répétant l’instruction soumise à l’interpréteur. • Chargement de la commande : une deuxième façon, plus économique en terme de mémoire, d’accéder à une commande qui se trouve dans un package consiste à ne charger que la commande concernée, toujours à l’aide de la commande with, en passant son nom en deuxième argument : >
>
>
with(LinearAlgebra,Rank); [Rank ] A:=Matrix(3,3,[1,2,3,1,0,0,0,1,0]); ⎡ ⎤ 1 2 3 A := ⎣ 1 0 0 ⎦ 0 1 0 Rank(A); 3
• Invocation par le nom long : la troisième façon de procéder pour accéder à une commande figurant dans un package consiste à préciser, au moment de l’appel, le nom du package34 dans lequel figure la commande35 :
34
On dit que l’on appelle la commande par son nom long par opposition à son nom court (voir page 27). 35 La syntaxe adoptée qui repose sur l’opérateur de sélection indique que le package est vu structurellement comme une table dont les noms des commandes qu’il regroupe sont les indices (voir la partie 2.8.1, page 75).
1. Prise en main
29
>
>
A:=Matrix(3,3,[1,2,3,1,0,0,0,1,0]); ⎡ ⎤ 1 2 3 A := ⎣ 1 0 0 ⎦ 0 1 0 LinearAlgebra[Rank](A); 3
Cette façon de procéder est la plus économique sur le plan de la mémoire puisque la commande n’est pas chargée en mémoire comme on le vérifie sur l’exemple : >
Rank(A); Rank(A)
Pour connaître la liste de tous les packages disponibles, on consultera la page d’aide en ligne index,package.
1.8
Erreurs classiques du débutant
Nous répertorions dans cette partie des erreurs fréquemment commises par l’utilisateur débutant. La lecture de cette partie devrait permettre de faire face à certaines situations qui peuvent paraître troublantes lorsqu’on n’a pas conscience de ce qui se passe.
1.8.1
Oubli du caractère de fin d’instruction
C’est probablement la première erreur qui sera commise par le débutant lorsqu’il utilise l’interface classique36 . Elle est facile à corriger puisqu’il suffit d’ajouter un “:” ou un “;” à la fin de l’instruction. Quoi qu’il en soit, un message d’avertissement (warning) suffisamment explicite permet de comprendre son oubli : >
1+2
Warning, premature end of input 36
Dans les versions récentes du logiciel, l’interface standard, alternative à l’interface classique, n’exige pas de caractère de fin d’instruction.
30
Maple : règles et fonctions essentielles >
1+2; 3
1.8.2
Confusion entre égalité et affectation
Cette erreur est assez difficile à détecter car Maple compte le type équation (equation) parmi les types reconnus (voir la partie 2.9.1, page 88). Ainsi, l’instruction : >
b=4*x+1; b = 4x + 1
ne pose aucun problème37 à Maple, car on a écrit un objet de type équation ; cependant si l’on demande à connaître le contenu de b, on voit que l’on n’a pas effectué l’affectation de 4x + 1 à b : >
b; b
La variable b s’évalue en son nom ce qui signifie que son contenu est vide. On peut se convaincre que Maple accepte le type équation en affectant l’objet précédent à une variable : >
Eq:=b=4*x+1; Eq := b = 4 x + 1
37
Le lecteur averti conviendra néanmoins que saisir une telle instruction n’a pratiquement aucun intérêt : elle consiste à faire afficher une valeur qui ne sera plus jamais accessible puisqu’elle n’est pas affectée comme contenu à une variable.
1. Prise en main
1.8.3
31
Confusion entre pi et Pi
pi est un nom de variable licite alors que Pi est un mot réservé qui désigne la constante trigonométrique usuellement notée π en mathématiques (voir la partie 1.5.3, page 18 et partie 2.3.1, page 37). Nous espérons que désormais le lecteur ne sera plus dérouté lorsqu’il aura malencontreusement saisi : > sin(pi);evalf(sin(pi)); sin(π) sin(π) au lieu de : > sin(Pi);evalf(sin(Pi)); 0 0. qui conduit bien aux résultats espérés.
1.8.4
Confusion entre e et exp(1)
e est un nom de variable licite et si l’on souhaite évoquer la base de l’exponentielle népérienne, il convient de parler de exp(1) (voir la partie 2.3.2, page 39) : > ln(e);ln(exp(1)); ln(e) 1 >
e;exp(1); e e
>
evalf(e);evalf(exp(1)); e 2.718281828
32
Maple : règles et fonctions essentielles
1.8.5
Confusion entre i et I
L’unité imaginaire est désignée par I alors que i désigne un nom de variable licite (voir la partie 2.3.2, page 39). Averti de cette distinction, pour calculer ce qu’en mathématiques on a l’habitude d’écrire (1 + i)2 , le débutant ne perdra plus patience devant : > evalc((1+i)^2); (1 + i)2 car il saisira à juste titre : > evalc((1+I)^2); 2I
1.8.6
Oubli du caractère “ *” dans un produit
Emporté par l’habitude et les usages mathématiques, il arrive souvent que l’on oublie de saisir le caractère “*” désignant le produit dans une expression : > z:=2x; Error, missing operator or ‘;‘
Si, dans l’exemple qui précède, l’erreur se détecte aisément, grâce notamment au message d’erreur produit par l’interpréteur, il n’en est pas toujours ainsi et il arrive parfois que l’on mette beaucoup de temps à déceler le défaut dans l’expression saisie. L’exemple suivant permettra de s’en convaincre : > xy; x*y; xy xy
1. Prise en main
33
L’oubli ne déclenche pas d’erreur, ni de comportement particulier de Maple. Ce que l’on voulait être l’expression x*y est en fait vu par Maple comme un nouveau nom de variable licite xy. L’utilisateur attentif pourra remarquer que Maple fait une différence de présentation, minime mais observable, entre les résultats des évaluations respectives de xy et x*y.
1.8.7
Signe d’une constante
Maple connaît la plupart des constantes usuelles (voir la partie 2.3, page 37). L’utilisateur sera donc souvent amené à tester le signe d’une expression dans laquelle apparaît une constante. Spontanément, l’utilisateur aura tendance à écrire directement une instruction comme38 : >
evalb(Pi>0); 0<π
ce qui conduit à un résultat qui n’est pas satisfaisant39 (la partie 4.2.1, page 116 décrit une situation analogue qui se produit lors du test de la condition d’une structure if . . . then . . . else . . . end if). Pour pallier cette difficulté, la chose la plus simple à faire, dans la plupart des situations de ce type40 , consiste à recourir à la commande evalf41 pour forcer Maple à considérer une valeur numérique et non un objet symbolique42 : >
evalb(evalf(Pi)>0); true
38
La commande evalb essaie de déterminer la valeur logique de l’expression booléenne qui lui est passée en argument (voir la partie 2.4, page 41). 39 On s’attend à une réponse qui soit true ou false. Ici, Maple répète la question posée : le retour produit par evalb est non évalué (voir la partie 4.9, page 146). 40 Dans certains cas, on peut aussi recourir à la commande is (voir la partie 4.2.1, page 116). 41 La commande evalf est présentée dans la partie 2.5.5, page 52. 42 Plus précisément, Maple sait déterminer le signe des expressions de type numeric mais pas de celles de type constant (voir la partie 2.5.6, page 56).
34
1.8.8
Maple : règles et fonctions essentielles
Chronologie d’exécution et présentation matérielle
Si l’on utilise l’interface classique ou l’interface standard, il est possible de se déplacer facilement au sein d’une feuille de travail en utilisant les flèches de déplacement ou la souris. On peut ainsi valider à nouveau une instruction dont on n’a changé que quelques éléments sans la réécrire complètement. Cette souplesse d’utilisation a son revers. La chronologie des actions réalisées n’est pas nécessairement conforme à l’ordre dans lequel les instructions apparaissent sur la feuille de travail. Il est toujours préférable de travailler de manière méthodique et organisée mais lorsqu’on met au point la résolution d’un problème, il est fréquent que l’on procède par aller-retour pour affiner une idée ou un procédé, et l’on peut alors être confronté à des comportements que l’on aura du mal à expliquer. La raison probable en est que l’on n’a plus conscience de la chronologie véritable des actions. Il convient alors de réaliser une réinitialisation du système avec la commande restart (voir la partie 1.5.6, page 23). Une fois la mémoire réinitialisée, on exécute dans l’ordre voulu les calculs souhaités de manière à obtenir une présentation sur la feuille de travail en accord avec l’ordre chronologique dans lequel ont été exécutées les instructions de la feuille de travail. Nous conseillons de procéder à une réinitialisation périodique de sa feuille de travail, par exemple en plaçant un restart en son début et en réexécutant toutes les instructions dans l’ordre — ce qui peut être effectué très facilement en choisissant la rubrique Execute > Worksheet du menu Edit. Une difficulté analogue se présente lorsqu’on ouvre, par exemple à l’aide de la rubrique Open du menu Edit, une feuille de travail antérieurement enregistrée dans un fichier. On récupère la feuille de travail dans l’état où elle se trouvait au moment de son enregistrement et généralement on voit s’afficher des instructions et les résultats produits par Maple en réponse. Il faut comprendre qu’il ne s’agit là que d’un affichage et que les instructions de la feuille que l’on vient d’ouvrir n’ont pas pour autant été exécutées par Maple. Si donc on veut mettre en conformité la mémoire de travail avec le contenu de la feuille de travail, il faut réexécuter toutes les instructions de la feuille de travail, par exemple en choisissant la rubrique Execute > Worksheet du menu Edit.
Chapitre 2
Types fondamentaux 2.1
Préambule
Certains utilisateurs pensent que Maple manque de rigueur mais ils sont induits en erreur par la souplesse de son langage. En effet, Maple n’est pas fortement typé comme C ou Caml, en ce sens que l’on n’exige pas de l’utilisateur que le type d’une nouvelle variable soit déclaré au moment même de sa définition. Cependant, Maple reconnaît un grand nombre de types qui s’inscrivent dans une hiérarchie arborescente (partie 2.2). Pour en connaître la liste ainsi que l’arborescence, le lecteur est invité à se reporter à la page d’aide en ligne de la commande type. Tous ces types peuvent être utilisés pour filtrer les paramètres au moment de l’appel d’une fonction (voir la partie 4.4, page 129) ; Maple propose ainsi tous les fondements nécessaires à une programmation rigoureuse. Nous allons seulement présenter quelques types qui sont d’une utilisation fréquente et dont il convient de connaître les propriétés fondamentales. C’est souvent la méconnaissance de ces caractéristiques qui conduit certains utilisateurs à croire que Maple ne répond pas à leurs attentes. En outre, ces types, que l’on peut composer à souhait, permettent d’écrire des fonctions tirant bénéfice de la puissance du filtrage des arguments au moment de l’appel. Ainsi, nous allons considérer successivement les constantes (partie 2.3), les booléens (partie 2.4), différents types de nombres (partie 2.5) — ce qui sera l’occasion de constater la richesse des descriptions possibles avec Maple —, les types collectifs (partie 2.6) pouvant décrire des énumérations d’objets — parmi lesquels les séquences, les ensembles et les listes —, les chaînes de caractères (partie 2.7) et enfin les tables avec les tableaux et les matrices qui en dérivent (partie 2.8). Nous présenterons pour chaque type les commandes et opérateurs qui permettent de le manipuler efficacement.
36
Maple : règles et fonctions essentielles
2.2
L’arborescence des types de Maple
On distingue différentes catégories de types. L’ensemble des types surfaces 1 (voir l’aide en ligne sur type[surface]) rassemble ceux qui, lors du filtrage, ne nécessitent pas le parcours récursif de l’arbre sous-jacent2 à la variable filtrée, mais seulement la connaissance de la racine de cet arbre. Parmi ces types figurent, entre autres, les booléens (bool), les nombres entiers (integer), les nombres flottants (float), les listes (list), les ensembles (set) et les chaînes de caractères (string). L’ensemble des types imbriqués regroupe ceux qui, lors du filtrage, nécessitent le parcours récursif complet de l’arbre sous-jacent à la variable filtrée. Par exemple, le type constant (qui sert à déterminer si une expression est une constante au sens Maple) figure parmi les types imbriqués. Ces deux familles de types définis dans le noyau de Maple servent de base à d’autres types. Les types structurés (voir l’aide en ligne sur type[structured]) sont plus élaborés et leur structure repose sur l’utilisation de types surfaces. Par exemple, la liste [1,2,3] est une liste de type (structuré) list(integer). Ces types sont définis à l’aide d’une grammaire qui permet de décrire leur structure. L’utilisateur peut aussi définir de nouveaux types pour ses propres besoins. Pour en savoir davantage sur la manière dont Maple reconnaît un type avec la commande type et comment définir un nouveau type spécifique à ses besoins, se reporter à la page d’aide type[definition] et à [8]. Le type le plus général est le type anything qui englobe tous les autres types. Afin de déterminer si une variable relève d’un type particulier, on utilise la commande type : >
>
type(sqrt(2),constant); true type(sqrt(2),radical); true
Cet exemple montre qu’un même objet Maple peut relever de deux types différents. En l’occurrence, constant est un type plus général que radical. 1 Nous reprenons ici le terme employé par les concepteurs du logiciel. Le terme type simple serait peut-être plus approprié. 2 Se reporter au chapitre 5 (page 161) pour la description de la structure arborescente sous-jacente à toute variable Maple.
2. Types fondamentaux
2.3
37
Constantes
Les entiers, les fractions (représentations des nombres rationnels), les nombres à virgule flottante ainsi que les nombres complexes dont les parties réelle et imaginaire relèvent de l’un des cas précédents sont des constantes numériques pour Maple. Par ailleurs, la variable prédéfinie globale3 constants contient la séquence4 de tous les noms initialement connus par Maple comme des constantes symboliques. >
constants; false, γ, ∞, true, Catalan, FAIL, π
Nous allons voir que dans cette réponse figurent des constantes numériques célèbres ainsi que des constantes booléennes.
2.3.1
Constantes numériques célèbres connues de Maple
Il convient de bien connaître la façon dont on désigne les constantes mathématiques usuelles dans la syntaxe du langage (se reporter notamment au 1.5.3 page 18 pour l’obtention de lettres grecques avec Maple et pour les confusions à éviter à ce propos). La constante π est désignée par Pi (avec un P majuscule), à ne pas confondre avec la variable de nom π que l’on obtient par l’écriture pi (avec un p minuscule). Les calculs suivants montrent que Maple connaît maintes propriétés5 du nombre π : >
Pi;
>
evalf(Pi);
>
cos(Pi);
π 3.141592654 −1 3
La notion de variable globale est abordée au 4.5, page 131. Se reporter au 2.6.1, page 59 pour savoir précisément en quoi consiste une séquence pour Maple. Pour le moment, le lecteur peut la considérer comme une énumération dans laquelle les objets sont séparés par des virgules. 5 Se reporter au chapitre 5, page 161, pour connaître la représentation interne qui permet à Maple d’être informé de ces propriétés. 4
38
Maple : règles et fonctions essentielles >
sin(Pi); 0
>
arccos(0);
π 2 Maple connaît la constante d’Euler : >
gamma; γ
>
evalf(gamma); 0.5772156649
ainsi que sa définition6 : >
Limit(Sum(1/k,k=1..n)-ln(n),n=infinity)=limit(sum(1/k, k=1..n)-ln(n),n=infinity) ; n
1 − ln(n) = γ lim n→∞ k k=1
Il en va de même avec la constante de Catalan : >
Limit(Sum((-1)ˆn/(2*n+1)ˆ2,n=0..infinity)=sum((-1)ˆn /(2*n+1)ˆ2,n=0..infinity) ; ∞
(−1)n = Catalan (2 n + 1)2 n=0
>
evalf(Catalan); 0.9159655942
6
Dans cet exemple, comme dans le suivant, la réponse de Maple présente une écriture proche de l’écriture mathématique usuelle pour le membre de gauche. Celui-ci résulte d’un effet du pretty-printer à travers l’utilisation des commandes Limit et Sum, homologues inertes des commandes limit et sum (voir la partie 1.5.4, page 21).
2. Types fondamentaux
39
Les exemples précédents montrent aussi que Maple désigne l’infini7 par le mot-clé infinity : >
infinity; ∞
2.3.2
Autres constantes numériques usuelles
L’unité imaginaire qui permet l’écriture des nombres complexes est désignée par I. La majuscule a été préférée à la minuscule, pourtant usuelle en mathématiques, car i désigne souvent un compteur de boucle en programmation. En fait, I n’est pas une constante pour Maple mais un alias (soit un synonyme) pour la racine carrée principale8 de −1 : >
I^2; −1
>
solve(x^2+4=0,x);
>
sqrt(-1);
2 I, −2 I I La base e de l’exponentielle népérienne ne figure pas au rang des constantes initialement connues par Maple mais s’obtient comme un effet du pretty-printer par : >
exp(1); e
>
evalf(exp(1)); 2.718281828
7
De même, −∞ s’obtient par -infinity. La fonction racine carrée (n’)est définie (que) sur l’ensemble des nombres réels positifs. Toutefois, la commande sqrt s’applique aux nombres complexes en choisissant la racine carrée dite principale (c’est-à-dire celle dont la partie réelle est positive, ou dont la partie réelle est nulle et la partie imaginaire positive).
8
40
Maple : règles et fonctions essentielles
Le nom e est donc libre pour désigner une variable et le lecteur attentif remarquera la différence que fait le pretty-printer entre l’affichage d’une telle variable et celui de la base de l’exponentielle népérienne. En effet, la règle veut que Maple distingue, en général, les identificateurs connus des identificateurs libres (les variables) en faisant figurer les premiers en caractères droits et les seconds en caractères italiques : > exp(1), e; e, e
2.3.3
Introduire une nouvelle constante dans le système
L’utilisateur peut ajouter des constantes à la séquence initialement connue de Maple de la manière suivante. Après avoir vérifié, par exemple, que g n’y figure pas, on l’ajoute9 au contenu de constants : > type(g,constant); >
>
false constants:=constants,g; constants := false, γ, ∞, true, Catalan, FAIL, π, g type(g,constant); true
Remarquons que la constante ainsi créée peut prendre n’importe quelle valeur souhaitée par l’utilisateur, alors que les constantes initialement connues de Maple sont des noms protégés dont on ne peut pas modifier directement le contenu : > g:=9.81; g := 9.81 > gamma:=10; Error, attempting to assign to ‘gamma‘ which is protected
9
Se reporter à la partie 2.6.1, page 59, pour comprendre le procédé mis en œuvre pour étendre le contenu de la variable constants qui s’appuie sur les propriétés des séquences.
2. Types fondamentaux
41
S’il souhaite pouvoir disposer de γ, l’utilisateur doit enlever à l’aide de la commande unprotect la protection qui est active sur les mots réservés par Maple. Bien évidemment, ceci n’est pas recommandé. Maple connaît aussi la plupart des constantes physiques usuelles et permet d’y accéder à travers la mise en œuvre du package ScientificConstants. L’utilisateur désirant ajouter une constante à la liste des constantes déjà connues de Maple aura intérêt à d’abord consulter ce package.
2.4
Booléens
Les valeurs booléennes true et false sont répertoriées parmi les constantes initialement connues. À ces deux valeurs logiques présentes dans la plupart des langages informatiques vient s’ajouter un troisième état désigné par FAIL (en majuscules, contrairement à true et false). La réponse FAIL à une requête signifie que les éléments d’information mis à la disposition de l’interpréteur sont insuffisants pour prendre une décision. La logique mise en œuvre par Maple comporte donc trois états10 . Les expressions logiques sont formées à partir de variables logiques et de connecteurs logiques comme and, or, xor, implies et not (qui correspondent respectivement à la conjonction, la disjonction, la disjonction exclusive, l’implication et la négation logiques). Les tables de vérité binaires des connecteurs logiques usuels s’étendent naturellement à des tables à trois états, si l’on se souvient que FAIL signifie à peu près « je ne sais pas » (voir table 2.1). a true true true false false false FAIL FAIL FAIL
b true false FAIL true false FAIL true false FAIL
a and b true false FAIL false false false FAIL false FAIL
a or b true true true true false FAIL true FAIL FAIL
not a false
true
FAIL
Table 2.1 – Logique à trois états en vigueur dans Maple : tables de vérité du « et » (and), du « ou » (or) et du « non » (not) logiques. 10 Il faut savoir que dans le cadre d’une conditionnelle if ... then ... else ... un résultat FAIL lors du test de la condition entraîne l’exécution de la partie else, comme le ferait un résultat false. Voir partie 4.2.1, page 116.
42
Maple : règles et fonctions essentielles
La commande evalb force l’évaluation d’expressions comprenant des opérateurs relationnels (test d’égalité11 “=”, vérification d’inégalités comme “<”, “<=”, “>”, “>=” et “<>”) qui, sinon, seraient considérées comme des objets algébriques de type équation ou inéquation et ne seraient pas évaluées à leur valeur logique. > 1=2; 1=2 Maple a accepté l’entrée proposée, mais ne fait rien car cet objet est de type équation et l’utilisateur n’indique pas d’action à exécuter sur cet objet. Le recours à evalb conduit Maple à évaluer la valeur logique de l’objet passé en argument, en le considérant comme un test : > evalb(1=2); false Lorsque Maple ne dispose pas de suffisamment d’informations, evalb se comporte comme la plupart des commandes et se contente de répéter la question posée : > evalb(x>0); 0<x > evalb(2<sqrt(5)); √ 2< 5 Afin de parvenir à ses fins dans ce dernier exemple, il convient de forcer la conversion12 numérique de l’objet symbolique sqrt(5) à l’aide de la commande evalf (détaillée dans la partie 2.5.5, page 52) : > evalb(2<evalf(sqrt(5))); true 11
À distinguer soigneusement de l’affectation “ : =”. Il s’agit là d’un problème rencontré fréquemment par les débutants lorsqu’ils écrivent des programmes incluant des tests logiques.
12
2. Types fondamentaux
43
Enfin voici une situation impliquant des nombres complexes (voir la partie 2.5.4, page 50 pour des précisions sur le type complexe) dans laquelle Maple dit explicitement ne pas être en mesure de répondre à la question posée : >
evalb(2+3*I>1+I); FAIL
2.5
Nombres
Maple met à disposition une grande variété de types de nombres. Les types entier et flottant (ou nombre à virgule flottante), qui existent dans la plupart des langages de programmation, bénéficient avec Maple d’une possibilité de représentation infinie (aux limites de l’équipement matériel sous-jacent près) ; l’apanage des systèmes de calcul formel est justement de permettre l’exécution de calculs exacts sur les entiers de taille conséquente ou sur les flottants avec une précision aussi grande qu’on le souhaite. D’autres types de nombres, comme les nombres rationnels ou les nombres complexes, peuvent être représentés directement, ce qui est spécifique aux systèmes de calcul formel. Nous décrirons les types de nombres les plus importants en signalant leurs propriétés et en détaillant des commandes qui les concernent. Nous pourrons ainsi constater que Maple pratique implicitement la surcharge (voir la partie 2.5.7, page 58)pour les opérations arithmétiques fondamentales que sont l’addition, la soustraction, la multiplication13 et la division (respectivement notées “+”, “-”, “*” et “/”) et pour l’élévation à une puissance entière (notée “ˆ”). Lorsque différents types de nombres sont présents dans une expression, Maple utilise le type le plus particulier (au sens de l’inclusion dans l’arborescence des types de Maple) englobant tous ces nombres pour représenter le résultat.
2.5.1
Nombres entiers relatifs
Au premier abord, le type entier relatif (type integer) ainsi que les notations des opérations arithmétiques sur les entiers ne distinguent pas Maple des autres langages de programmation : 13
On gardera à l’esprit que Maple distingue certains produits non commutatifs, comme le produit matriciel, en les désignant par “&*” ou “.” selon les types considérés. Se reporter en particulier à la partie 2.8.3, page 82 pour le produit matriciel.
44
Maple : règles et fonctions essentielles >
((1+2)*(9-7))^2; 36
Les priorités entre opérations sont usuelles. En revanche, Maple en tant que système de calcul formel, est capable de représenter un nombre entier arbitrairement grand, dans la mesure des capacités matérielles du système sous-jacent14 . C’est ce que montre l’exemple suivant qui fait intervenir un calcul de factorielle15 : >
a:=15!; a := 1307674368000
La valeur de a n’aurait pas été aussi simplement accessible avec un langage de programmation comme C, Ada ou Caml avec lesquels un calcul naïf direct de 15! aurait provoqué un débordement de capacité. Il est possible de connaître le nombre de chiffres d’un entier à l’aide de la commande length : >
length(a); 13
ou d’obtenir une décomposition en facteurs premiers de ce nombre avec la commande ifactor : >
ifactor(a); (2)11 (3)6 (5)3 (7)2 (11) (13)
14
Le nombre de chiffres maximal que peut comporter un entier est indiqué par la variable kernelopts(maxdigits) qui vaut 268435448 sur le système (un classique PC 32 bits) utilisé par l’auteur au moment de la rédaction de cet ouvrage. 15 La factorielle s’obtient indifféremment avec la commande factorial ou avec l’opérateur postfixe “ !”.
2. Types fondamentaux
45
Maple fournit un test de primalité à travers la commande isprime : > isprime(7); true > isprime(8); false Par ailleurs, voici une manière16 d’obtenir les dix premiers nombres premiers avec la commande ithprime (qui permet d’obtenir le i-ième nombre premier) : > seq(ithprime(k),k=1..10); 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 Les commandes iquo et irem déterminent respectivement le quotient et le reste d’une division euclidienne de nombres entiers : > iquo(31,10); 3 > irem(31,10); 1 Toujours dans le domaine de l’arithmétique, igcd et ilcm fournissent respectivement le plus grand commun diviseur et le plus petit commun multiple de deux entiers : > igcd(12,30); 6 > ilcm(3,4); 12 16
Se reporter à la partie 2.6.1, page 59, pour des explications détaillées concernant l’obtention de séquences à l’aide de la commande seq.
46
Maple : règles et fonctions essentielles
Les commandes que l’on vient de présenter portent un nom qui commence par un i (pour integer ) pour rappeler qu’elles ont vocation à s’appliquer à des arguments de type entier. Ces commandes, qui relèvent de l’arithmétique, admettent des analogues dépourvus du préfixe i (quo, rem, gcd et lcm) qui s’appliquent aux polynômes. Terminons par les congruences. L’opérateur mod évalue une expression modulo un entier : >
231*7 mod 5; 2
Enfin, l’opérateur “&ˆ” implémente l’exponentiation rapide dans un anneau de congruences : >
2008 &^ 2008 mod 12; 4
ainsi que l’inversion modulaire, lorsqu’elle est possible17 : >
2008 &^ (-1) mod 13; 11
Maple offre ainsi la possibilité d’effectuer des calculs faisant intervenir des polynômes ou des matrices sur des corps finis (se reporter à l’aide en ligne sur mod pour un panorama plus complet des possibilités de Maple en la matière). Les entiers sont reconnus par le type integer. Ce type en englobe de plus particuliers qui peuvent s’avérer utiles comme posint (qui recouvre les entiers strictement positifs), nonnegint (qui recouvre les entiers positifs ou nuls) ainsi que leurs homologues respectifs negint et nonposint. 17
L’exemple qui précède, comme celui qui suit, montre la priorité de l’opérateur “&ˆ” sur l’opérateur mod, ce qui dispense d’écrire des parenthèses pour délimiter ce qui se trouve à gauche de mod.
2. Types fondamentaux
47
>
type(0,posint); false
>
type(0,negint); false
>
type(0,nonposint); true
>
type(1,posint);
>
type(-2,posint);
true false Les packages numtheory et combinat fournissent des commandes supplémentaires pour traiter de situations relevant de la théorie des nombres (développement en fractions continues, symboles de Legendre, de Jacobi, de Kronecker, etc.) et de la combinatoire (coefficients binomiaux, nombres de Stirling, etc.).
2.5.2
Nombres rationnels
Comme on peut le constater sur les exemples qui suivent, Maple transforme une expression arithmétique comportant des nombres rationnels18 en sa représentation normale et conserve sa représentation exacte : >
r:=2/3+5/7; r :=
29 21
On dispose des commandes numer et denom pour obtenir le numérateur et le dénominateur d’un nombre rationnel19 :
18
Pour être précis, il conviendrait de dire que Maple transforme une expression ne comportant que des rationnels ou des entiers (qui par surcharge sont vus comme des rationnels), à l’exclusion de tout autre type de nombre, en un nombre de type rationnel. 19 Ces commandes s’appliquent aussi aux fractions rationnelles.
48
Maple : règles et fonctions essentielles >
numer(r), denom(r);
>
numer(r);
>
denom(r);
29, 21 29 21 Pour connaître une valeur décimale approchée d’un nombre rationnel, il faut le demander explicitement20 à Maple, par exemple en utilisant la commande evalf (sur laquelle nous reviendrons au 2.5.5, page 52) : >
evalf(r); 1.380952381
Le type rational (qui reconnaît les fractions mais aussi les entiers relatifs) est plus général que le type fraction (lequel ne reconnaît que les fractions rationnelles non réductibles à un entier : >
type(1,fraction); false
>
type(1,rational);
>
type(1/2,fraction);
true true >
type(1/2,rational); true
20
Le principe général d’un logiciel de calcul formel est de conserver, autant que possible, la représentation exacte de l’objet considéré ou le plus haut niveau de généralité dans sa description. L’utilisateur décide s’il veut une représentation dégradée (non exacte ou moins générale).
2. Types fondamentaux
2.5.3
49
Radicaux
La commande sqrt permet de représenter des radicaux21 de manière exacte : >
sqrt(2);
√
2
Comme dans le cas des nombres rationnels, le recours à evalf force Maple à donner une valeur décimale approchée d’un radical : >
evalf(%); 1.414213562
Il est possible de savoir si une expression est un radical à l’aide du type radical : >
type(sqrt(5),radical);
>
true type(1+sqrt(3),radical);
>
false type(sqrt(1-x^2),radical); true
21
La commande sqrt calcule des racines carrées. Pour obtenir une racine n-ième, il faut recourir à la commande root. Par exemple, root(8,3) ou root[3](8) donne la racine cubique de 8.
50
Maple : règles et fonctions essentielles
2.5.4
Nombres complexes
Nous avons déjà vu (voir la partie 2.3.2, page 39) que Maple désigne l’unité imaginaire par I. Un nombre est de type complex s’il peut être ramené à la forme a+I*b où a et b sont des constantes réelles pour Maple (précisément de type realcons). >
a:=1+2*I;
>
b:=sqrt(3)-(1/2)*I;
a := 1 + 2 I
b :=
√
3−
1 I 2
Signalons au passage que les entiers de Gauss sont représentés naturellement par le type structuré complex(integer). Maple propose en outre le package GaussInt qui fournit toutes les primitives utiles pour calculer dans l’anneau des entiers de Gauss. On peut, bien entendu, effectuer toutes les opérations usuelles sur les nombres complexes : >
a+b; 1+
>
√ 3 I+ 3 2
a*b; √ 1 (1 + 2 I) ( 3 − I) 2
mais il convient de recourir à la commande evalc pour forcer la mise sous forme algébrique du résultat : >
c:=evalc(a*b); c := 1 +
√
3 + (2
√
3−
1 )I 2
Si l’on applique evalf (voir la partie 2.5.5, page 52) à un nombre complexe, Maple propose une valeur décimale approchée pour les parties
2. Types fondamentaux
51
réelle et imaginaire du nombre considéré (après l’avoir mis sous forme algébrique) : >
evalf(c); 2.732050808 + 2.964101616 I
Le module, l’argument, la partie réelle, la partie imaginaire et le conjugué d’un nombre complexe s’obtiennent respectivement avec les commandes abs22 , argument, Re, Im et conjugate : >
>
abs(a);
√
5
argument(a); arctan(2)
>
Re(a); 1
>
Im(a); 2
>
conjugate(a); 1 − 2I
La forme trigonométrique d’un nombre complexe s’obtient à l’aide d’une option de la commande convert ou directement à l’aide de la commande polar : >
z1:=convert(a,polar);
√ z1 := polar( 5, arctan(2))
Pour passer de la forme trigonométrique à la forme algébrique d’un nombre complexe, il suffit d’utiliser la commande evalc : 22
Comme en mathématiques, la commande abs appliquée à un nombre réel conduit à l’évaluation de sa valeur absolue.
52
Maple : règles et fonctions essentielles >
z2:=polar(a);
>
evalc(z1);
√ z2 := polar( 5, arctan(2)) 1 + 2I
Pour terminer, signalons que Maple effectue par défaut tous ses calculs dans le corps des complexes23 et que pour restreindre le champ d’évolution de certaines variables considérées dans un problème, il faut le spécifier explicitement, par exemple à l’aide de la commande assume (se reporter à l’aide en ligne) : >
z:=a+I*b; z := a + b I
>
> >
abs(z); |a + b I| assume(a,real); assume(b,real); abs(z); a˜2 + b˜2
Dans cet exemple, si l’on n’émet pas d’hypothèse sur a et b, Maple propose une réponse générale (en l’occurrence, il ne se produit qu’une activation du pretty-printer pour une présentation en accord avec les usages mathématiques). Dès que l’on précise que a et b sont des variables réelles, Maple fournit un résultat24 plus précis.
2.5.5
Nombres à virgule flottante
Les nombres à virgule flottante (ou plus simplement les flottants) admettent avec Maple une précision arbitraire (aux limites matérielles de la plateforme sous-jacente près). En d’autres termes, les flottants en Maple sont une implémentation des nombres décimaux. La virgule décimale d’un 23
Cela est aussi le cas pour les valeurs calculées par la plupart des fonctions connues par Maple. 24 On remarque la présence de tildes “˜” qui suivent a et b dans la réponse : ces signes rappellent à l’utilisateur que la validité de la réponse affichée est subordonnée à des hypothèses portant sur ces variables.
2. Types fondamentaux
53
nombre à virgule flottante est, comme dans la plupart des langages de programmation, désignée par un point (“.”) et les usages habituels, comme la notation scientifique, sont en vigueur sous Maple : >
a:=1.23; a := 1.23
>
b:=2.; b := 2.
>
c:=-.2; c := −0.2
>
d:=1.1e4; d := 11000.
La commande evalf force l’évaluation, sous forme de flottant25 , de l’expression qui lui est passée en argument : >
evalf(Pi); 3.141592654
Par défaut, evalf propose 10 chiffres significatifs, mais on peut obtenir une précision plus grande en spécifiant le nombre de chiffres significatifs requis sous forme d’argument optionnel passé entre crochets26 à la commande evalf : >
evalf[30](Pi); 3.14159265358979323846264338328
On peut aussi modifier le comportement par défaut de Maple afin d’obtenir systématiquement une précision plus grande. Cela est possible en 25
Ou sous forme de complexe dont les parties réelle et imaginaire sont de type flottant. L’instruction evalf[15](Pi) ; peut aussi être écrite evalf(Pi,15) ;. Bien que la première syntaxe soit celle préconisée par l’aide en ligne, la deuxième syntaxe est bien plus souvent utilisée dans les manuels. Les deux formes sont reconnues par les version actuelles du logiciel.
26
54
Maple : règles et fonctions essentielles
changeant le contenu de la variable d’environnement Digits dont la valeur initiale par défaut est 10 au début d’une session Maple : >
Digits:=20; Digits := 20
>
evalf(Pi); 3.1415926535897932385
L’option rational de la commande convert permet d’obtenir un nombre rationnel dont la valeur décimale s’approche du flottant passé en argument27 : >
>
>
convert(0.125,rational); 1 8 convert(evalf[5](Pi),rational); 3927 1250 convert(evalf[10](Pi),rational); 104348 33215
Nous avons constaté sur certains exemples que Maple gérait des flottants avec une précision arbitraire. Bien évidemment, cette précision a un coût : celui du traitement supplémentaire effectué pour obtenir une précision arbitraire, sachant que Maple est tributaire d’une plateforme dont le processeur a une limite de précision matérielle pour les opérations arithmétiques usuelles (précision relative de l’ordre de 10−15 sur le matériel courant au moment de la rédaction de cet ouvrage). Cependant, Maple fournit aussi la possibilité d’effectuer des calculs de manière plus efficace par le biais du type hfloat (pour hardware float) qui impose des calculs en précision limitée, gérés directement par le processeur de la plateforme sousjacente à Maple. L’expression passée en argument à la commande evalhf est évaluée au format d’un flottant (en double précision). 27
C’est en quelque sorte la réciproque de la restriction de la fonction evalf au type rational pour une valeur de Digits donnée. Bien sûr, le rationnel obtenu dépend de la précision demandée, se reporter à l’aide en ligne sur convert.
2. Types fondamentaux
55
>
evalhf(exp(1)); 2.71828182845904509
On appréciera la réduction du temps d’exécution procurée par evalhf sur l’exemple suivant. La commande28 time fournit l’heure de l’horloge interne. La boucle for effectue le calcul des 10000 premiers termes de la suite récurrente définie par les relations un+1 = sin(un ) et u0 = 2.5 : > restart: t:=time(): u:=2.5: for k from 1 to 10000 do u:=evalf[15](sin(u)) end do: time()-t; 0.901 > restart: t:=time(): u:=2.5: for k from 1 to 10000 do u:=evalhf(sin(u)) end do: time()-t; 0.050 Les commandes round, trunc, floor, ceil et frac implémentent des fonctions utiles pour les nombres de type flottants29 . L’instruction round(x) calcule l’entier le plus proche de x. L’instruction trunc(x) calcule l’entier le plus proche de x en allant vers 0 (« le nombre avant la virgule avec le signe »). L’instruction floor(x) (respectivement l’instruction ceil(x)) calcule le plus grand entier inférieur ou égal à x (respectivement le plus petit entier supérieur ou égal à x). L’instruction frac(x) calcule x - trunc(x) (« le nombre après la virgule avec le signe ») : >
28
round(1.15);round(1.95);round(-1.15);round(-1.95); 1 2
time est bien une fonction qui ne prend aucun argument et non une variable : l’appel se fait sous la forme time() et non time. 29 Ces commandes s’appliquent aussi aux nombres de type complexe ; pour bien appréhender le comportement de certaines d’entre elles, il est conseillé de se reporter à l’aide en ligne correspondante.
56
Maple : règles et fonctions essentielles
>
>
>
>
−1 −2 trunc(1.15);trunc(1.95);trunc(-1.15);trunc(-1.95); 1 1 −1 −1 floor(1.15);floor(-1.15); 1 −2 ceil(1.15);ceil(-1.15); 2 −1 frac(1.15);frac(-1.15); 0.15 −0.15
Enfin, mentionnons pour terminer cette partie consacrée aux flottants la commande min (respectivement max) qui détermine le plus petit élément (respectivement le plus grand élément) d’une énumération de nombres30 : >
min(2,-4,3.5,7.75); −4
>
max(3.35,-4,13,2.75); 13
2.5.6
Le type numeric
Le type numeric permet de reconnaître les nombres entiers (type integer), les rationnels (type rational et type fraction) ou les nombres flottants (type float). Le type numeric est donc utile pour spécifier que l’on souhaite traiter une variable qui s’évalue en une valeur numérique (ce qui garantit notamment que Maple est capable d’en déterminer le signe), à l’exclusion, entre autres, des variables qui s’évaluent en une valeur de type constant (dont Maple n’est pas capable de déterminer le signe) : 30
min et max sont des commandes qui acceptent un nombre variable d’arguments.
2. Types fondamentaux
57
>
type(3,numeric); true
>
type(-2.71,numeric); true
>
type(2/3,numeric); true
>
type(Pi,numeric); false
>
type(sqrt(2),numeric); false
Le type extended_numeric reconnaît les variables de type numeric ainsi que infinity, -infinity et undefined (indéfini) : >
type(infinity,numeric);
>
false type(infinity,extended_numeric); true
À l’instar du type integer (voir la partie 2.5.1, page 43), le type extended_numeric englobe des types plus particuliers qui peuvent s’avérer utiles comme positive (qui recouvre les nombres strictement positifs), nonnegative (qui recouvre les nombres positifs ou nuls) ainsi que leurs homologues respectifs negative et nonpositive : >
type(0,positive);
>
type(0,nonnegative);
false true >
type(-1.63,negative);
>
true type(-infinity,nonpositive); true
58
Maple : règles et fonctions essentielles >
type(x^2,nonnegative);
>
false type(sqrt(8),positive); false
2.5.7
Maple pratique la surcharge
Terminons cette partie consacrée à la gestion des différents types de nombres sous Maple en soulignant un aspect apparu précédemment dans certains exemples. Maple pratique la surcharge31 et donne un résultat dans le type le plus général possible qui englobe les opérandes considérés : >
1+2/3; 5 3
>
2/3 + 1.87656443;
>
z:=1+I; z+ 2/3;
2.543231097 z := 1 + I 5 +I 3
2.6
Séquences, listes et ensembles
Les séquences, les listes et les ensembles sont tous des types qui offrent au programmeur des structures de données agréables et puissantes permettant de décrire des collections d’objets. En outre, les listes et les ensembles sont intrinsèquement dépendants des séquences, ce qui justifie la présentation groupée de ces types dans cette partie. 31
La surcharge est la capacité du langage de programmation d’avoir le même nom pour plusieurs fonctions différentes et de procéder au choix de la méthode idoine selon le nombre ou les types des arguments transmis au moment de l’appel à la fonction. Par exemple, l’addition, notée “+” quels que soient les types des opérandes, va adapter son comportement (en particulier le type du résultat retourné) aux types des opérandes rencontrés.
2. Types fondamentaux
2.6.1
59
Séquences
Une séquence32 est une énumération finie d’objets Maple séparés par des virgules. Une séquence n’est pas délimitée par des caractères spécifiques alors que des crochets ou des accolades délimitent respectivement les listes et les ensembles. Cette caractéristique fait la commodité des séquences pour les manipulations élémentaires (en particulier pour la mise en séquence de plusieurs séquences), mais elle leur confère aussi une spécificité : les séquences ne peuvent généralement pas être passées directement comme argument à une commande ou à une fonction lors d’un appel (se reporter notamment au chapitre 3, page 91 pour la procédure mise en œuvre dans Maple lors d’un appel à une fonction). >
s:=a,1,b,2;
>
z:=10;
>
t:=2*Pi;
>
r:=z,t,z;
s := a, 1, b, 2 z := 10 t := 2 π r := 10, 2 π, 10 On peut accéder à un élément d’une séquence grâce à l’opérateur de sélection qui utilise une syntaxe faisant intervenir les crochets (“[” et “]”), comme il est usuel de le faire dans d’autres langages pour accéder aux éléments d’un tableau. Ici, on choisit de récupérer le deuxième élément de la séquence s : >
s[2]; 1
Indiquons au passage que l’ordre des éléments d’une séquence est contractuel (comme pour les listes et contrairement aux ensembles) ce qui légitime cette façon de procéder. 32 Nous avons choisi la traduction littérale par le terme séquence » plutôt que de suivre la traduction mathématique et de traduire sequence par « suite », car les propriétés des séquences Maple sont assez éloignées des propriétés mathématiques des suites.
60
Maple : règles et fonctions essentielles
La propriété fondamentale des séquences est la suivante : une séquence de séquences s’aplatit en séquence. En d’autres termes, en concaténant des séquences, on obtient une séquence de même niveau que les séquences initiales ; la hiérarchie des séquences assemblées ne se conserve pas. Ainsi, il est impossible à partir de la séquence résultat de connaître celles qui ont servi à la composer (contrairement au cas d’une liste de listes ou d’un ensemble d’ensembles). >
s,s; a, 1, b, 2, a, 1, b, 2
>
s,p,s; a, 1, b, 2, p, a, 1, b, 2
Maple met à la disposition de l’utilisateur la commande seq pour créer des séquences présentant un motif régulier avec une syntaxe simple et évidente. Noter, sur les exemples suivants, que l’intervalle définissant la variation de la variable muette qui intervient dans le motif est défini par deux nombres obligatoirement entiers séparés par deux points (voir la partie 2.9.2, page 89). Le lecteur est invité à consulter l’aide en ligne pour connaître des variantes à cette syntaxe. La commande seq s’avère pratique et efficace ; elle remplace avantageusement des boucles for dans certains schémas de calculs itératifs : >
k:=3;
k := 3 > seq(cos(k*Pi/23),k=1..6); 6 5 4 3 2 1 cos( π), cos( π), cos( π), cos( π), cos( π), cos( π) 23 23 23 23 23 23 L’opérateur “$” permet de créer des séquences comportant des répétitions d’objets identiques : >
x$5; x, x, x, x, x
2. Types fondamentaux
61
Ce procédé trouve une application fréquente dans le calcul de dérivées d’ordres élevés, comme dans l’exemple suivant où l’on calcule la dérivée33 cinquième de x → sin(3x + 2) : >
diff(cos(3*x+2),x$5); −243 sin(3 x + 2)
L’opérateur “$” permet aussi de fabriquer des suites d’entiers consécutifs : >
$2..6; 2, 3, 4, 5, 6
La séquence vide est désignée par NULL. On ne peut pas la visualiser car, rappelons-le, une séquence n’a pas de délimiteur : >
NULL;
On peut, par exemple, vérifier que x$0 produit bien une séquence vide de la manière suivante (les parenthèses autour de x$0 sont nécessaires pour des raisons de priorité entre les opérateurs “$” et “=”) : >
evalb((x$0)=NULL); true
Ces caractéristiques de la séquence vide contribuent à rendre délicate la manipulation des séquences. Lorsqu’on travaille avec une séquence, il est souvent préférable de la transformer en liste pour manipuler ses éléments sans risquer des problèmes liés à la présence éventuelle d’une séquence vide ou à la propriété d’aplatissement des séquences. Précisons qu’il n’existe pas de type séquence à proprement parler car un tel type ne pourrait pas être reconnu par la commande type, notamment 33
Se reporter à l’aide en ligne pour une présentation détaillée de la commande diff.
62
Maple : règles et fonctions essentielles
en raison des mécanismes mis en œuvre au moment d’un appel de fonction ou de commande (voir la partie 3.6, page 105). En revanche, il est possible de reconnaître une séquence (comportant plus d’un élément) à l’aide de la commande whattype. Le type reconnu par Maple est dans ce cas le type exprseq : >
s:=1,2,3; s := 1, 2, 3
>
whattype(s); exprseq
Cependant, l’exemple suivant met en évidence les problèmes34 que pose l’utilisation des séquences lorsqu’une séquence s’avère ne comporter en définitive qu’un seul élément : >
s:=1;t:=NULL;u:=s,t; s := 1 t := u := 1
>
whattype(u); integer
En vertu de la propriété d’aplatissement des séquences, la séquence résultat u ne contient qu’un seul élément. L’interpréteur ne voit alors plus u comme une séquence à un élément mais comme une variable du type du contenu de u (en l’occurrence u est de type entier).
34
Ces difficultés ont notamment conduit les développeurs de Maple à interdire l’accès direct à certaines séquences gérées intrinsèquement par le système. C’est le cas, par exemple, de la séquence contenue par la variable intrinsèque _params (voir la partie 4.7, page 135).
2. Types fondamentaux
2.6.2
63
Ensembles
Un ensemble Maple (variable de type set) s’obtient en délimitant une séquence par des accolades : > E:={$1..6}; E := {1, 2, 3, 4, 5, 6} Contrairement à certains langages comme Caml, Maple n’exige pas que tous les éléments d’un ensemble relèvent du même type : > F:={1,2,3,a,2,a,b,4}; F := {1, 2, 3, 4, a, b} On constate sur l’exemple précédent que, conformément à la règle mathématique, Maple supprime les doublons. Par ailleurs, toujours selon cet usage, l’ordre des éléments d’un ensemble n’est pas contractuel : > evalb({1,2,3}={2,1,3}); true Cependant, même s’il faut pour des raisons évidentes éviter de faire reposer un calcul sur l’ordre des éléments d’un ensemble, on peut constater que cet ordre demeure figé tout au long d’une session (sauf si l’on force explicitement des modifications dans la structure Maple interne qui représente l’ensemble considéré, voir chapitre 5, page 161) et qu’il dépend seulement de la session. Les ensembles en Maple sont donc une implémentation des ensembles mathématiques finis. On obtient le nombre d’éléments d’un ensemble avec la commande nops : > nops(F); 6 et la séquence sous-jacente à un ensemble grâce à la commande op35 : 35
On verra au chapitre 5 pourquoi le résultat de l’application des commandes nops et op à des ensembles conduit aux résultats voulus alors que ces commandes ont une portée bien plus vaste.
64
Maple : règles et fonctions essentielles >
op(F); 1, 2, 3, 4, a, b
Pour accéder directement à un élément d’un ensemble, il suffit d’indiquer sa position dans l’ensemble36 , toujours à l’aide de la commande op à laquelle on passe la position en premier argument : >
op(4,F); 4
ou, plus directement, à l’aide de l’opérateur de sélection : >
F[5]; a
En revanche, cette syntaxe ne permet pas de modifier un élément de l’ensemble37 : >
F[5]:=12;
Error, cannot reassign the entries in a set
36
Toutefois, cette façon de procéder n’est guère recommandable : certes, on a vu que l’ordre des éléments d’un ensemble demeure figé au cours d’une session, mais qu’il dépend de cette session. 37 On comprend facilement pourquoi une telle instruction est interdite, si l’on se souvient qu’un ensemble ne saurait comporter un doublon. Une explication plus approfondie se trouve au chapitre 5 qui décrit la structure servant à représenter en interne tout objet Maple.
2. Types fondamentaux
65
Il est utile de savoir désigner l’ensemble vide. Cela se fait naturellement : > Omega:={}; Ω := {} >
nops(Omega);
0 Par ailleurs, un certain nombre d’opérations usuelles sur les ensembles sont implémentées dans Maple. Ainsi, l’union, l’intersection et la différence non symétrique s’obtiennent respectivement à l’aide des opérateurs union, intersect et minus : > A:={seq(2*i,i=1..10)}; >
A := {2, 4, 6, 8, 10, 12, 14, 16, 18, 20} B:={seq(3*i,i=1..10)};
B := {3, 6, 9, 12, 15, 18, 21, 24, 27, 30} A union B; {2, 3, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20, 21, 24, 27, 30} > A intersect B; {6, 12, 18} > A minus B; {2, 4, 8, 10, 14, 16, 20} >
L’appartenance à un ensemble peut être testée avec la commande in : > 1 in {1,2,3,4}; 1 ∈ {1, 2, 3, 4} Dans cet exemple, comme dans celui de la page 42, il faut expliciter sa demande pour obtenir l’évaluation booléenne de l’expression considérée : > evalb(%); true
66
Maple : règles et fonctions essentielles
2.6.3
Listes
On obtient une liste (variable de type list) en délimitant une séquence par des crochets : >
L:=[$1..6]; L := [1, 2, 3, 4, 5, 6]
Contrairement à certains langages comme Caml, Maple n’exige pas que tous les éléments d’une liste relèvent du même type : >
F:=[1,2,3,a,2,a,b,4]; F := [1, 2, 3, a, 2, a, b, 4]
Les listes se distinguent des ensembles : d’une part, les doublons éventuels sont conservés, comme le montre l’exemple précèdent, et, d’autre part, l’ordre des éléments est contractuel, comme en témoigne l’exemple suivant : >
evalb([1,2,3]=[2,1,3]); false
On peut donc considérer les listes Maple comme une implémentation des n-uplets. Comme dans le cas des ensembles, et toujours pour les mêmes raisons (voir note 35, page 63), le nombre d’éléments d’une liste s’obtient avec la commande nops : >
>
T:=[1,3.2,5,a,1]; T := [1, 3.2, 5, a, 1] nops(T); 5
et la séquence sous-jacente à une liste avec la commande op :
2. Types fondamentaux
67
>
op(T); 1, 3.2, 5, a, 7
Comme dans le cas des listes, la commande op dotée d’un deuxième argument permet d’accéder à un élément de la liste considérée : >
op(2,T); 3.2
ce que l’opération de sélection fournit aussi directement : >
T[2]; 3.2
De manière plus générale, on peut extraire d’une liste une sous-liste (d’éléments consécutifs) de la manière suivante : >
T[2..4]; [3.2, 5, a]
Enfin, contrairement au cas des ensembles, on peut modifier directement un élément de la liste avec cette même syntaxe : >
T[2]:=55; T2 := 55
>
T; [1, 55, 5, a, 7]
68
Maple : règles et fonctions essentielles
Sur le plan de la rigueur algorithmique, cette façon de procéder pour modifier des éléments au sein d’une liste n’est pas la plus recommandable38 , mais elle est assez commode. Le lecteur averti aura compris, à travers ces exemples, que les listes en Maple sont implémentées selon un double chaînage. Elles constituent ainsi des structures de données souples et agréables à manipuler puisque, contrairement à l’implémentation des listes simplement chaînées telle qu’on la trouve en Caml ou en Prolog, on n’est pas contraint à un parcours pas à pas de la liste pour atteindre un élément. On dispose ainsi en quelque sorte d’un tableau de longueur variable. L’agrément d’une telle structure de données fait vraisemblablement rêver beaucoup d’utilisateurs confrontés aux langages de programmation usuels comme C ou Caml qui en sont dépourvus ; cependant, on ne saurait occulter que la contrepartie d’une telle structure est un coût conséquent en terme d’algorithmique. La liste vide s’obtient simplement : >
B:=[];
>
nops(B);
B := [ ] 0 Maple propose diverses commandes particulièrement utiles pour manipuler les listes. Lors de l’appel select(f,L), la commande select sélectionne dans une liste les éléments de la liste L qui satisfont à la fonction f à valeurs booléennes39 . Voici comment ne conserver que les nombres premiers dans la liste des entiers compris entre 1 et 20 : >
select(isprime,[$1..20]); [2, 3, 5, 7, 11, 13, 17, 19]
La commande remove fonctionne comme select, mais elle retire les éléments qui satisfont au critère de sélection f :
38
Attention notamment aux débordements de l’indice lorsqu’on contrôle mal la longueur de la liste parcourue. 39 En d’autres termes, f est un prédicat.
2. Types fondamentaux
69
>
remove(isprime,[$1..20]); [1, 4, 6, 8, 9, 10, 12, 14, 15, 16, 18, 20]
Au-delà de leurs utilisations premières, illustrées par les exemples précédents, les commandes select et remove présentent différentes options que le lecteur est invité à découvrir en consultant les pages d’aide en ligne correspondantes. Lors de l’appel map(f,L), la commande map permet d’appliquer40 la fonction f à tous les éléments de la liste41 L, comme le montre l’exemple abstrait suivant : >
map(f,[a,b,c,d]); [f(a), f(b), f(c), f(d)]
Dans l’exemple concret suivant, on demande à Maple d’élever au carré42 tous les éléments de la liste des entiers compris entre 1 et 10 : >
map(x->x^2,[$1..10]); [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
La commande map présente différentes syntaxes optionnelles, notamment afin de pouvoir traiter l’application de fonctions de plusieurs variables à tous les éléments d’une liste. L’appel map(f,L,b,c,d) équivaut, en fait, à l’appel map(x->f(x,b,c,d),L). Cette possibilité de map est illustrée par l’exemple suivant : appliquer la dérivation à une liste de fonctions. Le calcul d’une dérivée première peut être mené avec la commande diff ; celle-ci requiert que son premier argument soit l’expression à dériver et le deuxième, 40
Ce qui suppose que les éléments de la liste figurent bien dans l’ensemble de définition de la fonction. 41 Comme cela a déjà été le cas pour les commandes op et nops, on s’intéresse ici à l’effet de select, remove et map sur les listes mais en fait ces commandes ont une portée plus vaste et peuvent s’appliquer à une expression de n’importe quel type (voir chapitre 5). 42 On remarque au passage la possibilité de définir à la volée la fonction à appliquer selon la notation mathématique “->” habituelle (se reporter à l’aide en ligne concernant l’option arrow).
70
Maple : règles et fonctions essentielles
la variable de dérivation. Le second argument de diff, commun à tous les calculs, devient donc ici le troisième argument de map : > map(diff,[seq(x^k,k=1..5)],x); [1, 2 x, 3 x2 , 4 x3 , 5 x4 ] Enfin, le lecteur est invité à se reporter à la partie 5.4.3 pour découvrir la possibilité, extrêmement commode, d’appliquer map à une expression de n’importe quel type. Mentionnons encore la commande zip. Si f est une fonction de deux variables, et A et B des listes43 , l’appel zip(f,A,B) fabrique la liste dont les éléments sont C[i]=f(A[i],B[i]). Dans cet exemple, on additionne deux à deux les éléments des listes passées en entrée : > zip((x,y)->x+y,[1,2,3],[4,5,6]); [5, 7, 9] Terminons cette partie consacrée aux listes avec la commande member qui, parmi diverses options44 , permet de tester l’appartenance d’un objet à une liste : > member(y, {x, y, z}); true D’autres commandes permettant des manipulations encore plus fines sur les listes sont regroupées dans le package ListTools.
2.7
Chaînes de caractères
Une chaîne de caractères (type string) est constituée d’une suite de caractères délimitée par des guillemets anglo-saxons “"” (double quote). La taille maximale d’une chaîne dépend de la plateforme sous-jacente au système45 : 43
Consulter la page d’aide en ligne de zip pour en connaître les différentes options, en particulier, lorsque les deux listes ne sont pas de même longueur. Noter, par ailleurs, que zip s’applique aussi à des variables de type vecteur. 44 En consultant l’aide en ligne, on constatera que member s’applique aussi à des ensembles. 45 268435439 caractères pour un PC standard à architecture 32 bits.
2. Types fondamentaux
71
>
ch:="Un exemple."; ch := “Un exemple.”
>
ch; “Un exemple.”
La commande cat permet de concaténer des chaînes de caractères. Conformément au déroulement normal d’un appel à une commande Maple (voir la partie 3.6, page 105), tous les arguments passés à cat sont évalués avant concaténation : >
cat("Deux mots.",ch); “Deux mots.Un exemple.”
Ce deuxième exemple montre, au passage, que cat est l’une des commandes Maple qui admettent un nombre variable d’arguments : >
cat("ba","rat","in"); “baratin”
L’opérateur “||” sert, lui aussi, à réaliser la concaténation de chaînes46 . Cependant, il convient de bien comprendre comment fonctionne cet opérateur pour lequel l’opérande de gauche est évalué en son nom et l’opérande de droite, en son contenu : >
ch||"Deux mots."; chDeux mots.
Dans l’exemple précédent, la variable ch n’est pas évaluée en son contenu mais en son nom ce qui explique le résultat obtenu ; on remarquera qu’il 46
L’opérateur de concaténation était désigné par “.” dans les versions antérieures à la version 6 de Maple.
72
Maple : règles et fonctions essentielles
n’est pas de type chaîne, mais un nom de variable. En revanche, dans l’exemple qui suit, cette même variable est bien évaluée47 : >
"Deux mots."||ch; “Deux Mots.Un exemple.”
L’opérateur de concaténation “||”, dont le comportement peut dérouter au premier abord, trouve son utilité pour fabriquer, par exemple, des noms de variables : >
p||n; pn
>
n:=4:p||n; p4
Ce procédé se généralise : >
x||(a,b,4,y); xa, xb, x4 , xy
et permet d’obtenir des suites de noms de variables : >
x||(1..10); x1 , x2 , x3 , x4 , x5 , x6 , x7 , x8 , x9 , x10
et même des suites de noms doublement indexés : >
x||(1..2)||(1..4); x11 , x12 , x13 , x14 , x21 , x22 , x23 , x24
47
Parce qu’une chaîne s’évalue systématiquement en son contenu.
2. Types fondamentaux
73
Enfin, voici comment on peut forcer l’opérateur “||” de concaténation à se comporter comme la commande cat : >
""||"ba"||"rat"||"in"; “baratin”
La première chaîne passée dans cette suite de concaténations est la chaîne vide et elle est évaluée en son nom qui est son contenu, c’est-à-dire la chaîne vide. . . De nombreuses fonctions sont disponibles pour travailler sur les chaînes de caractères. Il est possible d’effectuer des recherches de sous-chaînes avec la commande searchtext et SearchText. On peut aussi extraire des souschaînes d’une chaîne avec la commande substring : >
substring( "wxyz",2..3); “xy”
ou encore plus directement : >
ch:="Un exemple."; ch := “Un exemple.”
>
ch[1];
>
ch[4..10];
“U” “exemple” >
substring(ch,4..10); “exemple”
Pour terminer, signalons que le package StringTools fournit un grand nombre de primitives efficaces pour manipuler les chaînes de caractères.
74
Maple : règles et fonctions essentielles
2.8
Tables, tableaux et matrices
Les tables, les tableaux et les matrices sont des types de données qui présentent des caractéristiques communes. Plus précisément, dans les versions de Maple antérieures à la version 6, les tableaux de type array (respectivement les matrices de type matrix) sont des cas particuliers de tables du type table (respectivement de tableaux de type array) présentant des propriétés supplémentaires. En particulier, les commandes décrites dans la partie spécifique au type table s’appliquent, en général, aussi aux objets de type table ou de type matrix. Ces types subsistent dans les versions récentes de Maple, mais nous devons d’emblée souligner un point délicat : Maple, depuis la version 6, propose une implémentation plus efficace des tableaux et des matrices, avec de nouveaux types dont le comportement est analogue à celui de leurs homologues anciens tout en présentant certaines améliorations. C’est ainsi que sont apparus les types rtable (une nouvelle structure de données interne à Maple sans rapport immédiat avec le type table), Array et Matrix. Pour des raisons de compatibilité ascendante et pour permettre l’utilisation d’anciennes bibliothèques et feuilles de travail, les développeurs de Maple ont choisi de faire coexister les anciens types et les nouveaux. Enfin, de nombreux ouvrages et articles font intervenir les anciens types. Tous ces éléments militent pour que l’on présente aussi dans ce chapitre les caractéristiques des anciens types. Le principe syntaxique permettant de différencier les anciens types des nouveaux est simple : les premiers portent des noms tout en minuscules (array, matrix) alors que les seconds commencent par une majuscule (Array, Matrix). Ce même principe permet, en général, de distinguer les commandes s’appliquant aux anciens types de celles qui s’appliquent aux nouveaux, même si les commandes homologues dans l’ancien système et dans le nouveau ne portent pas toujours le même nom. Ainsi l’ancien package qui fournit les fonctions relatives à l’algèbre linéaire s’appelle linalg et le nouveau, LinearAlgebra. Par ailleurs, si certaines commandes telles adjoint, eigenvalues, eigenvectors, trace, transpose deviennent respectivement Adjoint, Eigenvalues, Eigenvectors, Trace, Transpose, d’autres voient leur nom changer : linsolve devient LinearSolve, charpoly devient CharacteristicPolynomial, . . . La principale différence de comportement entre les anciens types et les nouveaux réside dans leur évaluation. Les variables de l’un des anciens types table, array ou matrix sont des objets agrégés qui relèvent donc de la règle d’évaluation au dernier nom (voir la partie 3.5, page 97). Aussi pour obtenir leur contenu ne peut-on se contenter de les invoquer : il convient de recourir à la commande eval. Les variables de l’un des nouveaux types Array ou Matrix s’évaluent complètement comme le feraient des objets de type simple.
2. Types fondamentaux
75
Après avoir présenté les tables, nous présenterons les types (ancien et nouveau) permettant de décrire les tableaux et les matrices.
2.8.1
Tables
Une table (variable de type table) permet d’établir des correspondances entre des indices et des valeurs (chacun pouvant être de nature quelconque). Déclarer une table n’est pas nécessaire, car elle est créée par simple affectation d’une de ses cases. >
Trad["bleu"]:="blue": Trad["rouge"]:="red": Trad["vert"]:="green":
On accède à un élément avec l’opérateur de sélection : >
Trad["rouge"]; “red”
Une table est un objet agrégé (voir les règles d’évaluation propres à Maple, partie 3.5.3, page 100). Un appel au nom d’une table de type table provoque simplement l’affichage de ce nom et il faut recourir à la commande eval pour obtenir l’affichage du contenu de la table : >
Trad;
>
Trad eval(Trad); table([“bleu” = “blue”, “vert” = “green”, “rouge” = “red”])
Maple lui-même utilise les tables pour ses propres besoins. À certaines fonctions sont associées des tables de valeurs ou tables de remember (voir 4.8, page 139). Par ailleurs les packages sont organisés comme des tables dont les entrées sont les fonctions définies en son sein.
76
Maple : règles et fonctions essentielles
Signalons encore deux commandes qui s’appliquent aux tables. La commande entries permet de récupérer sous forme de séquence tous les éléments d’une table. Le résultat se présente sous la forme d’une séquence de listes. L’ordre de cette séquence est arbitraire48 (mais l’ordre obtenu avec entries sera en correspondance avec celui fourni par indices pour une session donnée) : >
entries(Trad); [“blue”], [“red”], [“green”]
L’appel à entries avec l’option nolist conduit à une réponse sans liste, ce qui présente un intérêt essentiellement dans le cas des tables à une dimension : >
entries(Trad,’nolist’); “blue”, “red”, “green”
La commande indices est le pendant de entries et fournit sous forme d’une séquence de listes les indices de la table à laquelle elle est appliquée : >
>
indices(Trad); [“bleu”], [“rouge”], [“vert”] indices(Trad,’nolist’); “bleu”, “rouge”, “vert”
Le type table est conservé tel quel dans les dernières versions de Maple. Un nouveau type rtable a été créé depuis la version 6, mais son rôle est de servir de constructeur aux nouveaux types Array et Matrix (se reporter à l’aide en ligne). 48
Comme l’est l’ordre dans lequel est affiché le contenu d’un tableau. Pour s’en convaincre, il suffit de lancer à différentes reprises la création du tableau Trad, après avoir fait restart ou changé de session, et de considérer les différentes réponses données par Maple lors d’un appel eval(Trad).
2. Types fondamentaux
2.8.2
77
Tableaux
Un tableau de type array est une particularisation du type table dont chaque dimension est indexée par un entier naturel et dont les éléments peuvent être de n’importe quel type. Un tableau a des dimensions finies, fixées une fois pour toutes au moment de sa création (ce qui le différencie des listes). Un tableau peut être défini à l’aide de la commande array en précisant simplement ses dimensions : >
T:=array(1..10); T := array(1..10, [ ])
Dans l’exemple précédent, on crée un tableau de 10 éléments auxquels aucune valeur n’a été affectée. On peut accéder à n’importe quel élément en ayant recours à l’opérateur de sélection selon la syntaxe suivante : >
T[5]; T5
L’élément demandé n’étant pas initialisé, la réponse obtenue est le nom du tableau indexé par l’indice de l’élément considéré. On utilise aussi l’opérateur de sélection pour placer une valeur dans un élément d’un tableau : > >
for i from 1 to 5 do T[i]:=ithprime(i) end do: T[1];T[8]; 2 T8
Un tableau de type array étant un objet agrégé (voir la partie 3.5.1, page 97), il faut recourir à eval pour obtenir son contenu :
78
Maple : règles et fonctions essentielles >
T; T
>
eval(T); [2, 3, 5, 7, 11, ?6 , ?7 , ?8 , ?9 , ?10 ]
On peut aussi visualiser le contenu d’un tableau avec la commande print49 : >
print(T); [2, 3, 5, 7, 11, T6 , T7 , T8 , T9 , T10 ]
Lorsque l’on applique eval, on constate que l’affichage n’est pas exactement le même. On peut fournir l’ensemble des valeurs des éléments d’un tableau au moment de sa définition en passant les valeurs des éléments sous forme de liste (ou sous forme de liste d’équations ou encore sous forme de liste de listes, voir l’aide en ligne) : >
T:=array(1..4,[2,4,6,8]); T := [2, 4, 6, 8]
On vérifie que Maple a bien enregistré les valeurs voulues : >
print(T); [2, 4, 6, 8]
Bien évidemment, Maple permet de travailler avec des tableaux multidimensionnels. Voici un exemple de tableau à deux dimensions : 49
La commande print permet l’affichage non formaté du contenu de n’importe quel type de variable. La commande print produit un effet de bord mais aucun résultat. Elle n’a donc absolument pas le même effet que la commande eval.
2. Types fondamentaux
79
>
T:=array(1..3,1..2); T := array(1..3, 1..2, [ ])
À ce stade, le tableau est vide. À titre d’exemple, on remplit le tableau en mettant dans chaque élément la plus grande valeur de ses coordonnées : >
>
for i from 1 to 3 do for j from 1 to 2 do T[i,j]:=max(i,j) end do end do: eval(T); ⎡
1 ⎣ 2 3
⎤ 2 2 ⎦ 3
On peut utiliser des options pour définir la forme du tableau. Dans l’exemple qui suit, on décide d’avoir un tableau symétrique : > >
T:=array(symmetric,1..2,1..2): T[1,1]:=1:T[1,2]:=3:eval(T);
1 3 3 ?2, 2
Le lecteur est invité à se reporter à l’aide en ligne pour connaître les diverses options disponibles. Il convient de prêter attention au point suivant car sa méconnaissance est source d’erreurs. Pour copier un tableau dans un autre, on ne peut se contenter de procéder à une affectation comme dans le cas d’une variable de type simple50 . Il faut impérativement recourir à la commande copy. Considérons le tableau suivant : 50
Le lecteur averti sait que le problème est analogue avec tous les langages de programmation qui considèrent que les tableaux sont des objets mutables.
80
Maple : règles et fonctions essentielles >
T:=array(1..2,1..2,[[1,-1],[0,3]]):eval(T);
1 −1 0 3
et la suite d’instructions suivantes pour constater l’effet des différentes façons (les mauvaises et la bonne) de procéder : >
A:=T: B:=eval(T): C:=copy(T): T[1,1]:=0: eval(T); eval(A); eval(B); eval(C);
0 −1 0 3
0 −1 0 3
0 −1 0 3
1 −1 0 3
On constate que A (obtenue par une affectation directe), B (obtenue par affectation du résultat de eval(T)) et T sont synonymes si bien que toute modification à destination de l’une des trois dénominations affecte les trois de la même manière (les trois noms pointent sur le même emplacement en mémoire). Seule la variable C a une vie indépendante de celle de T, une fois créée. Au moment de sa création, elle est initialisée à la valeur de T. S’il présente beaucoup plus d’options que son homologue array, le nouveau type Array n’en diffère que sur quelques points. Lorsqu’on définit une variable de type Array sans initialiser ses éléments, la valeur 0 leur est automatiquement attribuée : >
T:=Array(1..10); T := [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
C’est toujours la même syntaxe, fondée sur l’opérateur de sélection, qui permet d’accéder au contenu d’un élément :
2. Types fondamentaux
81
>
T[5]; 0
ou d’en modifier le contenu : >
for i from 1 to 5 do T[i]:=ithprime(i) end do:
On constate qu’un tableau de type Array s’évalue complètement lorsqu’on l’instancie, et qu’il est inutile de recourir à eval pour obtenir son contenu : >
T; [2, 3, 5, 7, 11, 0, 0, 0, 0, 0]
Il faut néanmoins savoir que la visualisation classique d’un tel tableau n’est possible que s’il comporte moins de 10 éléments. Un tableau de plus grande dimension est affiché de manière compacte : >
S:=Array(1..20);
S := [ 1..20 1 − D Array Data Type : anything Storage : rectangular Order : Fortran_order ] On vérifie bien qu’il ne s’agit pas d’un problème d’évaluation : >
eval(S);
[ 1..20 1 − D Array Data Type : anything Storage : rectangular Order : Fortran_order ]
82
Maple : règles et fonctions essentielles
Pour obtenir l’affichage en extension de tableaux de plus grande dimension, il faut modifier le contenu de la variable d’environnement rtablesize, par exemple en lui attribuant la valeur infinie : >
>
interface(rtablesize=infinity); 10 S; [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
On remarque, au passage, que l’appel interface(rtablesize=infinity) donne la réponse 10 qui était la précédente valeur de rtablesize et la valeur par défaut de cette variable : Par défaut51 , les tableaux de type Array sont mutables : >
A:=T;
>
A[1]:=1;
A := [2, 3, 5, 7, 11, 0, 0, 0, 0, 0] A1 := 1 >
A;
>
T;
[1, 3, 5, 7, 11, 0, 0, 0, 0, 0] [1, 3, 5, 7, 11, 0, 0, 0, 0, 0] La modification portée sur A apparaît aussi sur T. Comme dans l’exemple précédent qui concernait les tableaux de type array, ce sont deux étiquettes pour le même objet en mémoire et il faut, là encore, recourir à la commande copy.
2.8.3
Matrices
Une matrice de type matrix est un tableau de type array particulier dont on ne définit plus que les dimensions (entières). Le recours à des matrices de ce type donne accès aux commandes du package linalg qui 51
Une option permet de définir des tableaux non mutables, se reporter à la page d’aide en ligne décrivant le type Array.
2. Types fondamentaux
83
offre les opérations usuelles sur les matrices ainsi que les procédés de calcul d’algèbre linéaire : >
with(linalg):
Warning, the protected names norm and trace have been redefined and unprotected
Comme les tableaux, les matrices peuvent être définies de différentes manières (pour la description exhaustive des possibilités en la matière, se reporter à la page d’aide en ligne de matrix). Dans l’exemple suivant, nous définissons une matrice carrée d’ordre 3 en faisant figurer ses coefficients sous forme d’une liste de listes (les dimensions de la matrice se déduisant implicitement de ces listes) : >
A:=matrix([[0,1,1],[0,0,1],[0,0,0]]); ⎡ ⎤ 0 1 1 A := ⎣ 0 0 1 ⎦ 0 0 0
Pour accéder à un élément de la matrice, on utilise, comme d’habitude, l’opérateur de sélection : >
A[1,2]; 1
À titre d’illustration, on montre ici le calcul de la trace de la matrice A à l’aide de la commande trace du package linalg : >
trace(A); 0
Cependant, ce chapitre étant consacré aux types fondamentaux de Maple ainsi qu’à leurs propriétés et caractéristiques, nous ne détaillerons pas ici
84
Maple : règles et fonctions essentielles
l’ensemble des commandes que Maple met à disposition pour traiter des problèmes relevant de l’algèbre linéaire. Dans cette partie, nous évoquons seulement les propriétés structurelles des types matrix et Matrix. Une matrice de type matrix est un objet agrégé (voir la partie 3.5.1, page 97). Un calcul matriciel conduit donc à une évaluation du résultat au dernier nom : >
A^2; A2
et il faut recourir à la commande evalm, qui est une spécialisation de la commande eval au type matrix, pour obtenir son évaluation : >
evalm(%);
⎡
0 ⎣ 0 0
0 0 0
⎤ 1 0 ⎦ 0
Le produit matriciel pour le type matrix est désigné par “&*” et non simplement par “*”, afin de souligner le caractère non commutatif de ce produit52 : >
>
B:=matrix(3,3,(i,j)->i+j); ⎡ 2 3 B := ⎣ 3 4 4 5 evalm(A&*B);evalm(B&*A); ⎡ 7 9 11 ⎣ 4 5 6 0 0 0
⎤ 4 5 ⎦ 6 ⎤ ⎦
52 Cette notation n’a pas été conservée pour le produit des matrices de type Matrix. Comme on le verra un peu plus loin, la nouvelle notation pour le produit est “.”. Indiquons aussi que “&” a par ailleurs pour objet de préfixer certains noms d’opérateurs pour signaler qu’ils sont inertes. . .
2. Types fondamentaux ⎡
0 ⎣ 0 0
85
⎤ 5 7 ⎦ 9
2 3 4
Pour les mêmes raisons que précédemment, on utilise la commande evalm pour obtenir l’évaluation complète du résultat. Les matrices de type matrix sont des variables mutables et, à ce titre, il faut prendre des précautions pour copier une matrice, toujours en ayant recours à la commande copy. Dans l’exemple suivant, on crée à l’aide de la commande randmatrix une matrice aléatoire A que l’on duplique dans une nouvelle matrice B : >
A:=randmatrix(3,3); ⎡
−85 A := ⎣ −35 79
>
B:=copy(A);
⎡
−85 B := ⎣ −35 79
−55 97 56
⎤ −37 50 ⎦ 49
−55 97 56
⎤ −37 50 ⎦ 49
Un autre point délicat se présente lorsque l’on souhaite comparer deux matrices dans un test d’égalité. L’instruction qui suit, indépendamment du fait que l’on travaille sur des matrices, ne permet pas de conclure puisqu’on ne fait qu’écrire un objet Maple de type équation : >
A=B; A=B
Les deux façons de procéder suivantes n’étant pas concluantes, elles sont à proscrire : >
evalb(A=B); false
86
Maple : règles et fonctions essentielles >
evalb(evalm(A)=evalm(B)); false
Pour comparer deux matrices, il faut utiliser la commande equal du package linalg, conçue à cet effet : >
equal(A,B); true
Les versions récentes ont vu l’avènement du nouveau type Matrix en remplacement du type matrix dont il reprend l’essentiel des propriétés et capacités en y ajoutant un grand nombre d’options et d’améliorations. Les procédés de l’algèbre linéaire et les opérations usuelles sur les matrices associées au type Matrix sont accessibles à travers le package LinearAlgebra, le pendant de l’ancien package linalg. En premier lieu, il faut mentionner la différence fondamentale avec le type matrix : une variable de type Matrix n’est pas considérée comme un objet agrégé et s’évalue donc complètement (pas de blocage de l’évaluation au dernier nom). >
>
C:=Matrix(2,2,(i,j)->1/(i+j)); ⎡ 1 1 ⎤ ⎢ 2 3 ⎥ C := ⎣ ⎦ 1 1 3 4 C^2; ⎡ 13 1 ⎤ ⎢ 36 4 ⎥ ⎦ ⎣ 25 1 144 4
Une autre différence fondamentale réside dans l’écriture du produit matriciel : le symbole “&*”, utilisé pour le type matrix, est désormais remplacé par “.” :
2. Types fondamentaux
87
>
>
B:=Matrix([[0,1],[0,0]]); 0 B := 0 B.C; ⎡ 1 1 ⎣ 3 4 0 0
1 0
⎤ ⎦
Les matrices de type Matrix sont par défaut53 des objets mutables ; comme pour les types matrix, array et Array, il faut donc utiliser la commande copy pour les copier. Par ailleurs, il convient toujours de prendre des précautions pour comparer deux matrices de type Matrix dans un test d’égalité et de recourir à la commande Equal qui figure dans le package LinearAlgebra. Pour terminer cette partie consacrée aux matrices, signalons que la commande map peut aussi s’appliquer à une matrice en distribuant l’effet de la fonction passée en premier argument à tous les éléments de la matrice passée en deuxième argument : >
>
A:=Matrix([[1,2],[2,4]]);
1 2 A := 3 4 map(exp,A);
e e2 e3 e4
53
Le type Matrix étant une particularisation du type Array, il hérite des options disponibles pour les Array, notamment de celle permettant de déclarer une matrice non mutable.
88
Maple : règles et fonctions essentielles
2.9
Quelques autres types utiles
Présentons encore quelques types qui interviennent dans de nombreuses situations.
2.9.1
Le type équation
Nous avons déjà mis en garde le lecteur contre la faute de frappe qui consisterait à saisir “=” à la place de “:=” (voir la partie 1.8.2, page 30). La raison en est que Maple reconnaît le type equation (équation) comme on le constate sur l’exemple suivant : >
eq := x + y = 0;
>
eq := x + y = 0 type(eq,equation); true
Les commandes lhs (pour left hand side) et rhs (pour right hand side) permettent d’obtenir respectivement le membre droit et le membre gauche de l’équation qui leur est passée en argument : >
rhs(eq);lhs(eq); 0 x+y
Le type équation intervient lorsqu’on veut résoudre des équations ou des systèmes d’équations, par exemple avec les commandes solve ou fsolve54 , ou encore des équations ou des systèmes différentiels avec la commande dsolve. Ainsi, pour résoudre le système linéaire de deux équations à deux inconnues x et y 2x + y = 2 3x − y = 1 on peut commencer par définir chacune des équations du système : 54
solve entreprend une résolution exacte si elle est possible alors que fsolve lance une résolution numérique approchée.
2. Types fondamentaux
89
>
>
eq1 := 2*x + y = 2; eq1 := 2 x + y = 2 eq2 := 3*x - y = -1; eq2 := 3 x − y = −1
ce qui simplifie ensuite l’appel à solve qui va résoudre le système : >
solve({eq1,eq2},{x,y}); 8 1 {x = , y = } 5 5
2.9.2
Le type intervalle
Une expression de type intervalle (range ou même “..”) admet deux opérandes qui sont les bornes de l’intervalle représenté. C’est ainsi que 1..3 représente l’intervalle [1, 3] (à ne pas confondre avec la séquence 1,2,3). De nombreuses commandes Maple prennent une variable de type intervalle en argument. C’est le cas de la commande seq et de l’opérateur “$” (voir la partie 2.6.1, page 59), de la commande de concaténation cat (voir la partie 2.7, page 70), de la commande op (voir la partie 5.3, page 164) et de l’opérateur de sélection. C’est aussi le cas des commandes add55 , sum, mul, product, int56 et plot57 , d’utilisation courante en analyse, pour lesquelles on utilise une expression de type intervalle pour spécifier les bornes de l’ensemble (de sommation, d’intégration, de définition) parcouru par la variable qui intervient dans l’opérande58 : 55
add (respectivement mul) calcule la valeur numérique de la somme (respectivement du produit) d’une suite de valeurs numériques. Les bornes de la somme (respectivement du produit) doivent donc s’évaluer en des valeurs numériques, éventuellement infinies. Pour déterminer l’expression symbolique (la forme close) d’une somme (respectivement d’un produit) de termes il faut utiliser sum (respectivement product). 56 int sert à calculer des intégrales ou des primitives. 57 La commande plot est dotée de nombreuses options et permet la réalisation de représentations graphiques en dimension 2, notamment la représentation graphique de fonctions d’une variable réelle, comme dans l’exemple qui suit. 58 Dans certains des exemples qui suivent, la réponse de Maple présente une écriture proche de l’écriture mathématique usuelle pour le membre de gauche. Celui-ci résulte d’un effet du pretty-printer à travers l’utilisation des commandes Sum et Int, homologues inertes des commandes sum et int (voir la partie 1.5.4, page 21).
90
Maple : règles et fonctions essentielles >
Sum(i,i=1..3)=add(i,i=1..3); 3
i=6
i=1 >
Sum(i^2,i=1..n)=factor(sum(i^2,i=1..n)); n
n (n + 1) (2 n + 1) i2 = 6 i=1
>
Int(1/(1+x^2),x=0..infinity)=int(1/(1+x^2),x=0..infinity); ∞ π 1 dx = 2 2 1+x 0 plot(sin(x),x=0..4*Pi);
>
1
0.5
0
2
4
6 x
8
10
12
–0.5
–1
2.9.3
Le type procédure
Le type procédure (type procedure) est celui qui caractérise les fonctions écrites par l’utilisateur ou les commandes Maple. Une expression de type procédure présente les mêmes caractéristiques et le même comportement que n’importe quel autre type, comme on le vérifiera au chapitre 5. Maple distingue le type procédure du type fonction (function) : le second renvoie à un appel de fonction ou de commande instancié comme f(x).
Chapitre 3
Évaluation 3.1
Préambule
L’évaluation de variables se produit en maintes circonstances : affectation, appel de fonction ou de commande Maple, demande explicite de l’utilisateur ou occurrence lors de l’exécution d’un programme. Dans ce chapitre, nous expliciterons toutes les règles d’évaluation de Maple et les illustrerons par des exemples. Nous commencerons par cerner les liens entre affectation et évaluation (partie 3.2). Maple fournit aussi un moyen de différer l’évaluation d’une variable. Nous présenterons ce mécanisme et ses implications dans la partie 3.3). Nous aborderons ensuite le contrôle fin de l’évaluation par Maple et présenterons la notion de niveau d’évaluation (partie 3.4). Le déroulement de l’évaluation d’une variable dépend de son type et des circonstances de l’évaluation ; les différentes règles sont décrites et expliquées dans la partie 3.5 qui constitue l’un des éléments essentiels de ce chapitre. L’appel à une fonction déclenche l’évaluation des paramètres d’appel selon une procédure décrite dans la partie 3.6. Enfin nous présenterons différentes commandes permettant d’évaluer spécifiquement certains types de variables.
3.2
Évaluation et affectation
L’importance de l’affectation et l’interdépendance entre celle-ci et l’évaluation apparaissent dès les premières pages de cet ouvrage : on a vu que pour procéder à l’affectation d’une valeur à une variable, on utilise le symbole “:=” (qu’il convient de distinguer de l’égalité, “=”, (voir la partie 1.8.2, page 30).
92
Maple : règles et fonctions essentielles
Les exemples introductifs présentés au chapitre 1 montrent aussi que la simple invocation d’une variable provoque1 l’affichage de son contenu si cette variable en a un : >
a:=1; a := 1
>
a; 1
Lorsque la variable considérée n’a pas de contenu, son invocation conduit à la mention de son nom : >
toto; toto
Cet exemple illustre la première règle liée à l’évaluation dans Maple. Règle n˚ 1 : une variable non affectée, c’est-à-dire dont le contenu est « vide », s’évalue en son nom ; réciproquement, on reconnaît une variable non affectée au fait qu’elle s’évalue en son nom.
3.3
Évaluation différée
Maple dispose de différents mécanismes lui permettant de contrôler l’évaluation des variables. L’un d’eux consiste à bloquer l’évaluation d’une variable à la demande. Une expression figurant entre apostrophes “’” (single quote, à ne pas confondre avec l’accent grave “‘”, back quote) voit son évaluation différée d’un temps2 : >
a:=4; a := 4
>
’a’; a
1
Ce n’est pas systématiquement le cas, voir la partie 3.5, page 97. Rappelons que l’opérateur % (opérateur dito) est un conteneur pour le précédent résultat calculé par l’interpréteur (voir la partie 1.5.5, page 22).
2
3. Évaluation >
93
x:=1;y:=2; x := 1 y := 2
>
’x+y’;
>
%;
x+y 3 Une expression peut être simplifiée bien qu’encadrée par des apostrophes ; les étapes d’évaluation et de simplification sont donc distinctes : >
’2*a+4*x-3*(a+x)’; −a + x
Chaque fois que Maple évalue une expression écrite entre apostrophes, il enlève une paire d’apostrophes homologues : >
a:=5; a := 5
>
”a”;
>
%;
>
%;
’a’ a 5 Ce mécanisme fournit un moyen de désaffecter une variable. En effet, une variable étant considérée comme non affectée dès qu’elle s’évalue en son nom, il suffit, pour la désaffecter, de lui assigner son propre nom, encadré par une paire d’apostrophes : >
a:=5;
>
a:=’a’;
a := 5
94
Maple : règles et fonctions essentielles a := a >
a; a
Ce procédé peut aussi être mis en œuvre avec des variables indexées : >
a[1]:=12.5; a1 := 12.5
>
a[1]:=’a[1]’; a1 := a1
>
a[1]; a1
On trouvera dans la partie 3.6 (page 107) une autre situation typique dans laquelle il convient de différer l’évaluation d’une variable.
3.4
Niveaux d’évaluation
Les exemples que l’on vient d’étudier et faisant intervenir l’évaluation différée suggèrent que Maple est capable de contrôler finement l’évaluation. Si on le souhaite, Maple donne accès au niveau d’évaluation, comme le montre l’exemple3 qui suit. On introduit trois variables u, v et w avec les dépendances suivantes : >
restart:u:=v;v:=w; u := v v := w
Prenons connaissance du contenu de ces variables : > 3
u;v;w;
Dans certains exemples présentés dans ce chapitre, nous faisons apparaître le restart préalable à toute manipulation car l’ordre d’exécution des instructions et le fait que les variables soient non affectées au moment de débuter l’exemple jouent un rôle essentiel.
3. Évaluation
95
w w w À présent, affectons une valeur à w : >
w:=1; w := 1
et demandons à connaître le contenu actualisé des trois variables : >
u;v;w; 1 1 1
La simplicité de la démarche et le caractère naturel des résultats obtenus masquent un processus d’évaluation complexe. À l’aide de la commande eval, on va pouvoir visualiser plus précisément le processus interne mis en œuvre par Maple. Dans la forme utilisée ici4 , eval prend deux arguments : le premier désigne la variable sur laquelle on travaille, et le deuxième son niveau d’évaluation (voir la partie 3.8.1, page 112) : >
eval(u,1);eval(u,2);eval(u,3); v w 1
On visualise ainsi la chaîne u •—> v •—> w 1 qui a été créée dans la mémoire de travail de Maple5 . La variable d’étiquette u a un contenu défini 4 Comme on le verra plus loin, eval est une commande qui accepte un nombre variable d’arguments ; son comportement ainsi que les buts poursuivis dépendent du nombre et de la nature des arguments passés. 5 Cette chaîne présente trois niveaux si bien qu’un eval(u,n) avec n supérieur à 3 conduit au même résultat que l’appel eval(u,3).
96
Maple : règles et fonctions essentielles
à partir de celui de v. La variable d’étiquette v a un contenu défini à partir de celui de w. Le contenu de la variable d’étiquette w est 1. D’ores et déjà, on peut remarquer dans l’exemple précédent que, pour donner la valeur (ou plus correctement : le résultat de l’évaluation) de u, le système a parcouru toute la chaîne de manière récursive. Nous attirons l’attention du lecteur sur les implications de ce processus : l’ordre des affectations effectuées au début de l’exemple est important. Considérons la suite d’instructions suivante, qui diffère à peine de la précédente : cette fois, la variable d’étiquette u est définie avant la variable d’étiquette v : >
restart:v:=w;u:=v; v := w u := w
Demandons à présent à connaître les contenus de u, v et w : >
u;v;w; w w w
Une lecture superficielle pourrait laisser croire que rien n’a changé. Essayons d’être plus précis : >
eval(u,1);eval(u,2);eval(u,3); w w w
Les trois niveaux d’évaluation affichent la même réponse : w (contrairement à l’exemple précédent). En fait, il n’y a qu’un seul niveau de profondeur dans l’évaluation de u. Cette fois, au lieu de l’unique chaîne reliant les trois variables, deux chaînes nouvelles ont été créées en mémoire : d’une et, d’autre part, u •—> w . En effet, part, sans surprise, v •—> w
3. Évaluation
97
au moment d’affecter le contenu de v dans u, le système s’est aperçu que v était déterminé par une chaîne qu’il a parcourue complètement, conduisant au résultat w. Même si elles sont toutes les deux définies à partir de w, les variables u et v ne dépendent plus directement l’une de l’autre : >
w:=1; w := 1
>
u;v;w;
>
1 1 1 eval(u,1);eval(u,2);eval(u,3); w 1 1
Retenons de ces exemples l’importance de l’ordre d’affectation. Les variables en question subissent une évaluation complète (en d’autres termes, un parcours récursif complet des niveaux d’évaluation). Nous allons maintenant déterminer dans quels cas s’applique cette règle.
3.5
Règles d’évaluation
Avant d’énoncer les trois règles fondamentales d’évaluation, définissons certains termes.
3.5.1
Vocabulaire
L’évaluation d’une variable dépend du contexte de l’évaluation et du type de la variable considérée. • Toplevel et exécution d’un programme : nous devrons distinguer une évaluation qui se produit au cours de l’exécution d’un programme d’une évaluation déclenchée par l’utilisateur lors d’une interaction directe avec Maple (l’évaluation est alors réputée avoir lieu au toplevel , littéralement au « niveau supérieur »). • Types agrégés et types simples : nous aurons aussi besoin de différencier deux catégories de types de variables. Reprenant la terminologie des développeurs du logiciel, nous regroupons sous le terme de type
98
Maple : règles et fonctions essentielles
agrégé le type procédure (type procedure6 , qui correspond aux fonctions programmées par l’utilisateur ou aux commandes prédéfinies Maple), le type table (type table), l’ancien type tableau (type array) et l’ancien type matrice7 (type matrix). Caractériser simplement ces types est devenu difficile ; en effet, les nouveaux tableaux (type Array) et matrices (type Matrix) apparus dans les récentes versions de Maple sont structurés mais pas agrégés. Avant l’introduction des types Matrix et Array le type agrégé rassemblait les types de variables qui aggloméraient des données ou des informations selon une structure élaborée (en quelque sorte, presque des types structurés). Dans la suite de cette partie, un type non agrégé sera qualifié de simple.
3.5.2
Évaluation des variables de type simple
Règle n˚ 2 : une variable de type simple évaluée au toplevel subit une évaluation complète (full evaluation), c’est-à-dire que les différents niveaux éventuels sont parcourus jusqu’au dernier. Cette règle est naturelle ; elle correspond au comportement attendu d’un environnement informatique dans les situations « normales ». Elle explique certains des résultats intermédiaires de l’exemple développé dans la partie précédente. Cette règle est également mise en œuvre dans l’exemple suivant : >
restart:x:=y:y:=5:x;y; 5 5 y
Au moment de la définition de x, la variable y n’a pas de contenu. Elle s’évalue donc en son nom. On a créé la chaîne x •—> y 5 , comme le confirme la commande eval : 6
Dans la terminologie de Maple les types procedure et fonction sont différenciés. Le premier se réfère aux fonctions, au sens informatique, ou aux commandes Maple prédéfinies (qui peuvent d’ailleurs être elle-mêmes des fonctions écrites en code Maple quand ce ne sont pas des programmes C compilés) ; le second renvoie à un appel de fonction ou de commande instancié comme f(x). Dans certains langages de programmation, comme Pascal, ou en informatique théorique, on distingue les procédures des fonctions : les premières sont des cas particuliers de fonctions qui ne produisent pas de résultat en sortie. Ce n’est donc pas dans cette acception que le terme « procédure » est utilisé dans la terminologie de Maple. 7 Pour davantage de précisions à propos de ces anciens types matrix et array, voir la partie 2.8, page 74.
3. Évaluation
99
>
eval(x,1);eval(x,2); y 5
On vérifie alors que si le contenu de y change, celui de x est affecté de la même manière ; la chaîne évoquée précédemment subsiste : >
y:=1;x;y; y := 1 1 1
En revanche, si l’on modifie l’ordre de définition des variables x et y de la manière suivante : >
restart:y:=5:x:=y:x;y;eval(x,1); 5 5 5
on crée deux cases mémoires indépendantes. Dans l’ordre, y 5 est créée, puis on définit x à partir de y. x est ensuite évalué en son contenu qui, cette fois, n’est pas son nom ; en conséquence, une deuxième case, x 5 , indépendante de la première, est créée. Les instructions suivantes montrent que x et y ont des destins indépendants : >
y:=1:x;y; 5 1
100
3.5.3
Maple : règles et fonctions essentielles
Évaluation des variables de type agrégé
Règle n˚ 3 : une variable de type agrégé subit une « évaluation au dernier nom » (last name evaluation). Pour une telle variable, la chaîne des niveaux est parcourue et le résultat affiché est le dernier nom rencontré dans ce parcours. Explicitons la logique sous-jacente à cette règle : le parcours des niveaux s’interrompt juste avant d’atteindre le niveau dont l’affichage serait complexe ou fastidieux, traduisant la volonté des développeurs de produire un résultat « simple ». Voici un exemple qui illustre cette règle. Il utilise l’ancien type (agrégé) matrix8 : >
>
A:=linalg[matrix](2,2,[1,1,0,1]);
1 1 A := 0 1 B:=A+A; B := 2 A
Maple arrête son parcours au dernier nom rencontré, en l’occurrence 2A. À l’aide de la commande eval à deux arguments, on peut néanmoins obtenir davantage d’informations : >
eval(B,1);eval(B,2); 2
2A
1 1 0 1
Comme on pouvait s’y attendre, parcourir deux niveaux suffit pour obtenir l’évaluation complète de B. Dans ce cas, l’utilisation de la commande evalm, conçue dans le but de provoquer l’évaluation complète d’une variable de type matrix, s’avère la meilleure façon de procéder :
8
Toutes les commandes relatives aux manipulations sur les matrices de type matrix sont regroupées dans le package linalg. On reconnaît ici l’appel par son nom long à la commande matrix (voir la partie 1.7, page 26).
3. Évaluation
101
> evalm(B);
2 2 0 2 Dans ce cas particulier9 , le recours à la commande eval ne conduit pas au résultat espéré ; l’évaluation reste encore bloquée au dernier nom : > eval(B); 2A
3.5.4
Évaluation d’une variable locale ou d’un paramètre
Règle n˚ 4 : une variable locale10 évaluée au sein de la fonction qui la définit est systématiquement évaluée au premier niveau, quel que soit son type. La même règle vaut pour un paramètre évalué par la fonction à laquelle il est rattaché. Cette règle évite à l’interpréteur de rentrer dans une récursion infinie au moment d’exécuter une instruction comme k:=k+1 au sein d’un programme. Précisons qu’une variable globale est évaluée selon les règles en vigueur au toplevel, en fonction de son type (c’est-à-dire selon les règles n˚ 2 ou n˚ 3). L’exemple suivant, qui concerne le cas d’une variable locale, illustre cette règle : > essai1:=proc(n) local u, v; u:=v; v:=n; u; end proc; essai1 := proc(n) local u, v; u := v ; v := n ; u end proc 9
Plus généralement, eval n’est pas le remède universel à tous les maux de l’évaluation. . . Se reporter au chapitre 4 pour une définition des termes « variable locale », « variable globale », « paramètre » et « argument » qui concernent tous l’écriture ou l’emploi de fonctions définies par l’utilisateur.
10
102
Maple : règles et fonctions essentielles
Deux variables locales, u et v, sont déclarées dans la fonction essai1. La réponse fournie par la fonction est la valeur de u dont on escompte qu’elle soit égale à la valeur du paramètre n récupérée au moment de l’appel. Voici le résultat obtenu en appliquant la fonction essai1 à une valeur particulière : >
essai1(5); v
En fait, la variable u est définie par la chaîne u •—> v n qui, au premier niveau d’évaluation, conduit au résultat v. Dans une telle situation, il faut utiliser la commande eval à un argument qui force l’évaluation de son argument, en l’occurrence ici u. Cette version modifiée de la fonction précédente atteint désormais le but recherché : >
>
essai2:=proc(n) local u, v; u:=v; v:=n; eval(u); end proc; essai2 := proc(n) local u, v; u := v ; v := n ; eval(u) end proc essai2(5); 5
Comme indiqué au début de cette partie, la règle n˚ 4 s’applique aussi aux paramètres d’une fonction : sans autre indication de la part du programmeur, ils sont évalués au premier niveau. Trouver un exemple simple illustrant l’application de la règle dans le cas d’un paramètre est plus difficile. En voici un que nous suggérons au lecteur d’étudier en deuxième lecture, sa compréhension étant un peu plus ardue que celle des exemples précédents. La fonction essai3, qui prend un paramètre u, a pour but de produire en sortie la valeur obtenue par évaluation de u :
3. Évaluation
103
> essai3:=proc(u) global t; t:=1; u; end proc; essai3 := proc(u) global t; t := 1 ; u end proc t est une variable globale ; on définit deux autres variables globales x et y : >
x:=y;y:=t; x := y y := t
Toujours à l’aide de la commande eval à deux arguments, on visualise la : chaîne x •—> y •—> t >
eval(x,1);eval(x,2);eval(x,3); y t t
À présent, appliquons la fonction essai3 à x : >
essai3(x); t
Le résultat obtenu n’est autre que celui produit par l’évaluation de u au premier niveau. En effet, au moment de l’appel essai3(x), l’argument, évalué complètement (voir la partie 3.6, page 105, à propos de l’évaluation de la séquence des arguments d’appel), conduit à t, suite à l’évaluation complète de x. La variable non affectée t est donc substituée au paramètre u ; plus précisément, c’est l’adresse11 de cette variable qui est placée 11
Le lecteur averti aura reconnu ce que l’on appelle un « passage par variable » ou « passage par adresse » dans certains langages de programmation.
104
Maple : règles et fonctions essentielles
avant l’exécution de la foncdans u, conduisant à la chaîne u •—> t tion. L’instruction finale u ; provoque l’évaluation de u au premier niveau, comme le montre le résultat obtenu (en l’occurrence t), alors que ce résultat aurait été 1 si u avait subi une évaluation complète. essai4 est une version modifiée de la fonction essai3 qui force l’évaluation complète du paramètre u à l’aide de la commande eval : >
>
>
>
>
essai4:=proc(u) global t; t:=1; eval(u); end proc; essai4 := proc(u) global t; t := 1 ; eval(u) end proc x:=y;y:=t; x := y y := t eval(x,1);eval(x,2);eval(x,3); y t t essai4(x); 1 eval(x,1);eval(x,2);eval(x,3); y t 1
Cette fois, u a été évalué complètement.
3.5.5
En résumé
Les règles d’évaluation se retiennent aisément si on en a compris le principe et les raisons d’être. Ainsi, la règle n˚ 2 (évaluation complète des variables de types simples au toplevel ) est en quelque sorte la règle générale ; elle souffre des exceptions chaque fois que son application soulève des difficultés. La première exception fait l’objet de la règle n˚ 3 (évaluation au dernier nom) et concerne l’évaluation au toplevel de variables de type agrégé, en d’autres termes d’objets compliqués pour lesquels le système ne procède qu’à une évaluation précautionneuse : l’utilisateur doit
3. Évaluation
105
lui-même spécifier qu’il veut mener à son terme l’évaluation et se confronter à un résultat fastidieux ou complexe. La seconde exception correspond à la règle n˚ 4 (évaluation au premier niveau des paramètres et variables locales de fonctions) qui a pour but d’éviter à l’interpréteur d’entrer dans une récursion infinie lors de l’exécution d’instructions comme k:=k+1.
3.6
Évaluation et appel de fonction
Les fonctions étant susceptibles d’accepter un nombre variable d’arguments (voir la partie 4.7, page 135), la méconnaissance du processus fondamental présenté dans cette partie peut conduire à des résultats inattendus12 . Le mécanisme mis en œuvre au moment de l’appel à une fonction fait intervenir l’évaluation et ses règles (règles n˚ 2 et n˚ 3) ainsi que le type séquence et sa propriété essentielle d’aplatissement (voir la partie 2.6.1, page 59). En effet, les arguments passés à une fonction sont considérés comme formant une séquence (ce qui figure entre les parenthèses de l’instruction d’appel est une énumération d’objets séparés par des virgules, donc s’apparente naturellement à une séquence). Cette séquence est évaluée (ainsi donc que ses éléments) avant que les éléments de la séquence qui en résulte ne soient substitués aux paramètres de la fonction13 . Règle n˚ 5 : lors de l’appel à une fonction, chaque élément de la séquence des arguments transmis à la fonction est évalué de manière à former la séquence des arguments effectifs qui sont substitués aux paramètres de la fonction avant exécution de la fonction. À cause de la propriété d’aplatissement des séquences, lorsqu’un des arguments passés à la fonction est de type séquence, il peut arriver que les paramètres effectifs et les paramètres transmis soient en nombre différent. Illustrons cette situation : >
f:=proc(x,y,z) [x,y,z] end proc; f := proc(x, y, z) [x, y, z] end proc
12
Dans cette partie, le terme « fonction » désigne indifféremment une fonction écrite par l’utilisateur ou une commande du système Maple (relevant tous les deux du type procedure pour Maple) puisque le comportement de l’interpréteur est le même dans les deux cas. 13 Ce sont les éléments de la séquence résultat qui sont unifiés avec la séquence des paramètres de la fonction.
106
Maple : règles et fonctions essentielles
La fonction prend trois arguments x, y et z et produit la liste [x,y,z]. Lors de l’appel qui suit et présente une situation concrète, le résultat est conforme à l’objectif énoncé : >
f(1,2,3); [1, 2, 3]
L’appel suivant, plus abstrait, conduit lui aussi au résultat attendu : >
f(a,b,c); [a, b, c]
En revanche, lorsque certains des arguments transmis à la fonction au moment de l’appel sont des variables de type séquence, le résultat peut sembler déroutant : >
>
u:=0,2; v:=4,6,8; w:=10; u := 0, 2 v := 4, 6, 8 w := 10 f(u,v,w); [0, 2, 4]
La séquence des paramètres transmis au moment de l’appel est u,v,w. En vertu de la règle n˚ 5, chaque objet de cette séquence est évalué selon les règles n˚ 2 et n˚ 3 ; le résultat est placé, dans la même position que l’objet évalué, dans une séquence résultat qui forme la séquence des arguments effectifs. La séquence des arguments effectifs est donc ici 0,2,4,6,8,10 notamment en raison de la propriété d’aplatissement des séquences. Une fois cette séquence établie, son premier élément (ici 0) est substitué au premier paramètre de la fonction (ici x), son deuxième élément (ici 2) est substitué au deuxième paramètre de la fonction (ici y), et ainsi de suite jusqu’à épuisement des arguments effectifs ou des paramètres (ici, nous sommes dans ce dernier cas puisque la fonction compte trois paramètres alors que
3. Évaluation
107
la séquence des arguments effectifs compte six éléments). Les éventuels arguments effectifs en surnombre demeurent inutilisés14 . En revanche, si la séquence des arguments transmis s’avère être plus courte que celle des paramètres de la fonction, Maple affiche un message d’erreur. Voici, toujours avec la même fonction f et toujours pour les mêmes valeurs de u, v et w, deux autres situations illustrant notre propos : >
f(u);
Error, invalid input: f uses a 3rd argument, z, which is missing >
f(v); [4, 6, 8]
Lors de l’appel f(u), le système constate que la séquence des arguments effectifs, en l’occurrence 0,2, ne comporte pas assez d’éléments pour satisfaire tous les paramètres de la description de f. En revanche, lors de l’appel f(v), l’évaluation de v fait apparaître une séquence de trois éléments, ce qui suffit à contenter le système. La règle n˚ 5 s’applique à toutes les fonctions écrites par l’utilisateur ainsi qu’à la quasi-totalité des commandes Maple. Au rang des exceptions signalons les commandes evaln (voir la partie 3.7.3, page 111) et assigned (voir la partie 3.7.2, page 109) qui n’évaluent pas leurs arguments au moment de l’appel. Voici enfin un exemple typique de situation dans laquelle la méconnaissance de la règle n˚ 5 entraîne une erreur : >
x:=4; x := 4
>
diff(exp(x^2),x);
Error, invalid input: diff received 4, which is not valid for its 2nd argument
Il s’agit de calculer la dérivée première de la fonction x → exp(x2 ), à l’aide de la commande diff. L’appel à diff fait apparaître le nom x qui, dans le principe, est une variable muette servant seulement à définir les objets 14
Dans l’esprit de la fonction proposée en exemple, ils sont perdus ; voir la partie 4.7, page 135 pour davantage de précisions sur la gestion d’un nombre variable d’arguments.
108
Maple : règles et fonctions essentielles
mathématiques considérés. Cependant, x a déjà fait l’objet d’une définition. Au moment de l’appel à diff, les arguments sont évalués l’un après l’autre, conformément à la règle n˚ 5. La séquence des arguments transmis exp(x^2},x est transformée après évaluation en la séquence des arguments effectifs exp(16),4, inexploitable par la commande diff15 . Pour se prémunir contre cette évaluation intempestive de x, on peut différer son évaluation et formuler l’instruction ainsi : > diff(exp(’x’^2),’x’); 2
2 x e(x
)
Le lecteur trouvera encore d’autres exemples illustrant la règle n˚5 dans la partie 4.7 (page 135).
3.7
Commandes liées à l’affectation
Présentons maintenant un certain nombre de commandes qui concernent l’affectation, en nous bornant à en présenter les caractéristiques essentielles liées à l’évaluation.
3.7.1
Une autre façon de procéder à une affectation
La commande assign permet de procéder à des affectations, comme l’opérateur “:=”, mais avec une différence de comportement importante. Conformément au processus mis en œuvre lors d’un appel à une commande (règle n˚5, page 105), tous les arguments passés à assign sont évalués avant l’affectation en elle-même alors que, lors de l’utilisation de l’opérateur “:=”, seul le membre de droite est évalué. Illustrons notre propos par un exemple : > x:=y;x:=3; x := y x := 3 > x;y; 3 y 15
Comme l’indique le message d’erreur, le problème vient du deuxième argument passé à diff. L’expression passée en premier argument n’est pas conforme aux intentions de l’utilisateur, mais Maple aurait pu sans problème la dériver en la considérant comme une constante.
3. Évaluation
109
La suite d’instructions précédentes repose sur l’utilisation de l’opérateur “:=”. Utilisons maintenant la commande assign à la place de “:=” pour procéder aux affectations : > x:=y;assign(x,3); x := y > x;y; 3 3 L’appel à assign ne produit pas de résultat (en fait, elle retourne la séquence vide NULL qui ne s’affiche pas). On a créé la chaîne x •—> y 3 , comme on le vérifie : > eval(x,1);eval(x,2); y 3 Le lecteur est invité à consulter l’aide en ligne pour connaître d’autres utilisations possibles de la commande assign.
3.7.2
État d’une variable
La commande assigned permet de savoir si une variable est affectée ou non. Elle fait exception au processus mis en œuvre lors de l’appel d’une fonction (voir règle n˚ 5, page 105) puisque son argument n’est pas évalué. On conçoit aisément qu’il convient de récupérer le nom de l’argument avant son éventuel contenu pour déterminer son statut. . . Voici un exemple d’utilisation de la commande assigned : > assigned(a); false >
a:=1; a := 1
>
assigned(a); true
110
Maple : règles et fonctions essentielles
Nous invitons le lecteur à se reporter à l’aide en ligne pour connaître les subtilités de la commande assigned dans son utilisation conjointe avec la commande assume. La commande anames dresse la liste des variables affectées dans la session courante : >
>
a:=1;truc:=12.5;c:="baratin"; a := 1 truc := 12.5 c := “baratin” anames(); interface, truc, type/interfaceargs, a, c
anames ne prend pas d’argument mais il convient d’insister sur la forme de l’appel qui est bien anames() (c’est-à-dire celui d’une fonction sans paramètre) et non anames qui constituerait l’invocation d’un nom de variable. Par ailleurs, selon les instructions exécutées au cours de la session, la réponse peut aussi contenir des noms de variables d’interface (ici interface et type/interfaceargs). L’appel anames(user) limite la réponse aux seules variables définies par l’utilisateur : >
anames(user); truc, a, c
On peut aussi choisir de ne faire apparaître que les variables d’un type donné16 : >
anames(integer); Digits, Order , a, printlevel
Le lecteur est invité à se reporter à l’aide en ligne concernant les commandes assigned et anames pour connaître leurs différentes options. 16
Curieusement, on voit alors apparaître des variables d’environnement qui n’étaient pas présentes dans la réponse produite par l’instruction anames().
3. Évaluation
3.7.3
111
Désaffecter une variable
On a déjà vu une méthode pour désaffecter une variable, en s’appuyant sur l’opérateur qui permet de différer une évaluation (voir la partie 3.3, page 92) ; il suffit de lui affecter son nom : >
x:=2;x;x:=’x’;x; x := 2 2 x := x x
Nous rappelons ce procédé pour le comparer à d’autres dispositifs, par exemple, le recours à la commande unassign : >
x:=2;x; x := 2 2
>
unassign(x);
Error, (in unassign) cannot unassign ‘2’ (argument must be assignable)
Conformément à la règle n˚5 (voir la partie 3.6, page 105), cette commande procède à l’évaluation de son argument, ce qui explique le message d’erreur. La bonne façon de procéder consiste à bloquer l’évaluation de l’argument au moment de l’appel : >
unassign(’x’);
La commande unassign retourne une séquence vide qui ne s’affiche pas. La variable x est à présent désaffectée : >
x; x
112
Maple : règles et fonctions essentielles
La commande evaln propose une troisième manière de désaffecter une variable : elle évalue son argument en son nom. Comme la commande assigned, elle fait donc exception à la règle n˚ 5 (voir la partie 3.6, page 105) et n’évalue pas son argument en son contenu mais en son nom : >
x:=2;x;x:=evaln(x);x; x := 2 2 x := x x
3.8
La commande eval et ses dérivées
Dans cette partie nous passons en revue, la commande eval et ses dérivés qui portent des noms de la forme evalxxx où xxx est un suffixe qui indique le type de variables auquel s’applique ladite commande. Il faut connaître ces commandes car, en général, eval ne se substitue pas à elles.
3.8.1
La commande eval
Bien que nous ayons déjà rencontré la commande eval, précisons encore quelques points. L’instruction eval(expr) va provoquer l’évaluation de l’expression expr et débloquer jusqu’à deux niveaux d’évaluation différée. Ainsi vont être évalués d’abord chaque nom qui apparaît dans expr, puis les résultats obtenus ; tous les appels de fonction qui figurent dans expr seront aussi évalués ainsi que les paramètres afférents mais pas au-delà de deux niveaux. Voici une illustration simple de ce fonctionnement : >
a:=4; a := 4
>
eval(”a”); a
>
eval(’a’); 4
3. Évaluation
113
On voit clairement sur cet exemple que eval a débloqué deux niveaux d’évaluation différée. L’instruction eval(expr,n) évalue l’expression expr exactement au niveau n. On peut ainsi visualiser précisément les niveaux mis en jeu dans l’exemple précédent : >
eval(”a”,1); ’a’
>
eval(”a”,2); a
>
eval(”a”,3); 4
Le lecteur est invité à se reporter à l’aide en ligne afin de savoir pourquoi eval peut être une alternative préférable à la commande subs pour calculer la valeur d’une expression en un point.
3.8.2
Commandes spécifiques
Nous passons rapidement en revue les différentes commandes Maple dont le nom commence par eval. Elles présentent un lien avec l’évaluation, mais ont un champ d’application précis généralement lié à un type particulier. On reconnaît le type concerné par le suffixe du nom de la commande. Parmi les commandes les plus importantes de cette famille, déjà présentées par ailleurs, on compte : • evalb qui force une évaluation booléenne de son argument (voir la partie 2.4, page 41), • evalc qui met sous forme cartésienne son argument considéré comme nombre complexe (voir la partie 2.5.4, page 50), • evalf qui fournit une valeur décimale approchée de son argument (voir la partie 2.5.5, page 52), • evalhf qui permet de bénéficier de la vitesse du processeur sous-jacent à la plateforme de travail au prix d’une précision limitée pour le calcul de son argument (voir la partie 2.5.5, page 52), • evalm qui permet l’évaluation complète d’une matrice de type matrix (voir la partie 2.8.3, page 82), • evaln qui force l’évaluation de son argument à son nom (voir la partie 3.7.3, page 111).
114
Maple : règles et fonctions essentielles Pour être exhaustif, mentionnons encore :
• evala qui évalue des expressions dans des extensions algébriques. Comme les buts poursuivis par cette commande sortent du cadre mathématique élémentaire retenu pour cet ouvrage, nous ne détaillerons pas son fonctionnement ; le lecteur intéressé par cette commande est invité à consulter l’aide en ligne. • evalr qui concerne les intervalles (range). L’instruction evalr(expr) évalue une expression faisant intervenir des intervalles décrits à l’aide de la structure INTERVAL(a..b) ou des variables bornées décrites par une structure INTERVAL(x,a..b) ; elle indique l’intervalle parcouru par expr lorsque la variable décrit l’intervalle qui lui est assigné. Voici un exemple d’utilisation de cette commande : >
>
evalr(sin(INTERVAL(0..10))); INTERVAL(−1..1) evalr(sin(INTERVAL(0..Pi/2))); INTERVAL(0..1)
L’exemple suivant montre immédiatement les limites de cet outil : >
evalr((sin^2+cos^2)(INTERVAL(0..Pi/2))); INTERVAL(0..2)
Chapitre 4
Programmation 4.1
Préambule
Ce chapitre présente la syntaxe et les règles essentielles qui régissent la programmation avec Maple. Celui-ci étant un langage interprété, programmer revient à déclarer des variables de type fonction. Nous nous intéressons donc dans ce chapitre à l’écriture de fonctions. Comme dans la plupart des langages procéduraux, une fonction Maple consiste en une suite de déclarations et d’instructions devant être exécutées par le système lors d’un appel à cette fonction. Le description du déroulement des instructions est possible à l’aide de structures de contrôle dont la syntaxe est décrite dans la partie 4.2. Nous abordons ensuite les éléments essentiels qui interviennent dans la déclaration et l’écriture de fonctions en langage Maple (partie 4.3). Comme souvent, la déclaration de certains éléments est facultative, caractéristique qui fait la souplesse et l’agrément de ce langage de programmation. Néanmoins, Maple dispose de tous les outils permettant une programmation rigoureuse. Il permet en particulier de typer toutes les variables utilisées au moment de leur déclaration et propose un mécanisme de filtrage des paramètres que nous abordons dans la partie 4.4. Maple permet de recourir à des variables locales et à des variables globales ; les règles régissant la déclaration et l’utilisation de telles variables sont abordées dans la partie 4.5. Nous revenons ensuite sur le processus mis en œuvre lors de l’appel d’une fonction : la séquence des arguments transmis au moment de l’appel est évaluée et transformée en une nouvelle séquence d’arguments effectivement utilisés par la fonction (partie 4.6). La compréhension de ce mécanisme particulier à Maple est essentielle pour éviter certaines erreurs dans l’écriture de fonctions. Maple permet d’écrire des fonctions qui peuvent recevoir un nombre variable d’arguments ; les principes permettant la gestion d’un nombre variable d’arguments sont abordés
116
Maple : règles et fonctions essentielles
dans la partie 4.7. La partie 4.8 est consacrée aux fonctions récursives et aux tables de remember propres à Maple. La partie 4.9 montre comment on peut écrire des fonctions capables de produire un retour non évalué, ayant ainsi un comportement similaire à celui des commandes Maple prédéfinies. Comme d’autres langages procéduraux, Maple offre la possibilité de passer des arguments par variable (ou par adresse). La partie 4.10 décrit le mécanisme de passage par variable propre à Maple. Enfin, nous abordons dans la partie 4.11 les principes élémentaires relatifs à la gestion des entrées/sorties au sein d’une fonction pour permettre l’interaction avec l’utilisateur au cours de l’exécution de cette fonction. On verra ainsi comment écrire un programme interactif avec ce système qui repose sur un interpréteur et non sur un compilateur, ce qui complique légèrement l’interaction entre un programme et l’utilisateur. Nous nous sommes efforcés de présenter des exemples très simples pour illustrer les notions abordées ; leur étude apporte un éclairage complémentaire sur le fonctionnement et le comportement de certaines commandes Maple prédéfinies et, plus généralement, sur le fonctionnement du système.
4.2
Structures de contrôle
Si l’on ne se préoccupe pas de la présentation des résultats et plus généralement de la question des entrées/sorties1 , qui fait l’objet de la partie 4.11, on peut, dans un langage de programmation procédural, écrire la plupart des fonctions informatiques en ayant seulement recours à l’affectation, aux opérations logiques et arithmétiques élémentaires, en appelant d’autres fonctions et en organisant le déroulement des instructions à l’aide des trois structures de contrôle fondamentales que sont le branchement conditionnel, l’itération conditionnelle et l’itération non conditionnelle. Nous présentons dans cette partie la syntaxe Maple de ces trois structures de contrôle fondamentales. Maple étant interprété, il est possible de les utiliser directement au toplevel et pas seulement au sein d’un programme.
4.2.1
Branchement conditionnel
Le branchement conditionnel, ou structure alternative, n’offre que deux issues possibles à la poursuite du programme, s’excluant mutuellement. L’exécution de l’un des deux traitements distincts ne dépend que du résultat d’un test effectué sur une condition : si la condition est vérifiée, le premier traitement est exécuté ; si la condition n’est pas vérifiée, c’est le 1
C’est-à-dire des procédés permettant à l’utilisateur d’entrer en cours de programme des données saisies au clavier ou d’obtenir des résultats à l’écran ou, plus généralement, permettant à un programme de lire des données ou d’écrire des résultats dans un fichier.
4. Programmation
117
deuxième traitement qui est exécuté. Présente dans tous les langages de programmation modernes, la forme complète de cette structure de contrôle obéit à la syntaxe suivante dans le langage Maple2 : if condition then actions_1 else actions_2 end if Il est difficile de montrer cette structure en action au toplevel. Nous anticipons sur la syntaxe permettant de définir une fonction pour présenter un premier exemple d’utilisation de if, au sein d’une fonction ayant pour but de calculer la valeur absolue3 d’un nombre4 : >
Vabs := proc(x) if x>=0 then x else -x end if; end proc; Vabs := proc(x) if 0 ≤ x then x else − x end if end proc
Utilisons maintenant Vabs : >
Vabs(3); 3
>
Vabs(-2.5); 2.5
2
Le délimiteur end if remplace dans les versions récentes du logiciel l’ancien mot-clé fi. Pour assurer une compatibilité ascendante des versions successives du logiciel, la version actuelle du logiciel reconnaît aussi l’ancien délimiteur fi. 3 Bien sûr, une telle fonction est déjà implémentée dans Maple, sous le nom de abs. Il s’agit ici de traiter un exemple simple, sans prétendre qu’il est novateur ou original. 4 Rappelons que pour passer à la ligne au sein du même crochet d’exécution sans provoquer l’exécution individuelle d’une instruction au moment du changement de ligne, il faut presser simultanément les touches Majuscule et Entrée ( Shift et Enter sur un clavier anglo-saxon).
118
Maple : règles et fonctions essentielles
La fonction Vabs a un comportement satisfaisant sur les deux exemples précédents. Cependant, il l’est moins sur les deux suivants : >
Vabs(Pi);
Error, (in Vabs) cannot determine if this expression is true or false: 0 <= Pi >
Vabs(a);
Error, (in Vabs) cannot determine if this expression is true or false: 0 <= a
Ce problème se présente fréquemment lors de l’exécution de tests logiques. Ici, Maple n’est pas capable de décider du résultat du test x>=0 parce que l’argument π n’est pas spontanément évalué en un nombre signé. Le même écueil se rencontre avec l’argument a, variable non affectée au moment de l’appel, qui est évaluée en son nom. Dans une telle situation, le recours à la commande is5 permet de s’affranchir de la difficulté. Si l’on souhaite faire les choses proprement et discerner le cas où une variable non affectée a été passée en argument à Vabs, il faut procéder à un double test, x>=0 puis x<=0, pour pouvoir conclure sans ambiguïté. Ceci nous donne, en outre, l’occasion d’évoquer une option6 de la structure if, fondée sur le mot-clé elif (contraction de else if) (voir les détails dans l’aide en ligne), qui facilite l’imbrication de plusieurs niveaux de if : >
5
Vabs2 := proc(x) if is(x>=0) then x elif is(x<=0) then -x else ’Vabs2’(x) end if; end proc;
L’instruction is(e,p) détermine si l’expression e satisfait la propriété p. La réponse donnée par le prédicat is est true si toutes les valeurs que peut prendre l’expression e satisfont la propriété p. La réponse est false si l’une des valeurs que peut prendre e ne satisfait pas p. Enfin la réponse est FAIL si Maple n’est pas capable de déterminer s’il se trouve dans l’un des deux cas précédents, par manque d’information ou parce qu’il est impossible de faire les déductions logiques demandées. 6 Cette option permet de s’approcher du fonctionnement de l’instruction case présente dans certains langages comme C ou Pascal.
4. Programmation
119
Vabs2 := proc(x) if is(0 ≤ x) then x elif is(0 ≤ −x) then − x else ’Vabs2 ’(x) end if end proc Reprenons les situations précédentes avec Vabs2 : > Vabs2(3); 3 > Vabs2(-2.5); 2.5 > Vabs2(Pi); π > Vabs2(a); Vabs2(a) Cette nouvelle version Vabs2 est capable de traiter le cas de π. Elle a par ailleurs un comportement analogue à celui de toutes les commandes Maple lorsqu’on l’applique à une variable non affectée : elle produit un retour non évalué (voir la partie 4.9, page 146), ce qui se traduit par le fait qu’elle répète la question posée. Rappelons que Maple travaille avec une logique à trois états (voir la partie 2.4, page 41), true (« vrai »), false (« faux ») et FAIL (« je ne sais pas »). Cette situation a une incidence sur le déroulement des instructions conditionnelles. Il est important de retenir que, dans le cas où le test conduit à la réponse FAIL, c’est la partie else qui est exécutée. Il en résulte qu’il n’est pas équivalent d’écrire : if condition then actions_1 else actions_2 end if et d’écrire : if not condition then actions_2 else actions_1 end if Dans le premier cas, une réponse FAIL au test (et par voie de conséquence à son complémentaire) conduit à l’exécution de actions_2 alors que dans le second il conduit à l’exécution de actions_1. Il convient donc de savoir ce que doit faire le programme en cas de résultat FAIL lors des tests dans les instructions if.
120
Maple : règles et fonctions essentielles
4.2.2
Itération non conditionnelle
L’itération, ou structure itérative, répète l’exécution d’une opération ou d’un traitement. Dans le cas de l’itération non conditionnelle, le nombre de répétitions est connu à l’avance et la sortie de la boucle se produit lorsque le nombre de répétitions souhaitées est atteint. On utilise une variable, ou indice, de contrôle d’itération caractérisée par sa valeur de début, sa valeur de fin et son pas de variation. En langage Maple, cette structure itérative obéit à la syntaxe suivante7 : for indice from valeur_debut to valeur_fin by pas do actions end do Voici un exemple simple de boucle for : >
for k from 5 to 11 by 2 do k^2 od; 25 49 81 121
On pourra consulter l’aide en ligne pour connaître les différentes options8 de la syntaxe de la boucle for ; parmi celles-ci, la partie by est facultative et par défaut le pas d’incrémentation est de 1 (voir la partie 2.5.5, page 52 à propos de la commande evalf) : >
for k from 1 to 5 do evalf[k](Pi) od; 3. 3.1 3.14 3.142 3.1416
7
end do remplace l’ancien mot-clé od dans les versions récentes du logiciel. Pour assurer une compatibilité ascendante des versions successives du logiciel, la version actuelle du logiciel reconnaît aussi l’ancien délimiteur od. 8 Mentionnons la possibilité, extrêmement commode, d’utiliser une boucle for avec un compteur de boucle qui prend pour valeurs les différents opérandes d’une expression (voir la partie 5.4.4, page 191).
4. Programmation
4.2.3
121
Itération conditionnelle
Contrairement au cas de l’itération non conditionnelle, la répétition de l’exécution du traitement d’une itération conditionnelle dépend d’une condition logique testée à chaque itération. Dans la forme while retenue par le langage Maple, l’instruction commence par tester la condition et, si elle est vérifiée, exécute les actions du corps de la boucle, puis teste à nouveau la condition et ainsi de suite (ainsi, il peut arriver que le corps de la boucle ne soit jamais exécuté). L’itération conditionnelle en Maple obéit à la syntaxe suivante9 : while condition do actions end do Dans l’exemple suivant, on s’intéresse à la suite définie par le premier terme u0 = 1 et la relation de récurrence un+1 = sin(un ). On cherche le premier terme de la suite ayant une valeur absolue inférieure à 0.65 en procédant avec une boucle while : >
>
epsilon:=0.65; u:=1.; n:=0; ε := 0.65 u := 1. n := 0 while abs(u) > epsilon do n:=n+1; u:=sin(u); od; n := 1 u := 0.8414709848 n := 2 u := 0.7456241417
9 Le lecteur attentif aura remarqué que le délimiteur de fin de boucle, end do, est le même, que l’on ait affaire à une boucle for ou à une boucle while. Nous avons choisi de faire, conformément aux habitudes des (bons) cours d’algorithmique, une présentation séparée de l’itération conditionnelle et de l’itération non conditionnelle ; dans le langage de programmation Maple, ces deux boucles relèvent d’une seule et même structure dont les différentes composantes sont optionnelles, permettant d’obtenir une boucle for ou une boucle while selon les termes retenus, et permettant (malheureusement) aussi de mêler les deux, ce qui est peu recommandable. . . D’une manière générale, nous limitons notre exposé aux instructions strictement nécessaires pour écrire des programmes, sans présenter toutes les options et détails, dont certains sont de toute façon à nos yeux nuisibles à une programmation de bon goût. Ainsi, nous ne nous étendons pas sur l’instruction break qui permet une sortie exceptionnelle de boucle (for ou while), et dont l’utilisation, comme celle du goto dans certains langages, est proscrite par tout programmeur soucieux de respecter les canons de la programmation déclarative.
122
Maple : règles et fonctions essentielles n := 3 u := 0.6784304774 n := 4 u := 0.6275718321
4.3 4.3.1
Fonctions Déclaration d’une fonction
Une fonction informatique réalise une association entre un certain nombre d’entrées, appelées paramètres, et une sortie10 . La méthode permettant d’obtenir la sortie à partir des entrées est décrite dans le corps de la fonction. Cette méthode est constituée d’une suite d’instructions11 qui seront exécutées séquentiellement dans l’ordre où elles apparaissent. Le déroulement des instructions peut être organisé à l’aide des structures de contrôle vues précédemment. Une fonction est un type de variable comme un autre pour Maple ; en particulier, une fonction peut être passée en argument à une autre fonction ou à une commande. Maple se montre en cela supérieur à de nombreux langages de programmation, comme C ou Pascal, pour lesquels les fonctions sont des objets très particuliers dont les propriétés ne sont pas analogues à celles des autres types. Dans le langage de programmation Maple, la définition d’une fonction repose sur les mots-clés proc et end proc12 qui servent à délimiter la déclaration d’une fonction. 10
Dans la plupart des langages de programmation, une fonction produit exactement un objet en sortie. Cependant, dans un langage élaboré, on peut facilement faire en sorte qu’une fonction ne produise aucune valeur en sortie ou, à l’inverse, en produise plusieurs. Certains langages comme Pascal distinguent d’ailleurs les fonctions des procédures ; une procédure n’est rien d’autre qu’une fonction qui ne produit aucune sortie (le mot-clé proc tire d’ailleurs son origine du terme procedure). En Maple, il est facile d’écrire une fonction qui ne produit aucune sortie : il suffit de produire la séquence vide NULL. De même, il est très simple de réaliser une fonction qui produit plusieurs sorties : il suffit de regrouper les différents objets qui doivent être présents en sortie dans une variable structurée comme une liste. 11 Comme on pourra le constater implicitement dans les exemples qui suivent, deux instructions de même niveau doivent pouvoir être distinguées l’une de l’autre par le système. On utilise à cet effet le “ ;” pour les séparer (voir la partie 4.3.4, page 125). 12 Le délimiteur end proc remplace dans les versions récentes du logiciel l’ancien motclé end. Pour assurer une compatibilité ascendante des versions successives du logiciel, la version actuelle du logiciel reconnaît aussi l’ancien délimiteur end.
4. Programmation
123
Voici un exemple très simple de fonction, qui présente la syntaxe minimale à mettre en œuvre pour définir une fonction. Il s’agit d’implémenter13 la fonction numérique x → x2 + 1 : > f:=proc(x) x^2+1; end proc; f := proc(x) x2 + 1 end proc On définit donc une fonction en déclarant à l’interpréteur une nouvelle variable, à laquelle on affecte une structure délimitée par proc . . . end proc. Dans l’exemple présenté, la fonction f dépend du paramètre x comme l’indique la séquence x placée entre parenthèses à côté du mot-clé proc. Au moment de l’appel, il faudra donc en principe lui transmettre un argument. Une fonction peut même ne prendre aucun argument, comme dans l’exemple suivant : > bonjour:=proc() "Salut !" end proc; bonjour := proc() “Salut !” end proc La fonction bonjour ne prend aucun paramètre en entrée et son action consiste simplement à produire la chaîne de caractère "Salut !", comme on va le voir dans la partie qui suit14 .
4.3.2
Appel d’une fonction
Pour utiliser cette fonction, on procède à son appel en écrivant son nom suivi de l’argument qu’on souhaite lui transmettre : 13
Encore une fois, l’exemple n’a aucune prétention scientifique ! Le lecteur averti sait que l’on peut se servir de l’opérateur “->” pour déclarer f de manière (presque) équivalente en écrivant f := x->xˆ2+1 ;. Cette définition de f conduit au même corps de fonction que celui obtenu dans l’exemple à un détail près : la fonction f est alors affublée de deux options supplémentaires, l’option arrow et l’option operator, qui conduisent Maple à la présenter conformément aux notations mathématiques usuelles pour écrire une fonction numérique (avec une flèche) au lieu de l’écrire comme avec le mot clé proc suivi d’un corps de fonction (informatique). 14 Insistons sur le fait suivant : la fonction bonjour produit un objet en sortie, à savoir la chaîne "Salut !". Il ne faut pas confondre ce comportement avec l’utilisation d’une instruction d’affichage comme print("Salut !") (voir la partie 4.11, page 151) qui induit un affichage à l’écran sous forme d’effet de bord mais qui ne produit pas d’objet en sortie.
124
Maple : règles et fonctions essentielles
>
f(1);
>
f(10);
2 101 On verra dans la partie 4.7 qu’une fonction peut accepter un nombre variable d’arguments. Une fonction peut aussi être appelée sans argument. Dans ce cas, il convient de bien faire figurer les parenthèses (qui ne délimiteront que du vide) pour éviter la confusion entre une invocation du nom de la fonction et un appel à la fonction. Un certain nombre de commandes Maple, comme rand15 , fonctionnent selon ce principe. Voici comment appeler la fonction bonjour définie dans la partie précédente : >
bonjour(); “Salut !”
4.3.3
Valeur produite en sortie
Pour comprendre pourquoi les fonctions f et bonjour ont bien le comportement espéré, il faut connaître le principe suivant qui est spécifique à Maple. Dans le cas d’un déroulement normal16 , l’objet produit en sortie par une fonction Maple est le résultat de la dernière instruction exécutée par l’interpréteur avant de rencontrer le délimiteur end proc qui marque la fin du corps de la fonction17 . Il convient dès à présent d’attirer l’attention du lecteur sur une confusion extrêmement répandue. Dans le langage de programmation Maple, il existe le mot-clé return (équivalent, dans les versions récentes du logiciel, du mot-clé RETURN des premières version de Maple) que de nombreux auteurs ou utilisateurs utilisent de manière erronée, pensant probablement 15
Ł’appel rand() produit un nombre entier aléatoire. Se reporter à l’aide en ligne pour connaître les différentes possibilités offertes par la commande rand. 16 C’est-à-dire si aucune erreur n’intervient lors de l’exécution des instructions successivement rencontrées par l’interpréteur dans le corps de la fonction. Si l’on rencontre une telle erreur, l’exécution de la fonction se termine de manière anormale avec la production d’un message d’erreur qui est affiché pour l’utilisateur si l’appel a eu lieu au toplevel, ou qui est passé à l’instance appelante dans le cas d’un appel emboîté. 17 Noter que la dernière instruction exécutée par l’interpréteur n’est pas nécessairement celle qui figure juste avant le délimiteur end proc dans l’écriture de la fonction.
4. Programmation
125
que return joue un rôle analogue au mot-clé homonyme du langage C. En C, return sert à préciser la valeur produite en sortie par une fonction. Comme l’indique la règle que nous venons de voir, la syntaxe Maple ne requiert pas de mot-clé particulier pour préciser la valeur de sortie d’une fonction. Dans le langage de programmation Maple, return (ou RETURN) sert à forcer une sortie exceptionnelle d’une fonction18 . Lorsque l’interpréteur rencontre return, il interrompt l’exécution de la fonction et passe l’argument de return à l’instance appelante (en d’autres termes, la valeur retournée par la fonction est l’argument de return). Voici donc, en principe, ce qu’il faut éviter d’écrire pour définir la fonction f : >
f2 := proc(x) return(x^2+1) end proc; f 2 := proc(x) return x2 + 1 end proc
Dans les situations courantes, un programmeur n’a pratiquement jamais besoin de recourir à return19 . Le recours délibéré à return est tolérable lorsqu’on programme volontairement dans un contexte où l’on gère les exceptions et où l’on tire parti explicitement du mécanisme de rattrapage d’erreur implémenté dans Maple. Voir, à ce propos, l’aide en ligne consacrée à traperror, try . . . catch ou lasterror.
4.3.4
Utilisation du “ ;”
Le lecteur a vraisemblablement remarqué l’utilisation de “;” comme symbole servant à délimiter deux instructions successives dans la description d’une fonction. Il convient de préciser un certain nombre de principes à propos de l’utilisation du “;”, l’utilisation intempestive de ce séparateur pouvant conduire à des erreurs d’interprétation du système. Le principe général est que le “;” sert à séparer des déclarations ou des instructions de même niveau. Ainsi, dans la fonction somme (dont l’étude est détaillée plus loin, voir la partie 4.5, page 131), il n’apparaît qu’une 18
De manière analogue, le mot-clé error (équivalent de ERROR dans des versions plus anciennes du logiciel), conduit à l’arrêt de l’exécution de la fonction courante et au déclenchement d’une erreur. Cette erreur peut ensuite être rattrapée par le mécanisme de gestion des erreurs. Consulter à ce propos l’aide en ligne décrivant les commandes try, traperror et lasterror. 19 Ni au mot-clé break (qui a pour effet de provoquer une sortie anormale de boucle, chose à proscrire selon nous), de même qu’un programmeur qui se respecte n’utilise jamais de goto dans des langages comme Pascal lorsqu’il écrit un programme qui se veut déclaratif.
126
Maple : règles et fonctions essentielles
seule instruction20 dans le corps de la boucle for, et il n’est pas nécessaire de la terminer par un “;”. Ajouter un “;” ne change rien à l’interprétation de la fonction par le système car on signifie ainsi que l’unique instruction effectivement présente est séparée d’une instruction qui est vide. Le même principe vaut pour la dernière instruction de la fonction (s ;). On peut donc indifféremment déclarer : > somme:=proc(n::nonnegint)::nonnegint; local i, s; s:=0; for i from 1 to n do s:=s+i; end do; s; end proc; somme := proc(n : :nonnegint ) : :nonnegint ; local i, s; s := 0 ; for i to n do s := s + i end do ; s end proc ou : > somme:=proc(n::nonnegint)::nonnegint; local i, s; s:=0; for i from 1 to n do s:=s+i end do; s end proc; somme := proc(n : :nonnegint ) : :nonnegint ; local i, s; s := 0 ; for i to n do s := s + i end do ; s end proc Le programmeur prudent aura tendance à faire figurer spontanément tous les “;”, y compris ceux qui ne sont pas nécessaires, en prévision de 20
Les instructions situées au sein du corps de la boucle for ne sont pas du même niveau que les instructions qui sont dans le corps de la fonction. La fonction comporte ici une déclaration (local i, s ;) suivie de trois instructions (l’initialisation de s à la valeur 0, la boucle for et l’appel à s).
4. Programmation
127
l’évolution future de son programme : s’il doit ajouter des instructions à certaines parties de la fonction, il n’y aura pas de faute de syntaxe liée à l’oubli éventuel d’un séparateur rendu nécessaire par la modification. Il convient toutefois de mettre en garde contre une situation dans laquelle l’insertion d’un “;” conduit à une erreur (dans la version actuelle du logiciel). Elle est liée à une bizarrerie syntaxique de Maple21 , dans les versions récentes où il est devenu possible de typer et de filtrer la valeur de retour d’une fonction (voir la partie 4.4, page 129). En l’occurrence, considérant toujours l’exemple de la fonction somme, il convient impérativement de faire figurer le “;” à la fin de la première ligne juste après la déclaration du type de la valeur de retour de la fonction. Dans ce genre de situations, l’absence du “;” conduit à une erreur de syntaxe et au rejet de la déclaration par l’interpréteur : > somme:=proc(n::nonnegint)::nonnegint local i, s; s:=0; for i from 1 to n do s:=s+i; end do; s; end proc; Error, reserved word ‘local‘ unexpected
En revanche, si l’on renonce à déclarer le type de la valeur de retour, chose fréquente lorsqu’on écrit les premières versions d’une nouvelle fonction, il ne faut pas terminer la première ligne par un “;”, sous peine de provoquer une erreur de syntaxe et un rejet par l’interpréteur : > somme:=proc(n::nonnegint); local i, s; s:=0; for i from 1 to n do s:=s+i end do; s end proc; Error, reserved word ‘local‘ unexpected
21
La possibilité de typer et de filtrer le résultat d’une fonction n’est apparue que dans les version récentes du logiciel ; cette possibilité implique la démarcation avec un “ ;” mais les versions récentes de Maple devraient alors – selon nous – aussi accepter la présence du “ ;” à la fin de l’en-tête lorsque le type de la valeur de retour n’est pas précisé (tout en acceptant aussi l’absence du “ ;” pour assurer une compatibilité ascendante).
128
Maple : règles et fonctions essentielles
Dans ce cas, il convient donc d’écrire : >
somme:=proc(n::nonnegint) local i, s; s:=0; for i from 1 to n do s:=s+i end do; s end proc; somme := proc(n : :nonnegint ) local i, s; s := 0 ; for i to n do s := s + i end do ; s end proc
4.3.5
Insertion de commentaires
Il est possible d’insérer des commentaires dans l’écriture d’une fonction, selon les mêmes modalités que lorsqu’on saisit des instructions au toplevel (voir la partie 1.4.4, page 15) : le caractère “#” sert à introduire un commentaire. Tout ce qui suit ce caractère et la fin de la ligne dans laquelle il figure n’est pas pris en compte par l’interpréteur : >
somme:=proc(n) local i, s; # variables locales s:=0; # initialisation for i from 1 to n do s:=s+i end do; s # valeur retournée par la fonction end proc;
somme := proc(n) local i, s; s := 0 ; for i to n do s := s + i end do ; s end proc
4. Programmation
4.4
129
Filtrage des arguments
Dans les versions récentes de Maple, il est possible de recourir au filtrage des paramètres passés à une fonction. La déclaration du type des paramètres d’une fonction au moment de sa définition demeure facultative. Cependant, comme il se doit lorsqu’on se conforme aux canons de la programmation déclarative, il est recommandé de déclarer les paramètres utilisés. On bénéficie ainsi des avantages que procure le filtrage des arguments au moment de l’appel à la fonction. Le symbole “::” sert à introduire le type d’un paramètre ou d’une variable. Illustrons par un exemple très simple l’apport du filtrage. La fonction étudiée a pour but d’annoncer la parité de l’argument qui lui est passé en entrée, ce qui sous-entend que cet argument est de type entier relatif. La réponse fournie par la fonction est du texte ; elle produit donc une chaîne de caractères en sortie, c’est-à-dire une variable de type string. Voici une première façon d’implémenter une telle fonction, dans laquelle le type du paramètre n’est pas déclaré : > parite1 := proc(n) if n mod 2 = 0 then "pair" else "impair" end if end proc; parite1 := proc(n) if n mod 2 = 0 then “pair” else “impair” end if end proc Voici une seconde manière d’écrire la fonction dans laquelle, cette fois, le type de l’argument est déclaré : > parite2 := proc(n::integer) if n mod 2 = 0 then "pair" else "impair" end if end proc; parite2 := proc(n : :integer ) if n mod 2 = 0 then “pair” else “impair” end if end proc
130
Maple : règles et fonctions essentielles
Comparons le comportement des fonctions parite1 et parite2 : >
parite1(1);parite1(2);parite1(1.5); “impair” “pair”
Error, (in parite1) invalid argument for modp or mods >
parite2(1);parite2(2);parite2(1.5); “impair” “pair”
Error, invalid input: parite2 expects its 1st argument, n, to be of type integer, but received 1.5
Lorsqu’on appelle les deux fonctions sur des entiers relatifs, leur comportement est identique. En revanche, lors de l’appel sur l’argument 1.5, dans le premier cas, une erreur se produit au cours de l’exécution de la fonction parite1, tandis que dans le deuxième cas, la fonction parite2 déclenche elle-même une exception22 . Le problème lié au défaut de type de l’argument est contrôlé dans le cas de la fonction parite2 alors qu’il est subi dans le cas de parite1. Comme dans la plupart des langages procéduraux déclaratifs, le type de la valeur produite par la fonction peut être précisé au moment de la définition, toujours à l’aide du symbole “::”. Noter dans ce cas la nécessité de mettre un “;” à la fin de la première ligne (alors qu’il faut, au contraire, éviter de mettre ce “;” dans le cas où l’on ne précise pas le type de la valeur retournée par la fonction) : >
22
parite3 := proc(n::integer)::string; if n mod 2 = 0 then "pair" else "impair" end if end proc;
Maple propose un mécanisme pour gérer les exceptions et rattraper les erreurs. On trouve des renseignements sur ce mécanisme en consultant les pages d’aide en ligne consacrées aux mots-clés try . . . with, traperror ou lasterror.
4. Programmation
131
parite3 := proc(n : :integer ) : :string; if n mod 2 = 0 then “pair” else “impair” end if end proc Avec cette option, un contrôle est effectué au moment de l’exécution de la fonction et une exception est déclenchée23 si le type de la valeur retournée n’est pas conforme au type déclaré lors de la définition de la fonction. Insistons sur le fait que, dans le cas où l’on déclare le type produit en sortie par la fonction, il est nécessaire de terminer la première ligne par un délimiteur “;” alors qu’il ne faut pas mettre un tel délimiteur dans le cas où l’on ne déclare pas le type produit en sortie. Dans ses dernières versions, Maple propose de nombreuses options pour déclarer les types des paramètres d’une fonction, notamment pour les fonctions qui admettent un nombre variable d’arguments (voir la partie 4.7, page 135). Il est même possible de déclarer des valeurs initiales par défaut pour les arguments facultatifs. Le lecteur est invité à consulter la page d’aide en ligne concernant le symbole “::” pour connaître les multiples options disponibles. Enfin, on déclare le type des variables locales d’une fonction (voir la partie 4.5, page 131) selon le même principe, en ayant recours au symbole “::”.
4.5
Variables locales - Variables globales
Maple permet à l’utilisateur de définir des variables locales (à une fonction) et des variables globales (à la session). Dans les versions de Maple postérieures à la version V.4, les règles de fonctionnement des variables locales et globales sont analogues à celles en vigueur dans la plupart des langages de programmation procéduraux. Une variable locale n’existe qu’au sein de la fonction qui la définit24 . Tous les objets utilisés ou définis à l’intérieur de cette fonction, y compris les fonctions locales25 à cette fonction, peuvent accéder au contenu de cette 23
Plus précisément, une exception est déclenchée si la variable du noyau assertlevel dédiée à la gestion des assertions est activée, c’est-à-dire si sa valeur est 2, ce que l’on obtient au moyen de l’instruction kernelopts(assertlevel=2) ;. 24 Conformément à l’usage dans les langages procéduraux, elle est créée au moment d’un appel à la fonction qui la déclare et sa durée de vie est limitée au temps d’exécution de cet appel. 25 Tel n’était pas le cas jusqu’à la version V.4 dans laquelle le comportement des variables locales était un peu plus exotique : seule la fonction définissant une variable locale avait accès à cette variable, pas les fonctions localement définies dans cette fonction. Il n’y avait pas de visibilité hiérarchique par inclusion.
132
Maple : règles et fonctions essentielles
variable. Conformément à l’usage, au sein de la fonction qui la définit, une variable locale est prioritaire sur une variable globale de même nom et sur toute variable locale de niveau supérieur de même nom. Une variable globale est globale à l’ensemble de la session dans laquelle elle est définie. Une variable définie au toplevel est, par défaut, une variable globale. Cependant, pour qu’une telle variable soit vue par une fonction, elle doit être déclarée explicitement comme globale dans la fonction qui souhaite l’utiliser. En l’absence de déclaration explicite26 , Maple va décider de la nature, locale ou globale, des identificateurs nouveaux (i.e. qui ne figurent pas dans la liste des identificateurs présents dans la mémoire de travail attachée à la session) qui apparaissent dans la définition d’une fonction. Le cas échéant, Maple informe l’utilisateur de sa décision en affichant un message d’alerte (warning). Considérons la fonction somme1 suivante dont le but est de calculer la somme des n premiers entiers naturels. Le paramètre entier n désigne la borne supérieure de la somme qui est calculée classiquement par accumulation dans la variable intermédiaire s l’aide d’une boucle for : > somme1:=proc(n::nonnegint)::nonnegint; s:=0; for i from 1 to n do s:=s+i; end do; s; end proc; Warning, ‘s‘ is implicitly declared local to procedure ‘somme‘ Warning, ‘i‘ is implicitly declared local to procedure ‘somme‘
somme1 := proc(n : :nonnegint) : :nonnegint ; local s, i; s := 0 ; for i to n do s := s + i end do ; s end proc On constate que les deux variables i et t, qui n’avaient pas été déclarées lors de la définition de la fonction, ont été déclarées locales par Maple lors de l’interprétation de la définition. Bien évidemment, il est conseillé de déclarer explicitement toutes les variables utilisées dans un programme, de manière à éviter toute ambiguïté. Ainsi, on préférera écrire la fonction précédente de la manière suivante : 26
Procédé peu recommandable que tout bon programmeur évite soigneusement.
4. Programmation
133
> somme2:=proc(n::nonnegint)::nonnegint; local i, s; s:=0; for i from 1 to n do s:=s+i; end do; s; end proc; somme2 := proc(n : :nonnegint ) : :nonnegint; local i, s; s := 0 ; for i to n do s := i + s end do ; s end proc Par ailleurs, il est important d’initialiser les variables locales que l’on utilise, comme le montre l’exemple suivant : > somme3:=proc(n::nonnegint)::nonnegint; local i, s; for i from 1 to n do s:=s+i; end do; s; end proc;
>
somme3 := proc(n : :nonnegint ) : :nonnegint; local i, s; for i to n do s := i + s end do ; s end proc somme3(5); 15 + s
La valeur calculée par la fonction somme3 renvoie au contenu initial de la variable s qui, n’ayant pas de contenu, s’évalue en son nom (voir règle n˚1, partie 3.2, 92). Maple diffère en cela d’autres langages de programmation qui auraient simplement récupéré la valeur figurant dans la case mémoire d’étiquette s (conduisant la plupart du temps à un résultat numérique incohérent). Il est tout aussi fortement conseillé de déclarer les variables globales utilisées dans une fonction. Toujours sur la base de la fonction précédemment étudiée, on peut envisager les définitions suivantes, avec l’intention
134
Maple : règles et fonctions essentielles
d’utiliser au sein de la fonction somme4 la variable s définie au toplevel, donc implicitement globale : > >
s:=10; s := 10 somme4:=proc(n::nonnegint)::nonnegint; local i; for i from 1 to n do s:=s+i; end do; s; end proc;
Warning, ‘s‘ is implicitly declared local to procedure ‘somme4‘
somme4 := proc(n : :nonnegint) : :nonnegint ; local i, s; for i to n do s := s + i end do ; s end proc Cependant, comme on peut s’y attendre à l’aune des exemples précédents, cette façon de procéder n’est pas concluante puisque la variable s qui intervient dans la description de la fonction27 somme4 va être considérée comme locale par Maple. Elle sera donc distinguée de son homonyme globale : >
somme4(5); s + 15
La présence de la variable globale s ne change rien au déroulement des opérations et on se retrouve dans une situation rigoureusement analogue à celle vue pour la fonction somme3. Pour parvenir à nos fins, il convient donc de déclarer s comme variable globale au sein de la fonction. >
s:=10; s := 10
27
Nous avons choisi de conserver la même base pour toutes les fonctions qui servent d’illustrations à cette partie afin d’en simplifier la lecture.
4. Programmation >
>
135
somme5:=proc(n::nonnegint)::nonnegint; global s; local i; for i from 1 to n do s:=s+i; end do; s; end proc; somme5 := proc(n : :nonnegint ) : :nonnegint; local i; global s; for i to n do s := s + i end do ; s end proc somme5(5); 25
Cette fois, le comportement de la fonction somme5 est conforme aux espoirs du programmeur : la somme des cinq premiers entiers naturels est ajoutée au contenu préalable de la variable globale s.
4.6
Séquence des arguments effectifs
Nous revenons à présent brièvement sur le déroulement du processus mis en œuvre au moment de l’appel à une fonction. Il s’agit de rappeler la règle n˚5, essentielle, énoncée au chapitre 3 : lors de l’appel à une fonction, chaque élément de la séquence des arguments transmis à la fonction est évalué de manière à former la séquence des arguments effectifs qui sont substitués aux paramètres de la fonction avant exécution de celle-ci. Le lecteur est invité à se reporter à la partie 3.6 (page 105) pour apprécier la portée de cette règle et, en particulier, son impact dans le cas où certains des arguments transmis à la fonction au moment de l’appel sont de type séquence. Cette règle explique aussi pourquoi on ne peut passer des variables de type séquence à certaines commandes Maple.
4.7
Nombre variable d’arguments
Un autre aspect important concerne le déroulement d’un appel de fonction, et plus particulièrement le traitement de la séquence des arguments reçus à l’appel : c’est la possibilité d’écrire avec Maple des fonctions capables de gérer un nombre variable d’arguments.
136
Maple : règles et fonctions essentielles
Selon un schéma analogue à celui mis en œuvre pour les programmes en langage C, à toute fonction Maple sont associées deux variables intrinsèques locales, appelées _passed et _npassed (respectivement args et nargs dans les versions plus anciennes de Maple), qui sont créées au moment d’un appel28 à la fonction et qui contiennent respectivement la séquence et le nombre d’arguments transmis au moment de l’appel. Voici, par exemple, comment on peut utiliser ces variables intrinsèques29 pour écrire une fonction mini qui calcule le minimum d’une collection de valeurs numériques dont le nombre n’est pas connu au moment de l’appel30 . La fonction définit trois variables locales. La variable i est un banal compteur de boucle ; s est une variable intermédiaire qui contient à chaque itération de la boucle for la plus petite valeur des paramètres d’entrée rencontrée jusqu’alors ; enfin, aux est une fonction auxiliaire définie localement31 au sein de la fonction mini qui retourne le plus petit des deux arguments qui lui sont passés. Dans la boucle for, on parcourt la séquence des arguments passés à mini que l’on trouve dans _passed et on contrôle cette boucle au moyen de _npassed. >
28
mini:=proc() local i, s, aux; aux := proc(a,b) if a
Très précisément, ces deux variables sont créées au moment d’un appel à la fonction, sont spécifiques à cet appel et n’existent que le temps de son exécution. Les deux variables sont donc non seulement intrinsèques à la fonction mais encore intrinsèques à un appel particulier à celle-ci. 29 On remarquera que l’interpréteur a conservé à _passed et _npassed leurs anciens noms, args et nargs, lorsqu’il donne sa réponse à la définition de la fonction. . . 30 Bien évidemment, Maple met déjà à la disposition de l’utilisateur une telle fonction, qui répond au nom de min. L’exemple étudié a pour objet principal d’illustrer les notions qui viennent d’être exposées. 31 On remarque au passage que les fonctions Maple sont bien des types comme les autres puisqu’on peut définir une fonction constituant une variable locale pour une autre fonction.
4. Programmation
>
>
137
mini := proc() local i, s, aux ; aux := proc(a, b) if a < b then a else b end if end proc ; s := args1 ; for i from 2 to nargs do s := aux (s, argsi ) end do ; s end proc mini(1); 1 mini(1,2,3,0,3); 0
On constate que cette définition de mini fonctionne dès qu’on lui passe au moins un argument32 . Certaines commandes Maple, comme plot, print, max ou min, savent traiter un nombre variable d’arguments. Dans les versions récentes de Maple (corrélativement aux changements des noms args et nargs respectivement en _passed et _npassed), Maple met à la disposition de l’utilisateur toute une panoplie de variables intrinsèquement attachées à une fonction33 , comme _rest, _nrest, _options, _noptions, _params et _nparams, qui permettent de gérer finement les valeurs et le nombre (variable) d’arguments passés à la fonction. Le lecteur trouvera une présentation détaillée de ces variables dans l’aide en ligne. Considérons encore l’exemple de la fonction demo suivante afin de préciser clairement le contenu des variables intrinsèques _passed et _npassed et d’illustrer la distinction établie entre séquence des arguments transmis et séquence des arguments effectifs (voir la partie 3.6, page 105). Cet exemple nous permet aussi de présenter certaines des autres variables intrinsèquement attachées à une fonction. La fonction demo affiche34 le contenu 32
L’appel mini() déclenche une erreur liée au fait que l’on demande à accéder à _passed[1] qui n’existe pas dans ce cas. Bien sûr, on pourrait éviter facilement cet inconvénient en testant au préalable la valeur de _npassed. . . 33 Plus précisément, comme _passed et _npassed, ces variables ont une existence qui se limite au déroulement d’un appel et sont donc intrinsèques à un appel particulier de la fonction considérée. 34 La commande print (voir la partie 4.11, page 151) a pour effet de produire l’affichage brut (c’est-à-dire, sans mise en forme particulière) des contenus des expressions qui lui sont passées en argument, séparés par des virgules. La commande print sait gérer un nombre variable d’arguments. Dans l’exemple considéré, print s’applique à trois arguments. Le premier est une chaîne de caractère qui sera répercutée telle quelle avec ses délimiteurs, le deuxième un entier (_npassed ou _nparams), et le troisième une liste (contenant les éléments de la séquence _passed ou _params). La commande print ne produit aucune valeur en sortie, elle ne procède que par effets de bord.
138
Maple : règles et fonctions essentielles
des variables _npassed et _passed puis celui des variables _nparams et _params35 qui lui sont attachées, avant de produire la liste des arguments effectifs. On peut ainsi clairement constater que _passed et _npassed réfèrent aux arguments transmis à la fonction au moment de l’appel alors que _nparams et _params réfèrent à la séquence des arguments effectifs utilisés par le corps de la fonction : >
>
demo:=proc(a,b,c) print("arguments transmis",_npassed,[_passed]); print("arguments effectifs",_nparams, [seq(_params[i],i=1.._nparams)]); [a,b,c]; end proc; demo := proc(a, b, c) print(“arguments transmis”, nargs, [args]) ; print(“arguments effectifs”, _nparams, [seq(_paramsi , i = 1.._nparams)]); [a, b, c] end proc demo(1,2,3); “arguments transmis”, 3, [1, 2, 3] “arguments effectifs”, 3, [1, 2, 3] [1, 2, 3]
En particulier, on visualise bien la différence entre la séquence des arguments transmis et la séquence des arguments effectifs sur l’exemple suivant. La séquence transmise s,t s’aplatit en une séquence de quatre éléments (voir la partie 2.6.1, page 59) au moment de sa transformation en séquence des arguments effectifs : >
s:=5,6;t:=7,8; s := 5, 6
35
Les concepteurs de Maple ont choisi de brider délibérément l’accès à _params. En effet, afin d’éviter tout malentendu ou toute utilisation erronée de la séquence _params, en particulier dans le cas où elle ne contiendrait qu’un seul élément (voir la partie 2.6.1, page 59), on ne peut accéder aux éléments de la séquence qu’en les désignant un par un avec l’opérateur de sélection. Cela explique la manipulation effectuée avec la commande seq pour obtenir la liste de tous les éléments de _params.
4. Programmation
139
t := 7, 8 >
demo(s,t); “arguments transmis”, 4, [5, 6, 7, 8] “arguments effectifs”, 3, [5, 6, 7] [5, 6, 7]
4.8
Fonctions récursives
Il est très simple de déclarer une fonction récursive. Contrairement à certains langages comme Caml ou Pascal, aucun mot-clé distinctif n’est requis pour déclarer le caractère récursif d’une fonction. La simple présence d’un appel récursif conduit l’interpréteur à constater que la fonction est récursive. Voici un premier exemple de fonction récursive, qui permet de calculer les termes de la très classique suite de Fibonacci36 . Cette suite récurrente linéaire à deux termes est définie par les relations f0 = 0, f1 = 1 et ∀n ≥ 2, fn = fn−1 + fn−2 . La fonction fib1 s’applique à des entiers naturels (implémentés par le type nonnegint37 ). On définit la relation générale : > fib1:=proc(n::nonnegint)::nonnegint; fib1(n-1)+fib1(n-2); end proc; fib1 := proc(n : :nonnegint ) : :nonnegint ; fib1(n − 1) + fib1(n − 2) end proc puis les relations particulières indiquant les valeurs des premiers termes de la suite : > fib1(0):=0;fib1(1):=1; fib1(0) := 0 fib1(1) := 1 36
Certes cet exemple est rebattu, mais il permet d’illustrer simplement les notions présentées dans cette partie, notamment le gain apporté par les tables de remember. 37 Rappelons que le type nonnegint décrit les entiers naturels alors que le type posint décrit les entiers naturels non nuls.
140
Maple : règles et fonctions essentielles
On peut désormais obtenir la séquence des dix premiers termes de la suite : >
seq(fib1(i),i=0..9); 0, 1, 1, 2, 3, 5, 8, 13, 21, 34
Une erreur classique consiste à déclarer les relations particulières qui définissent les valeurs initiales de la suite avant de déclarer la fonction décrivant la relation général, comme dans l’exemple suivant : >
>
fib1(0):=0;fib1(1):=1; fib1(0) := 0 fib1(1) := 1 fib1:=proc(n::nonnegint)::nonnegint; fib1(n-1)+fib1(n-2); end proc;
fib1 := proc(n : :nonnegint ) : :nonnegint ; fib1(n−1) + fib1(n−2) end proc > fib1(10); Error, (in fib1) invalid input: fib1 expects its 1st argument, n, to be of type nonnegint, but received -1
La déclaration de la règle générale définissant une fonction prévaut en effet sur toute définition préalable attachée à celle-ci38 . On constate ici que l’instruction fib1(10) a lancé l’exécution d’une récursion sans fin (puisque le système en arrive à demander le calcul de fib1(-1). Le filtrage du type de l’argument de la fonction fib1 conduit alors à une exception39 qui interrompt l’exécution du programme. Voici ce qui se produit si l’on n’a pas recours au filtrage :
38
Tout simplement parce que lors de la déclaration générale de la fonction, on définit la variable fib1 à laquelle on affecte la déclaration de fonction ; tout ancien contenu éventuel de la variable en question est donc perdu. Soulignons que lorsqu’on définit des valeurs particulières, on n’évoque pas la variable fib1, mais on mentionne, par exemple, fib1(0). 39 Dans un souci de programmation de qualité, on pourra ainsi apprécier l’apport du filtrage qui déclenche ici une exception susceptible d’être rattrapée au lieu d’une erreur qui interrompt définitivement le cours des opérations.
4. Programmation
141
>
fib1(0):=0;fib1(1):=1; fib1(0) := 0 fib1(1) := 1
>
>
fib1:=proc(n); fib1(n-1)+fib1(n-2); end proc; fib1 := proc(n) fib1(n − 1) + fib1(n − 2) end proc fib1(10);
Error, (in fib1) too many levels of recursion
Maple interrompt les appels récursifs emboîtés car il a atteint la limite d’espace disponible dévolu à leur gestion. Il est préférable d’intégrer la définition des valeurs initiales de la suite au sein de la déclaration de la fonction comme suit40 : >
fib2:=proc(n::posint)::posint; if (n=0 or n=1) then 1 else fib2(n-1)+fib2(n-2) end if; end proc; fib2 := proc(n : :posint ) : :posint; if n = 0 or n = 1 then 1 else fib2(n − 1) + fib2(n − 2) end if end proc
On évite ainsi tout souci lié à l’amorce de la récursion. Il est connu qu’une telle implémentation du calcul des termes de la suite de Fibonacci est peu efficace puisque l’algorithme sous-jacent est de complexité exponentielle : chaque exécution d’un appel à fib2 lance deux nouveaux appels à la fonction. Une manière plus efficace de procéder consiste à prendre en compte les valeurs de fn déjà calculées, par exemple de la manière suivante : 40
Cette façon de procéder a notre préférence mais elle a un coût algorithmique puisque le test n=0 or n=1 est effectué à chaque appel récursif de la fonction.
142
Maple : règles et fonctions essentielles
>
fib3:=proc(n::nonnegint)::nonnegint; local i, u, v, w; if n=0 then 0 elif n=1 then 1 else u:=1; v:=0; for i from 2 to n do w:=v; v:=u; u:=v+w; end do; end if; end proc; fib3 := proc(n : :nonnegint) : :nonnegint ; local i, u, v, w; if n = 0 then 0 elif n = 1 then 1 else u := 1 ; v := 0 ; for i from 2 to n do w := v ; v := u ; u := v + w end do end if end proc
Cette fonction implémente un algorithme dont la complexité est linéaire et dont l’occupation mémoire est constante. Une autre manière de procéder consiste à conserver toutes les valeurs déjà calculées, par exemple dans un tableau. C’est ce que propose Maple à travers l’utilisation d’un table qui est automatiquement associée à une fonction si l’on ajoute à celle-ci l’option remember. Lorsque cette option est activée, Maple crée une table qu’il associe à la fonction considérée. Chaque nouvelle valeur calculée est placée dans la table. Au moment d’un appel, le système va regarder si l’image de la valeur passée en argument est présente dans la table et lance l’exécution de la fonction seulement dans la négative. Voici d’abord une nouvelle version de la fonction qui fait apparaître le nombre d’appels récursifs mis en œuvre pour faire le calcul du nombre de Fibonacci désiré, selon l’algorithme exponentiel :
4. Programmation
143
> fib4:=proc(n::nonnegint)::nonnegint; local nbappels, fibaux; fibaux:=proc(k) nbappels:=nbappels+1; if k=0 then 0 elif k=1 then 1 else fibaux(k-1)+fibaux(k-2) end if; end proc; nbappels:=0; [fibaux(n),nbappels]; end proc; fib4 := proc(n : :nonnegint) : :nonnegint ; local nbappels, fibaux ; fibaux := proc(k) nbappels := nbappels + 1 ; if k = 0 then 0 elif k = 1 then 1 else fibaux (k − 1) + fibaux (k − 2) end if end proc; nbappels := 0 ; [fibaux (n), nbappels] end proc On a déclaré une fonction auxiliaire récursive, fibaux, comme variable locale à la fonction fib4. La fonction fib4 produit en sortie, sous forme de liste à deux éléments, le couple formé par la valeur du nombre de Fibonacci demandé et le nombre d’appels à la fonction auxiliaire : > fib4(10); [55, 177] > fib4(20); [6765, 21891]
144
Maple : règles et fonctions essentielles
Le calcul de f10 a nécessité 177 appels à fibaux et celui de f20 en a nécessité 21891. La fonction fib5 présente exactement la même organisation que fib4 mais incorpore en outre l’option remember lors de la déclaration de la fonction auxiliaire fibaux : >
fib5:=proc(n::nonnegint)::nonnegint; local nbappels, fibaux; fibaux:=proc(k) option remember; # cette ligne diffère de fib4 nbappels:=nbappels+1; if k=0 then 0 elif k=1 then 1 else fibaux(k-1)+fibaux(k-2) end if; end proc; nbappels:=0; [fibaux(n),nbappels]; end proc; fib5 := proc(n : :nonnegint ) : :nonnegint ; local nbappels, fibaux ; fibaux := proc(k) option remember ; nbappels := nbappels + 1 ; if k = 0 then 0 elif k = 1 then 1 else fibaux (k − 1) + fibaux (k − 2) end if end proc; nbappels := 0 ; [fibaux (n), nbappels] end proc
On observe le gain apporté par l’option remember :
4. Programmation
145
>
fib5(10); [55, 11]
>
fib5(20); [6765, 21]
Désormais, le calcul de fn nécessite exactement n + 1 appels à la fonction. La complexité algorithmique de la fonction est donc devenue linéaire à partir du moment où l’option remember a été activée. Cette option a bien évidemment un coût. Dorénavant, l’espace mémoire nécessaire à la poursuite des calculs n’est plus constant mais proportionnel à l’indice de la valeur calculée. Cela n’apparaît pas directement à l’utilisateur puisque la table de remember associée à la fonction est cachée41 . Nous nous devions de présenter et d’illustrer le dispositif constitué par les tables de remember 42 . Nous ne résistons cependant pas à la tentation de proposer encore une autre version récursive de cette fonction. La récursion, terminale cette fois, conduit à un temps d’exécution proportionnel à n : >
fib6:=proc(n::nonnegint,u::nonnegint,v::nonnegint) ::nonnegint; if n = 0 then u else fib6(n-1,v,u+v) end if; end proc;
fib6 := proc(n : :nonnegint , u : :nonnegint , v : :nonnegint) : :nonnegint ; if n = 0 then u else fib6(n − 1, v, u + v) end if end proc > seq(fib6(i,0,1),i=0..9); 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 41
Plus précisément, les dimensions de la table de remember sont limitées, comme celles de n’importe quel objet informatique. On verra dans la partie 4.11.1 (page 151) et au chapitre 5 comment visualiser la table de remember associée à une fonction. 42 Pour être précis, soulignons que l’option remember n’est pas réservée aux fonctions récursives mais s’applique à toute fonction, même non récursive.
146
Maple : règles et fonctions essentielles
4.9
Retour non évalué
Il peut être utile d’écrire des fonctions dont le comportement se rapproche de celui des commandes Maple qui sont capables de produire des retours non évalués. Voici, à titre d’exemple, une fonction qui détermine le plus grand des deux éléments qui lui sont passés en argument. > maxi:=proc(x,y) if x>y then x else y end if end proc; >
>
maxi := proc(x, y) if y < x then x else y end if end proc maxi(2,3); 3 maxi(x,2*x);
Error, (in maxi) cannot determine if this expression is true or false: 2*x < x
Lorsqu’on applique la fonction maxi à deux arguments qui ne s’évaluent pas en des valeurs numériques, le système ne peut pas trouver de résultat au test x>y. Voici comment on peut procéder pour obtenir un retour non évalué et obtenir une fonction dont le comportement est similaire à celui de la plupart des commandes Maple43 : > maxi2:=proc(x,y) if type(x,numeric) and type(y,numeric) then if x>y then x else y end if else ’maxi’(x,y); end if end proc; maxi2 := proc(x, y) if type(x, numeric) and type(y, numeric) then if y < x then x else y end if else ’maxi ’(x, y) end if end proc 43
Cela suppose que, comme dans l’exemple étudié, on soit en mesure de contrôler la nature ou les propriétés des arguments reçus.
4. Programmation >
maxi2(2,3);
>
maxi2(x,2*x);
147
3 maxi(x, 2 x)
4.10
Passage d’un argument par variable
Certains langages de programmation, comme Pascal ou C, permettent de distinguer au moment d’un appel de fonction entre passage d’un argument par valeur et passage d’un argument par variable. Tous les exemples étudiés jusqu’à présent dans ce chapitre relèvent de passages d’arguments par valeur : au moment de l’appel, c’est la valeur de l’argument appelé qui est substituée au paramètre qui le décrit dans la déclaration de la fonction. Dans le cas d’un passage par variable, c’est l’adresse de l’argument, avec son contenu éventuel, qui est utilisée par la fonction. En conséquence, un argument passé par variable peut être modifié par l’exécution de la fonction qui le reçoit (chose impossible dans le cadre d’un passage par valeur). Un passage par variable a d’ailleurs généralement pour principal objectif de fournir à la fonction une case mémoire dans laquelle la fonction devra inscrire une valeur qui servira ultérieurement à d’autres fonctions. On peut se demander s’il est vraiment pertinent et indispensable de recourir à ce procédé dans le cadre des langages de programmation modernes qui offrent par ailleurs des variables globales (donc accessibles à différentes fonctions pour partager des informations communes). . . Toujours est-il que ce moyen est disponible sous Maple et qu’il nous paraît utile de le mentionner et d’expliquer son principe de fonctionnement car certaines commandes Maple, comme iquo ou irem, y ont recours. Considérons la fonction suivante dont le but est de déterminer si l’élément passé en premier argument est présent dans la liste passée en deuxième argument. Un troisième argument, facultatif au moment de l’appel44 , a pour objet de contenir la position dans la liste de la première occurrence de l’élément recherché dans le cas où cet élément est bien présent dans la liste45 : 44 45
Voir la partie 4.7 page 135 pour la gestion d’un nombre variable d’arguments. Rappelons que nops appliqué à une liste fournit le nombre d’éléments de cette liste.
148
Maple : règles et fonctions essentielles
>
membre:=proc(x::anything,L::list,p)::boolean; local i; i:=1; while L[i]<>x and i<=nops(L) do i:=i+1 end do; if i > nops(L) then false else if _npassed > 2 then p:=i end if; true; end if; end proc; membre := proc(x : :anything, L : :list, p) : :boolean; local i; i := 1 ; while Li = x and i ≤ nops(L) do i := i + 1 end do ; if nops(L) < i then false else if 2 < nargs then p := i end if ; true end if end proc
Le paramètre p est utilisé dans le cadre d’un passage par variable. Il contient la position de la première occurrence éventuelle de x dans L46 . Dans ce type de conception, le troisième argument de la fonction est facultatif et a pour objet d’apporter une information supplémentaire, autre que la valeur retournée par la fonction, si l’utilisateur le souhaite. Ainsi, dans l’exemple étudié, on peut se contenter de demander si un élément donné est présent dans la liste passée en deuxième argument : >
membre(33,[1,33,2,4,22]); true
On peut choisir de recourir au troisième argument pour connaître aussi la position de l’élément recherché :
46
Le type anything permet de filtrer n’importe quel type de variable ; l’utiliser permet une programmation déclarative de bon aloi.
4. Programmation
149
>
>
>
membre(33,[1,33,2,4,22],i); true membre(33,[1,33,2,4,22],k); true k; 2
Toutefois, il faut prendre garde à utiliser un nom de variable libre pour le troisième argument, sans quoi on s’expose au problème suivant : >
k;
>
2 membre(2,[1,33,2,4,22],k);
Error, (in membre) illegal use of a formal parameter
Au moment de l’appel, conformément aux règles d’évaluation (voir la partie 3.6, page 105), le troisième argument, k, est évalué au moment de former la séquence des arguments effectifs. Or, lors de ce deuxième appel à la fonction membre, la variable i contient la valeur 2, résultat du précédent appel à membre. En conséquence, au moment de l’appel, au lieu d’être évalué en son nom (c’est-à-dire son adresse) k est évalué en son contenu, 2, rendant impossible le mécanisme de passage par variable sollicité ultérieurement par l’exécution de la fonction. Une première façon de procéder pour se prémunir contre cette difficulté consiste à imposer le blocage de l’évaluation (voir la partie 3.3, page 92) de la variable utilisée pour le passage par variable. On est ainsi certain de passer un nom, quelles que soient les opérations réalisées antérieurement : >
>
membre(2,[1,33,2,4,22],’k’); true k; 3
L’inconvénient de cette façon de procéder est qu’il faut y penser systématiquement. Une manière plus définitive de traiter ce problème consiste à
150
Maple : règles et fonctions essentielles
modifier la définition de la fonction en précisant le type de son troisième argument de la manière suivante : >
membre2:=proc(x::anything,L::list,p::evaln)::boolean; local i; i:=1; while L[i]<>x and i<=nops(L) do i:=i+1 end do; if i > nops(L) then false else if _npassed > 2 then p:=i end if; true; end if; end proc; membre2 := proc(x : :anything, L : :list, p : :evaln) : :boolean; local i; i := 1 ; while Li = x and i ≤ nops(L) do i := i + 1 end do ; if nops(L) < i then false else if 2 < nargs then p := i end if ; true end if end proc
Le type evaln (dont le nom n’est évidemment pas sans rapport avec la commande Maple homonyme, voir la partie 3.7.3, page 111) force l’évaluation de l’argument passé en son nom au moment de la création de la séquence des arguments effectifs. Ainsi, il n’y a plus à se préoccuper de savoir si le troisième argument est une variable libre ou non au moment de l’appel : >
>
membre2(2,[1,33,2,4,22],k); true k; 3
4. Programmation
4.11
151
Entrées/Sorties
Certains programmes nécessitent le recours à des données extérieures, issues de fichiers ou saisies par l’utilisateur. D’autres programmes conduisent à des résultats que l’on souhaite afficher ou conserver dans des fichiers. Pour répondre à ces besoins, Maple est doté d’un ensemble assez fourni de commandes permettant de gérer les entrées/sorties. Cette partie aborde succinctement les commandes les plus fondamentales en la matière, et en détaille certaines qui permettent de traiter les situations les plus courantes. Pour une présentation plus complète des commandes d’entrées/sorties disponibles sous Maple, le lecteur est invité à se reporter à l’aide en ligne ou à consulter (en français) le chapitre 9 de [8].
4.11.1
La commande print
La commande print affiche, comme effet de bord, la valeur des expressions qui lui sont passées en arguments et retourne la séquence vide NULL en sortie. Les variables d’interface prettyprint et verboseproc déterminent la forme de la présentation des résultats (se reporter à l’aide en ligne). Par défaut, l’affichage produit par print est mis en forme de manière standard47 par le pretty-printer. Cette commande permet d’obtenir très simplement l’affichage du contenu d’une variable sans que l’on ait à se préoccuper de son type48 , ce qui est particulièrement pratique pour la mise au point des programmes. >
>
a:=1;b:=false;c:=Pi;d:=Matrix(2,2,[[1,0],[0,1]]); a := 1 b := false c := π
1 0 d := 0 1 print(a,b,c,d);
1 0 1, false, π, 0 1
La commande print peut aussi être utilisée pour visualiser le corps d’une fonction, à condition que la variable d’interface verboseproc soit au 47 Voir la commande lprint pour une présentation qui ne soit pas mise en forme par le pretty-printer. 48 Les utilisateurs de Caml apprécieront.
152
Maple : règles et fonctions essentielles
moins au niveau 1. C’est le cas par défaut, comme le montre le résultat de l’instruction suivante : >
interface(verboseproc); 1
Reprenons une des fonctions précédemment étudiées : >
somme:=proc(n) local i, s; # variables locales s:=0; # initialisation for i from 1 to n do s:=s+i end do; s # valeur retournée par la fonction end proc; somme := proc(n) . . . end proc
Si l’on change la valeur49 de verboseproc en 0 : >
interface(verboseproc=0); 1
Maple donne une réponse très succincte : >
print(somme); proc(n) . . . end proc
Lorsque verboseproc est à 0, l’affichage d’une fonction ou d’une commande par print se réduit au squelette de la fonction50 . Pour une valeur 49
On remarque que la valeur produite par l’instruction interface(verboseproc=0) ; est la précédente valeur de verboseproc. 50 L’affichage fait apparaître le contenu du champ description de la fonction considérée, si celle-ci en est doté (voir la partie 5.3.2, page 184).
4. Programmation
153
de verboseproc égale à 1, l’interpréteur affiche le corps des fonctions programmées par l’utilisateur. Pour la valeur 2, on obtient, en outre, l’affichage du corps des commandes Maple lorsque c’est possible (c’est-à-dire quand ce ne sont pas des commandes compilées faisant partie du noyau — commandes dites built-in). Enfin, pour verboseproc égal à 3, Maple affiche non seulement le corps des fonctions considérées, mais aussi le contenu de la table de remember qui lui est associée (voir la partie 4.8, page 139). Il est instructif d’étudier la réponse fournie par Maple pour certaines commandes, lorsque verboseproc est égal à 3. Dans le cas de min, qui est compilée dans le noyau, la réponse est laconique : > interface(verboseproc=3); 2 > print(min); proc() option builtin = min; end proc Dans le cas de sin, on obtient une réponse nettement plus détaillée, que nous abrégeons ici, et qui se termine par les valeurs qui figurent en permanence dans la table de remember associée à cette fonction : > print(sin); proc(x : :algebraic) local res, n, t, pull _out , keep_in, tmp; optionbuiltin = HFloat_sin, automap(1), ‘Copyright (c) 19 \ 92 by the University of Waterloo. All rights reserved .‘; if nargs = 1 then error “expecting 1 argument, got %1”, nargs elif type(x, ’complex(float)’) then return evalf(’sin’(x)) ... # [permanent](π/2) = 1 # [permanent](I) = sinh(1) ∗ I # [permanent](−∞) = undefined # [permanent](∞) = undefined # [permanent](π/6) = 1/2 # [permanent](0) = 0 # [permanent](π) = 0 # [permanent](π/4) = 2(1/2) /2 # [permanent](π/3) = 3(1/2) /2
154
4.11.2
Maple : règles et fonctions essentielles
Commandes d’entrées/sorties fondamentales
Types de fichiers gérés par Maple Maple gère deux sortes de fichiers, les fichiers tamponnés (STREAM) et les fichiers non tamponnés (RAW)51 . Les fichiers tamponnés sont gérés de manière plus efficace que les fichiers non tamponnés. Par défaut, la plupart des commandes d’entrées/sorties travaillent sur des fichiers tamponnés. Maple distingue aussi les fichiers textes (TEXT) des fichiers binaires (BINARY). La différence principale entre ces deux types de fichiers dépend du système d’exploitation sous-jacent et réside dans la manière de traiter le caractère de fin de ligne (New Line). À un moment donné, un fichier peut être ouvert en lecture ou en écriture. Les commandes permettant de spécifier si une requête s’applique en lecture ou en écriture utilisent les identificateurs READ ou WRITE52 . Les commandes d’entrées/sorties considèrent que l’interface Maple est un « fichier » comme un autre. Plus précisément, default désigne le flot d’entrée par défaut et terminal désigne le flot de sortie par défaut. Les commandes d’entrées/sorties peuvent référer à un fichier de deux façons : par un nom ou par un descripteur. La référence à un fichier par descripteur est familière aux utilisateurs de langages de programmation comme C ou Pascal. Un descripteur sert à identifier un fichier après qu’il a été désigné par son nom au moment de son ouverture. Ouverture et fermeture de fichiers Les deux commandes qui permettent d’ouvrir un fichier sont fopen pour les fichiers non tamponnés et open pour les fichiers tamponnés. Les commandes correspondantes permettant de fermer un fichier sont respectivement fclose et close. Fin de fichier La commande feof permet de savoir si l’on a atteint la fin du fichier auquel on l’applique.
51
Dans certains langages de programmation, des mémoires dites « tampons » sont utilisées pour le stockage temporaire de données lors du transfert d’informations afin de compenser la différence de débit, de traitement ou de synchronisation entre certains dispositifs d’un ordinateur et ses périphériques. Par exemple, lors de l’écriture dans un fichier sur disque, on va remplir la mémoire tampon progressivement puis, à la demande ou lorsqu’elle est pleine, adresser en une fois le contenu de la mémoire au fichier. Ce système permet de limiter les opérations d’écriture dans le fichier, qui sont beaucoup plus lentes que les autres types d’instructions. 52 Si l’on souhaite conserver le contenu du fichier et écrire à la suite du contenu existant, il faut ouvrir le fichier en mode APPEND.
4. Programmation
155
Lecture de lignes de texte dans un fichier La commande readline lit exactement une ligne de texte dans un fichier. Plus précisément, les caractères sont lus jusqu’à ce que le système rencontre le marqueur de fin de ligne (New Line). La commande readline élimine le marqueur de fin de ligne et retourne le texte récupéré sous forme d’une chaîne de caractères. Si readline ne parvient pas à lire une ligne complète dans le fichier, elle retourne l’entier 0 au lieu d’une chaîne de caractères. Un appel typique effectué à l’aide de la commande readline se présente sous la forme readline(IdentificateurFichier ) où IdentificateurFichier est le nom ou le descripteur du fichier dans lequel s’effectue l’opération de lecture. On peut omettre IdentificateurFichier, auquel cas Maple utilise le mot-clé default à la place de l’identificateur absent. Ainsi les appels readline() et readline(default) sont équivalents. L’appel readline(default) conduit Maple à lire dans le flot d’entrée courant. En particulier, au toplevel, un tel appel conduit Maple à lire et à détourner la ligne saisie par l’utilisateur ; cette ligne n’est pas transmise à l’interpréteur. C’est donc à l’aide de la commande readline que l’on peut écrire des programmes interactifs (voir l’exemple figurant à la fin de cette partie). Si l’on appelle readline avec un nom de fichier, et que ce fichier n’est pas encore ouvert, Maple l’ouvre en lecture et le considère comme un fichier texte. Si la commande readline retourne 0 — ce qui signifie qu’on est arrivé à la fin du fichier, Maple ferme ce fichier. Lecture formatée Les commandes fscanf et scanf permettent de lire dans un fichier et de filtrer des valeurs de différents types (nombres, booléens, caractères, chaînes. . .). Ces commandes, qui fonctionnent selon le même principe que leurs homonymes du langage C, retournent une liste constituée des objets reconnus par le filtrage. En cas d’échec, ces commandes retournent 0 au lieu d’une liste (ce qui signifie que la fin du fichier a été atteinte avant obtention du nombre d’objets requis). La commande fscanf obéit à la syntaxe fscanf(IdentificateurFichier, Format). IdentificateurFichier est bien entendu le nom ou le descripteur du fichier qu’on veut lire. L’appel fscanf(Format) est une version courte de l’appel fscanf(defaut, Format ). Le Format est une chaîne de caractères constituée d’une suite de spécifications de types éventuellement séparés par d’autres caractères qui obéit à des règles analogues aux formats du langage C. Les formats les plus courants sont %d, %f et %s ; ils servent respectivement à filtrer un entier, un flottant ou une chaîne de caractères. Plus encore que dans le langage C, de nombreux autres formats ainsi que des options sont disponibles ; le
156
Maple : règles et fonctions essentielles
lecteur en prendra connaissance en consultant l’aide en ligne à propos de fscanf. Écriture formatée Les commandes fprintf et printf sont les pendants de fscanf et scanf pour l’écriture formatée dans des fichiers. Leur syntaxe s’en déduit. Écriture de chaînes dans un fichier La commande writeline permet d’écrire dans un fichier les chaînes qui lui sont passées en argument. Chaque chaîne apparaît sur une ligne distincte. Premier exemple Voici un premier exemple qui illustre le fonctionnement des commandes que nous venons de passer en revue. On commence par ouvrir en écriture et en mode texte un fichier dont le nom complet dans l’arborescence des fichiers de la machine sous-jacente est “E :\Essai_Maple\Fichier_Test.txt” : >
f:=fopen("E:\Essai_Maple\Fichier_Test.txt",WRITE,TEXT); f := 0
La réponse 0 signifie que Maple a réalisé avec succès l’ouverture du fichier désigné par son emplacement physique et l’a associé au descripteur f. On écrit une ligne de texte (chaîne de caractères) dans le fichier : >
writeline(f,"Ceci est un essai"); 18
La réponse donnée par Maple indique le nombre de caractères écrits dans le fichier53 . Enfin, on ferme le fichier54 : 53
Selon le système d’exploitation sous-jacent, Maple comptabilise aussi le caractère de fin de ligne qu’il place automatiquement à la fin du texte. 54 Cette opération n’est pas obligatoire mais il est conseillé d’être rigoureux et de fermer les fichiers qui ne sont plus en utilisés. Lorsqu’une session est terminée, ou lors d’un appel à restart, Maple ferme tous les fichiers qui seraient encore ouverts.
4. Programmation
157
> fclose(f); Si l’on souhaite connaître le contenu du fichier que l’on vient de créer, il faut d’abord l’ouvrir en lecture : > f:=fopen("E:\Essai_Maple\Fichier_Test.txt",READ,TEXT); f := 0 On lit ensuite une ligne dans le fichier et on place son contenu dans la variable texte : > texte:=readline(f); texte := “Ceci est un essai” > texte; “Ceci est un essai” Si l’on essaie de lire une nouvelle ligne dans le fichier : > texte2:=readline(f); texte2 := 0 on récupère la valeur 0 qui signifie que readline a rencontré la fin du fichier. Fermons à présent le fichier considéré : > fclose(f); et voyons ce qui se passe si l’on essaie à nouveau de lire dans ce fichier : > texte3:=readline(f); Error, (in readline) file descriptor not in use
158
Maple : règles et fonctions essentielles
Maple produit un message d’erreur lié au fait que le descripteur f n’est plus lié à un fichier depuis la fermeture du fichier précédemment utilisé. Deuxième exemple Considérons un autre exemple simple de manipulations avec les fichiers, faisant intervenir les commandes fprintf et fscanf d’écriture et de lecture formatées. On ouvre à nouveau le fichier “E :\Essai_Maple\Fichier_Test.txt” : >
f:=fopen("E:\Essai_Maple\Fichier_Test.txt",WRITE,TEXT); f := 0
Dans la mesure où l’on ouvre le fichier considéré en mode WRITE, on écrase tout contenu que ce fichier aurait pu avoir préalablement55 . À l’aide d’une boucle for, on va écrire cinq lignes dans le fichier que l’on vient d’ouvrir56 . À chaque itération de la boucle, on écrit un nombre entier (en l’occurrence i) signalé par le format %d, puis une espace, puis un nombre flottant (en l’occurrence evalf(sqrt(i))) signalé par le format %f, et finalement le caractère de fin de ligne désigné par “\n” : >
for i from 1 to 5 do fprintf(f,"%d %f\n",i,evalf(sqrt(i))): end do:
On ferme le fichier : >
fclose(f);
Consultons à présent le contenu du fichier. On commence par ouvrir le fichier considéré : 55
Si l’on souhaite conserver le contenu du fichier et écrire à la suite du contenu existant, il faut ouvrir le fichier en mode APPEND. 56 Si le fichier n’avait pas été préalablement ouvert, il aurait été ouvert automatiquement par Maple lors de l’exécution de la première instruction fprintf.
4. Programmation
159
>
f:=fopen("E:\Essai_Maple\Fichier_Test.txt",READ,TEXT); f := 0
On parcourt le fichier ligne à ligne jusqu’à ce qu’on atteigne la fin du fichier. Le parcours est effectué à l’aide d’une boucle while. La boucle est contrôlée par un test qui fait appel à la commande feof. Dans le corps de la boucle, on utilise la commande fscanf à la recherche d’un entier et d’un flottant sur chaque ligne : >
while not(feof(f)) do L:=fscanf(f,"%d %f\n"); end do; L := [1, 1.000000] L := [2, 1.414214] L := [3, 1.732051] L := [4, 2.000000] L := [5, 2.236068]
Le résultat d’un appel à fscanf est une liste comportant autant d’éléments qu’il y avait de variables à filtrer dans le format passé en deuxième argument à fscanf. Conversion formatée de chaînes Les commandes sprintf et sscanf sont semblables aux commandes printf/fprintf et scanf/fscanf mais lisent ou écrivent dans des chaînes au lieu de le faire dans des fichiers. L’utilisation conjointe des commandes readline et sscanf permet d’écrire une fonction capable de prendre en compte des saisies effectuées interactivement par l’utilisateur. Voici un exemple qui illustre l’utilisation de ces commandes : >
saisie:=proc() local L; printf("Donnez un nombre\n"); L:=sscanf(readline(),"%d"); printf("La valeur saisie est : %d\n",L[1]); end proc;
160
Maple : règles et fonctions essentielles
saisie := proc() local L; printf(“Donnez un nombre \n”); L := sscanf(readline(), “%d”) ; printf(“La valeur saisie est : %d\n”, L1 ) end proc La fonction saisie affiche à l’écran le texte “Donnez un nombre” à destination de l’utilisateur57 . Ensuite, l’exécution de l’instruction readline() (qui apparaît au sein d’un appel à sscanf) provoque l’arrêt du déroulement du programme, en attente d’une saisie de l’utilisateur au clavier. Dès que l’utilisateur a pressé la touche Entrée, le programme reprend ; le contenu du résultat produit par readline() est alors filtré par sscanf et le résultat est placé dans la liste L. Enfin, le programme fait afficher le contenu du premier (et unique) élément de L à l’aide de la commande printf : >
saisie();
Donnez un nombre >
12;
La valeur saisie est : 12
Lorsqu’on a affaire à une fonction interactive, la principale difficulté réside dans le fait que l’on est obligé de passer par l’interpréteur pour l’utiliser58 . Chaque réponse saisie par l’utilisateur vient s’inscrire dans un nouveau crochet d’exécution (voir la partie 1.4.3, page 13) et l’on ne voit pas les questions et les réponses s’afficher consécutivement dans le même crochet d’exécution.
57
Le lecteur attentif aura remarqué que la chaîne de caractère se termine par le symbole “\n” désignant le caractère de fin de ligne. Il est prudent de faire figurer ce caractère systématiquement car, selon les versions de Maple, l’exécution de printf n’est suivie d’un effet que lorsque le tampon est plein. Le recours à “\n” conduit Maple à vider le tampon, donc à afficher la chaîne de caractères considérée, avant de passer à la ligne. 58 Lorsqu’on a compris le fonctionnement de Maple dans ces circonstances, cette difficulté est essentiellement d’ordre esthétique.
Chapitre 5
Structure d’une expression Maple 5.1
Préambule
Dans ce chapitre, nous décrivons la représentation interne d’une expression Maple. En prélude à cette étude, nous revenons sur la notion de type d’une expression (partie 5.2) et complétons ainsi l’information donnée au chapitre 2. Nous présentons ensuite, dans la partie 5.3, le principe récursif de la structure arborescente qui représente une expression Maple, puis nous détaillons cette structure pour la plupart des types courants. Cela nous conduit à la notion clé d’opérande d’une expression et aux commandes op et nops qui sont des outils essentiels pour connaître la nature de la structure sous-jacente à une expression. La connaissance de la représentation interne d’une expression permet de maîtriser les outils que Maple met à la disposition de l’utilisateur pour effectuer des transformations d’expression (substitution de sous-expressions, conversion de types) ; elle permet aussi une utilisation particulièrement efficace de la commande map ou des boucle for (partie 5.4).
5.2
Type d’une expression
Toute variable instanciée et toute expression Maple ont un type. Nous avons vu au chapitre 2 que Maple connaît un grand nombre de types et que ces types sont organisés de manière hiérarchique, certains types englobant une famille de types considérés comme des cas particuliers de ce type.
162
Maple : règles et fonctions essentielles
La commande whattype permet de connaître le type de base1 auquel se rattache l’argument qui lui est passé. La commande type permet d’obtenir des renseignements plus précis. L’instruction type(e,t) teste si l’expression e est reconnue par le type t. C’est ainsi que l’on peut constater que le nombre 1 est rattaché au type de base integer (entier relatif) mais que, le cas échéant, il peut aussi relever du type nonnegint (entier positif) ou du type posint (entier strictement positif) : > whattype(1); integer > type(1,integer); true > type(1,nonnegint); true > type(1,posint); true De même, le type de base du nombre 3.4 est float (nombre flottant) mais il peut aussi s’accorder au type numeric (valeur numérique) ou au type positive (valeur numérique positive). > whattype(3.4); float >
type(3.4,float); true
>
type(3.4,numeric); true
>
type(3.4,positive); true
Intéressons-nous encore au nombre 1/6, dont le type de base est fraction mais qui peut être vu comme un nombre rationnel (rational), une valeur numérique ou une valeur numérique positive : 1
La liste des types de base peut être consultée sur la page d’aide en ligne de la commande whattype. Par exemple, integer est un type de base qui englobe les types nonnegint, nonposint, posint et negint.
5. Structure d’une expression Maple
163
>
whattype(1/6); fraction
>
type(1/6,fraction);
>
type(1/6,rational);
true true >
type(1/6,numeric);
>
type(1/6,positive);
true true Considérons la liste [1,2,3]. Son type de base est évidemment list (liste). Cependant, on peut affiner cette analyse et demander à Maple de reconnaître cette liste comme un type structuré (voir la partie 2.2, page 36), en l’occurrence une liste d’entiers : >
whattype([1,2,3]); list
>
type([1,2,3],list);
>
true type([1,2,3],list(integer)); true
Il est intéressant d’observer la réponse proposée par whattype lorsqu’on lui soumet une expression algébrique : >
whattype(1+x+x^2); +
>
type(1+x+x^2,‘+‘);
>
whattype(2*x*exp(t));
>
type(2*x*exp(t),‘*‘);
true ∗
164
Maple : règles et fonctions essentielles true
>
whattype(cos(t));
>
function type(cos(t),function); true
On constate que Maple dispose dans sa panoplie des types + et * pour qualifier les expressions algébriques qui sont des sommes ou des produits de plusieurs termes. On remarque aussi, dans le dernier exemple, l’apparition du type function, qui caractérise un appel instancié2 .
5.3
Représentation arborescente d’une expression Maple
De manière interne, Maple représente toute expression par un arbre étiqueté. Les fils de la racine sont les opérandes de l’expression. Le plus souvent, l’étiquette de la racine de l’arbre indique le type ou le constructeur de l’expression considérée. La structure récursive de l’arbre sous-jacent à une expression peut être explorée à l’aide des commandes nops et op. L’instruction nops(e) fournit le nombre d’opérandes de l’expression e : >
expr:=x+2*y+sin(t); expr := x + 2 y + sin(t)
>
nops(expr); 3
L’instruction op(k,e) extrait le k-ième opérande de l’expression e : >
op(1,expr); x
2 Rappelons que Maple distingue le type procédure (procedure) du type fonction (function) : le type procédure caractérise les fonctions écrites par l’utilisateur ou les commandes Maple. Le type fonction caractérise un appel de fonction ou de commande instancié comme f(x) (voir la partie 2.9.3, page 90 et la partie 5.3.2, page 184).
5. Structure d’une expression Maple
165
On peut également obtenir tous les opérandes d’une expression en une seule instruction, à l’aide de la commande op : >
op(expr); x, 2 y, sin(t)
Enfin, dans la majorité des cas, op(0,e) fournit le type ou le constructeur de l’expression e. Lorsque e est une fonction, op(0,e) indique le nom de la fonction. Pour les autres cas, le lecteur est invité à se reporter à l’aide en ligne. >
op(0,expr); +
On peut déduire des résultats obtenus que l’arbre représentant l’expression x + 2y + sin(t) présente trois opérandes : x, 2y et sin(t). La racine de l’arbre porte l’étiquette +. Nous proposons la fonction arbre, écrite en Maple3 , qui permet d’obtenir la représentation graphique de la structure arborescente sous-jacente à une expression Maple4 . La représentation obtenue est souvent plus parlante que les résultats de l’application répétée de la commande op. Elle est très instructive dans le cas de certaines expressions algébriques ; elle permet de comprendre en quoi certaines manipulations visant à transformer des expressions réussissent ou échouent. 3 La fonction arbre est une version modifiée de la fonction tree présentée dans [4]. Nous remercions les auteurs, Ph. Fortin et R. Pomès, de nous avoir aimablement autorisé à reprendre le canevas de la fonction tree. 4 La version présentée ici de la fonction arbre ne permet pas de traiter certains cas particuliers. Au fil des nouvelles versions du logiciel, de nouveaux types sont apparus (par exemple, Array ou Matrix), d’autres comme le type fonction (procedure) ont été dotés de nouvelles caractéristiques (donc de nouveaux opérandes). Ainsi, la diversité des formes d’arbres s’est accrue. Il faudrait répertorier tous les cas particuliers possibles et traiter séparément ceux qui ne se prêtent pas au traitement général de parcours de l’arbre. Le lecteur pourra remarquer que certains types particulièrement importants font déjà l’objet d’un traitement particulier.
166
Maple : règles et fonctions essentielles
>
arbre:=proc(expr) local plot1, plot2; global affx, affy, affT, affS, marque; # marque:=proc(expr) local xa, xb, pos, lpos; global affx, affy, affT, affS; if type(expr,string) or type(expr,integer) or type(expr,symbol) then affT:=affT,[affx,affy,expr]; xa:=affx; affx:=affx+1; xa else affy:=affy-1; lpos:=map(marque,[op(expr)]); xa:=op(1,lpos); xb:=op(nops(lpos),lpos); affy:=affy+1; pos:=(xa+xb)/2; affT:=affT,[pos,affy,op(0,expr)]; affS:=affS,op(map(proc(a,b) CURVES([[a,affy-1],[b,affy]]) end,lpos,pos)); pos; end if; end proc; # affx:=0; affy:=0; affT:=NULL; affS:=NULL; if type(expr,string) or type(expr,integer) or type(expr,symbol) then affT:=[0,0,op(0,expr)],[0,-1,op(1,expr)]; affS:=CURVES([[0,0],[0,-1]]); else marque(expr); end if; plot1:=plots[textplot]({affT},font=[TIMES,BOLD,10]); plot2:=PLOT(affS); unassign(’affx’,’affy’,’affT’,’affS’,’marque’); plots[display]({plot1,plot2},axes=NONE); end proc:
Deux moyens sont désormais à notre disposition pour explorer la structure arborescente sous-jacente à une expression : la commande op et la fonction arbre.
5. Structure d’une expression Maple
167
Voici ce que donne l’application de la fonction arbre à l’expression x + 2y + sin(t) dont nous venons d’explorer la structure arborescente à l’aide de la commande op : >
arbre(x+2*y+cos(t));
+ x
cos
* 2
y
t
La racine porte l’étiquette +. La structure arborescente sous-jacente à une expression obéit à une construction récursive ; les trois fils de la racine sont donc des arbres qui représentent respectivement les expressions x, 2y et cos(t). L’addition étant commutative et associative, l’ordre d’apparition des fils (les opérandes de l’expression) dans l’arbre ne dépend que de la session. Maple sait reconnaître que deux arbres de racine + sont identiques lorsque les ensembles de leurs opérandes respectifs sont identiques même si les opérandes n’apparaissent pas dans le même ordre. À l’aide de la fonction arbre et de la commande op, nous examinons maintenant la structure arborescente associée à certaines expressions algébriques particulières. Nous passerons ensuite en revue les structures associées à une expression pour la plupart des types que nous avons présentés au chapitre 2.
5.3.1
Expressions algébriques
Il est important de connaître la structure associée à certaines expressions algébriques particulières car elle explique le comportement de Maple lorsqu’on travaille sur des transformations d’expressions, notamment avec la commande subs (voir la partie 5.4.1, page 186).
168
Maple : règles et fonctions essentielles
Somme et différence de termes Voici comment est représentée la différence de deux termes : >
arbre(a-b);
+ a
* –1
b
On constate que la forme de l’arbre associé à a − b n’est pas aussi naturelle qu’on aurait pu le penser, et qu’elle se distingue fortement de l’arbre associé à l’expression a + b : >
arbre(a+b);
+
a
b
5. Structure d’une expression Maple
169
C’est en fait la structure de l’arbre représentant −b qui peut surprendre au premier abord : >
arbre(-b);
*
b
–1
Produit et quotient de termes Voici de même l’arbre qui représente l’expression a/b : >
arbre(a/b);
* a
^ b
–1
Comme précédemment, la forme de cet arbre, a priori surprenante, se comprend si on la rapproche de celle de l’arbre représentant l’expression a.b :
170
Maple : règles et fonctions essentielles
>
arbre(a*b);
*
a
b
et de celle représentant 1/b : >
arbre(1/b);
^
b
–1
L’inverse d’un nombre est représenté comme sa puissance (−1)-ième. Cette particularité explique le comportement de Maple lors de certaines opérations de transformations d’expressions.
5. Structure d’une expression Maple
171
Expression comportant un exposant Voici la représentation d’une expression qui comporte un exposant : >
arbre(a^b);
^
a
b
Bien évidemment, les deux opérandes ne sont pas permutables. Radical L’arbre qui représente un radical se déduit partiellement de l’arbre d’une expression comportant un exposant : >
arbre(sqrt(2));
^ 2
Fraction 1
2
Les deux opérandes ne sont pas permutables.
172
5.3.2
Maple : règles et fonctions essentielles
Représentation interne des types fondamentaux
Nous passons maintenant en revue la structure arborescente associée aux types Maple les plus courants (essentiellement ceux présentés au chapitre 2). Variable libre Voici l’arbre associé à une variable libre (c’est-à-dire n’ayant pas de contenu défini jusqu’alors) : >
arbre(a);
symbol
a L’arbre comporte un seul opérande qui est le nom de la variable considérée, et la racine porte l’étiquette symbol. Constantes L’arbre associé à une constante présente une forme analogue à celui d’une variable libre, comme on peut l’observer sur les deux exemples qui suivent :
5. Structure d’une expression Maple
173
>
arbre(Pi);
symbol
Pi >
arbre(false);
symbol
false On remarque l’étiquette symbol que porte la racine de l’arbre dans ces cas.
174
Maple : règles et fonctions essentielles
Nombre entier relatif L’arbre qui représente un nombre entier relatif est très simple : >
arbre(3);
Integer
3 >
arbre(-2);
Integer
–2 La forme de l’arbre est la même pour un nombre positif et pour un nombre négatif.
5. Structure d’une expression Maple
175
Nombre rationnel (fraction) Voici comment est représenté un nombre rationnel qui ne se réduit pas à un entier (et qui relève donc du type fraction) : > arbre(-2/3);
Fraction
–2
3
Bien évidemment, l’ordre des opérandes est imposé. Nombre complexe Un nombre complexe est représenté par la structure suivante : > arbre(1+3*I);
Complex
1
3
Là encore, on comprend que l’ordre des opérandes est important, le fils gauche (respectivement droit) représentant la partie réelle (respectivement imaginaire) du nombre complexe considéré.
176
Maple : règles et fonctions essentielles
Nombre à virgule flottante La représentation arborescente d’un nombre flottant fait apparaître la mantisse (premier opérande) et l’exposant (deuxième opérande) du nombre considéré : > arbre(1.732);
Float
1732
–3
Les signes respectifs de la mantisse et de l’exposant sont directement portés par la mantisse et l’exposant. On comprend que les deux opérandes ne sont pas permutables. Chaîne de caractères La structure de l’arbre représentant une chaîne de caractères n’est pas surprenante : > arbre("Bonjour");
string
Bonjour
5. Structure d’une expression Maple
177
Liste Voici comment Maple représente une liste : >
arbre([1,2,3]);
list
1
2
3
On constate qu’il y a autant d’opérandes qu’il y a d’éléments dans la liste. L’ordre des opérandes n’est pas indifférent puisque l’ordre des éléments dans une liste est contractuel. On comprend maintenant pourquoi la commande nops fournit le nombre d’éléments d’une liste. Ensemble Les ensembles et les listes ont des propriétés communes, on s’attend donc à ce que l’arbre qui représente un ensemble ressemble à celui qui représente une liste : >
arbre({1,2,3});
178
Maple : règles et fonctions essentielles
set
1
2
3
Toutefois, l’ordre des éléments d’un ensemble étant indifférent, l’ordre des opérandes de l’arbre représentant un ensemble est lui aussi indifférent, comme on le vérifie sur l’exemple suivant : >
arbre({2,1,3});
set
1
2
3
Bien qu’on ait fourni les éléments dans un ordre différent, Maple a reconnu qu’on lui demandait de traiter le même ensemble que précédemment. Maple a donc représenté l’arbre associé à l’exemple précédent, car l’ensemble {1,2,3} est représenté une fois pour toutes pour la session courante. L’ordre d’apparition des opérandes (donc des éléments dans l’ensemble) ne dépend que de la session. Comme dans le cas d’une liste, la structure arborescente associée à un ensemble explique que nops fournisse le nombre d’éléments d’un ensemble.
5. Structure d’une expression Maple
179
Intervalle Voici sans surprise la structure qui représente une expression de type intervalle : > arbre(1..5);
..
5
1
On remarque l’étiquette “..” portée par la racine de l’arbre. Les opérandes ne sont pas permutables. Équation De manière analogue, voici la structure représentant une expression de type équation : > arbre(2*x+y=0);
= + y
* 2
0
x
180
Maple : règles et fonctions essentielles
La racine porte l’étiquette “=”. On reconnaît la structure de l’arbre représentant l’expression algébrique 2x + y dans le premier opérande. Les opérandes ne sont pas permutables. Table Nous abordons à présent la représentation de types plus élaborés. Commençons, comme il est naturel de le faire, en suivant la hiérarchie des types de Maple, c’est-à-dire par les tables. Reprenons l’exemple de la partie 2.8.1 (page 75) : > Trad["bleu"]:="blue"; Trad["rouge"]:="red"; Trad["vert"]:="green"; Trad “bleu” := “blue” Trad “rouge” := “red” Trad “vert” := “green” Rappelons qu’une table est un type agrégé et qu’une expression de ce type s’évalue donc au dernier nom (voir la partie 3.5.3, page 100). Il convient donc de recourir à eval si l’on souhaite obtenir des renseignements sur la structure arborescente qui décrit une table : >
arbre(eval(Trad));
table
list
=
rouge >
=
red
op(eval(Trad));
vert
=
green
bleu
blue
5. Structure d’une expression Maple
>
181
[“rouge” = “red”, “bleu” = “blue”, “vert” = “green”] op(Trad); table([“rouge” = “red”, “bleu” = “blue”, “vert” = “green”])
L’arbre présente une racine étiquetée table qui n’a qu’un seul opérande. Cet opérande représente une liste d’éléments qui sont de type équation (equation) et qui décrivent les correspondances entre indices et valeurs. Tableaux L’ancien type array est à peine plus compliqué que le type table dont il dérive. Il présente un opérande supplémentaire qui définit les dimensions du tableau. C’est un type agrégé et il faut donc recourir à eval pour obtenir les opérandes d’une expression de type array : >
T:=array(1..4);
>
T := array(1..4, []) for i from 1 to 4 do T[i]:=i^2 end do: arbre(eval(T));
>
array
..
1
list
=
4
1
=
1
2
=
4
3
=
9
4
16
La structure associée au nouveau type Array est plus dense et il devient difficile de reproduire sa représentation dans cet ouvrage. Nous nous
182
Maple : règles et fonctions essentielles
contentons de montrer les différents opérateurs à l’aide de la commande op : >
U:=Array(1..4);
> >
U := [0, 0, 0, 0] for i from 1 to 4 do U[i]:=i^2 end do: op(0,U);
>
op(U);
Array 1..4, {(2) = 4, (3) = 9, (1) = 1, (4) = 16}, datatype = anything, storage = rectangular , order = Fortran_order Le premier opérande est inchangé par rapport à l’ancien type array, le deuxième n’est plus de type liste mais de type ensemble5 ; son contenu est inchangé et présente toujours les correspondances entre indices et entrées. Les opérandes suivants définissent des options (à propos desquelles le lecteur pourra trouver des détails dans l’aide en ligne). Matrices L’ancien type matrix dérive directement du type array comme on le vérifie sur la représentation suivante. Comme matrix est un type agrégé, on passe par la commande evalm pour obtenir la représentation arborescente d’une expression de ce type : >
> 5
A:=matrix(2,2,(i,j)->i+j);
2 3 A := 3 4 arbre(evalm(A));
Cela paraît raisonnable dans la mesure où il n’y a pas d’ordre à privilégier dans les correspondances entre indices et entrées.
5. Structure d’une expression Maple
183
array
..
1
..
2
1
list
=
2
2 >
2
=
1
4
2
=
3
2
1
=
3
1
1
2
op(0,evalm(A)); array
>
op(evalm(A)); 1..2, 1..2, [(2, 1) = 3, (1, 2) = 3, (1, 1) = 2, (2, 2) = 4]
La structure du nouveau type Matrix comporte davantage d’opérandes qu’une structure de type matrix. Une expression de type Matrix est difficile à représenter clairement. Nous nous contentons ici de montrer les opérandes d’une expression de type Matrix à l’aide de la commande op : >
>
CC:=Matrix(2,2,shape=symmetric);
1 0 CC := 0 0 CC[1,1]:=1;CC[2,1]:=2;CC[2,2]:=3; CC 1, 1 := 1 CC 2, 1 := 2 CC 2, 2 := 3
>
>
CC;
1 2
2 3
op(0,C); Matrix
>
op(C);
184
Maple : règles et fonctions essentielles
2, 2, {(1, 1) = 1, (1, 2) = 2, (2, 1) = 3, (2, 2) = 4}, datatype = anything, storage = rectangular , order = Fortran_order , shape = [] Les trois premiers opérandes sont semblables à ceux d’une expression de l’ancien type matrix (comme dans le cas des tableaux, les correspondances entre indices et entrées ne sont plus indiquées sous forme de liste mais sous forme d’ensemble). Les opérandes qui suivent servent à décrire des options ou des particularités de la matrice en question (se reporter à l’aide en ligne pour davantage de précisions sur ces options). Fonction Une fonction Maple (type procedure6 ) est une expression au même titre que les expressions d’un autre type. En tant que telle, une fonction admet des opérandes auxquels on peut accéder avec le commande op. Plus précisément, une fonction admet huit opérandes7 . L’opérande 1 est constitué de la séquence des paramètres. L’opérande 2 est constituée de la séquence des variables locales. L’opérande 3 est constitué de la séquence des options, l’opérande 4 est constitué de la table de remember 8 , l’opérande 5 est constitué de la séquence de description9 , l’opérande 6 est constitué de la séquence des variables globales, l’opérande 7 est constitué de la table lexicale (voir l’aide en ligne) et l’opérande 8 est constitué par le type de la valeur de retour. Chacun de ces opérandes prend la valeur NULL (séquence vide), donc n’apparaît pas, lorsque son contenu est vide. Voici un exemple de fonction permettant de connaître les différents opérandes associés à une expression de type procedure : 6 Rappelons que Maple distingue le type procédure (procedure) du type fonction (function) : le type procédure caractérise les fonctions écrites par l’utilisateur ou les commandes Maple. Le type fonction caractérise un appel de fonction ou de commande instancié comme f(x). 7 Le nombre d’opérandes a évolué avec la syntaxe permettant de décrire les fonctions. En particulier, dans les anciennes versions de Maple il n’était pas possible de spécifier le type de la valeur retournée par la fonction. De même, les options n’étaient pas aussi nombreuses. 8 Voir la partie 4.8, page 139. 9 Le champ description d’une fonction Maple est facultatif, au même titre que le champ option. Lorsqu’il est présent, il contient une chaîne de caractères décrivant la fonction. Il permet d’informer l’utilisateur puisqu’il est affiché en même temps que le corps de la fonction. Il est aussi affiché lorsque verboseproc a une valeur faible (mode non verbeux de l’affichage des corps de fonctions, voir la partie 4.11.1, page 151).
5. Structure d’une expression Maple
185
>
> >
essai:=proc(x::float)::float; description "un exemple de fonction"; option remember; local i; global t; x^2+1; end proc; essai := proc(x : :float) : :float; local i; global t; option remember ; description “un exemple de fonction”; x2 + 1 end proc essai(3.5); 13.25 for i from 0 to nops(eval(essai)) do print(i,op(i,eval(essai))) end do; 0, procedure 1, x : :float 2, i 3, remember 4, table([3.5 = 13.25]) 5, “un exemple de fonction” 6, t 7 8, float
Le corps de la fonction essai n’apparaît pas parmi les opérandes. On l’obtient comme premier opérande de essai (et non pas de eval(essai)). On remarque au passage que essai n’a qu’un seul opérande et que la racine de l’arbre qui le représente est étiqueté par symbol : >
for i from 0 to nops(essai) do print(i,op(i,essai)); end do; 0, symbol
186
Maple : règles et fonctions essentielles
1, proc(x : :float ) : :float; local i; global t; option remember ; description “un exemple de fonction”; x2 + 1 end proc
5.4
Transformation d’expressions
Nous allons maintenant présenter différents procédés permettant de transformer des expressions. Toutes les manipulations évoquées s’appuient sur la connaissance et la transformation de la structure arborescente sousjacente à l’expression considérée. Il est fréquent qu’un utilisateur ne parvienne pas à obtenir la transformation qu’il souhaite opérer ou qu’il soit surpris par le résultat obtenu. Cela résulte simplement de l’ignorance de la structure de l’arbre sous-jacent à l’expression manipulée.
5.4.1
Substitution
Maple propose deux commandes pour effectuer des substitutions dans une expression : subs et subsop. La commande subsop est celle dont l’abord est le plus simple. L’instruction subsop(k=a,e) remplace le kième opérande de l’expression e par l’expression a. Bien évidemment, il convient de n’utiliser cette commande que lorsque l’ordre des opérandes de l’expression considérée est prévisible. Dans l’exemple qui suit, on considère une expression polynomiale en la variable x : >
p := x^3+2*x^2+x+3; p := x3 + 2 x2 + x + 3
Considérons le deuxième opérande de cette expression : >
op(2,p); 2 x2
et changeons le en y :
5. Structure d’une expression Maple
187
>
subsop(2=y,p); x3 + y + x + 3
Voici comment changer le deuxième terme (en l’occurrence, le deuxième opérande) de p en son opposé : >
subsop(2=-op(2,p),p); x3 − 2 x2 + x + 3
et voici comment annuler le premier terme (en l’occurrence, le premier opérande) de p : >
subsop(1=0,p); 3 + 2 x2 + x
Voici un autre exemple simple d’application de la commande subsop : >
subsop(1=t,x*y*z); tyz
Voici un exemple un peu plus délicat : >
subsop(1=NULL,2=z,3=y,[x,y,z]); [z, y]
Dans la liste initiale, [x,y,z], on a remplacé le deuxième opérande (y) par z, le troisième opérande (z) par y, et, plus subtil, le premier opérande est remplacé par la séquence vide NULL, ce qui, suivant la propriété d’aplatissement des séquences (voir la partie 2.6.1, page 59), conduit à une liste ne contenant plus que deux termes.
188
Maple : règles et fonctions essentielles
Dans certains cas, il est aussi possible d’effectuer une substitution sur l’opérande 0 d’une expression, comme dans l’exemple suivant : >
subsop(0=g,f(x^2)); g(x2 )
La commande subsop présente des options que le lecteur est invité à découvrir en consultant l’aide en ligne. On peut, par exemple, accéder directement aux sous-opérandes d’un opérande de la manière suivante : >
r := f(x,g(x,y,z),x);
>
r := f(x, g(x, y, z), x) subsop([2,1]=w,r); f(x, g(w, y, z), x)
On a remplacé le premier opérande du deuxième opérande par w. La commande subs permet de remplacer une sous-expression par une autre. Plus précisément, l’instruction subs(e1=e2,e conduit, lorsque cela est possible, au remplacement de la sous-expression e1 de e par e2. >
subs(a=x*y,a*b+cos(a)); x y b + cos(x y)
Il convient de bien avoir conscience de la structure sous-jacente aux expressions Maple lorsqu’on utilise subs : >
subs(a=t,a*b*c); tbc
Dans l’exemple qui suit, on ne parvient pas à ses fins :
5. Structure d’une expression Maple
189
>
subs(a*b=t,a*b*c); abc
Maple va rechercher l’arbre sous-jacent à la sous-expression dans l’arbre de l’expression. L’arbre qui représente a est bien présent dans celui qui représente l’expression a*b*c. En revanche, l’arbre qui représente a*b n’y figure pas. On peut avoir confirmation de cette situation en utilisant la commande has qui détermine si une expression est présente dans une autre : >
has(a*b*c,a); true
>
has(a*b*c,a*b); false
Une manière de contourner le problème que l’on rencontre dans le deuxième exemple consiste à modifier astucieusement l’instruction passée à Maple : >
subs(a=t/b,a*b*c); tc
On peut aussi recourir à la commande algsubs, généralisation de la commande subs, qui ne traite que des substitutions syntaxiques (par opposition à des substitutions d’opérandes entiers), et qui est particulièrement adaptée au traitement de substitutions d’expressions algébriques10 : >
algsubs(a=t,a*b*c); tbc
Pour terminer, signalons la commande applyop qui permet de manipuler des opérandes choisis d’une expression. L’instruction applyop(f,k,e) 10
Dans le cas particulier de cet exemple, on aurait aussi pu traiter la question à l’aide de la commande simplify, par exemple avec l’instruction simplify(a*b*c,a*b=t).
190
Maple : règles et fonctions essentielles
conduit le système à appliquer la fonction f passée en premier argument aux opérandes de l’expression e spécifiés par k. En particulier11 , lorsque k est un entier, f est appliquée au k-ième opérande de e : >
applyop(x->x^3,2,a+b+c); a + b3 + c
5.4.2
Conversion de type
Parmi de nombreuses autres fonctionnalités (se reporter à l’aide en ligne), la commande convert permet de modifier le type d’une expression : >
expr1:=a*b*c; expr1 := a b c
>
whattype(expr1);
>
∗ expr2:=convert(expr1,‘+‘); expr2 := a + b + c
>
whattype(expr2); +
5.4.3
Utilisation de la commande map
Nous avons vu précédemment (voir la partie 2.6.3, page 66) comment la commande map s’applique aux listes. En fait, cette façon d’utiliser map, qui est certes la plus naturelle, n’est que le cas particulier d’une situation plus générale. En effet, la commande map peut s’utiliser avec une expression de n’importe quel type : l’instruction map(f,e) applique la fonction f aux opérandes de l’expression e : >
map(x->x^3,[a,b,c]); [a3 , b3 , c3 ]
11
Pour le cas où k n’est pas un entier, se reporter à l’aide en ligne.
5. Structure d’une expression Maple >
191
map(x->x^3,a*b*c); a3 b3 c3
5.4.4
Utilisation de la structure for
On a présenté les boucles for au moment de passer en revue les différentes structures de contrôle disponibles pour organiser un programme (voir la partie 4.2.2, page 120). Il en existe une variante, extrêmement commode, dans laquelle le compteur de boucle, au lieu de parcourir un intervalle entier, parcourt les opérandes d’une expression : >
for i in [a,b,c] do i^2 end do;
>
a2 b2 c2 for i in a*b*c do i^2 end do; a2 b2 c2
Ainsi, connaissant la structure arborescente sous-jacente à une expression, on peut utiliser une boucle for pour la traiter efficacement, même lorsque cette expression est particulièrement complexe.
Bibliographie [1] R. M. Corless : Essential Maple 7. An Introduction for Scientific Programmers. Springer, 2002. [2] J.-M. Cornil et Ph. Testud : Maple V Release 4. Introduction raisonnée à l’usage de l’étudiant, de l’ingénieur et du chercheur. Springer, 1997. [3] Ph. Dumas et X. Gourdon : Maple. Son bon usage en mathématiques. Springer, 1997. [4] Ph. Fortin et R. Pomès : Premiers pas en Maple. Introduction à l’utilisation du calcul formel en mathématiques, physique et chimie. Vuibert, 1994. [5] K. M. Heal, M. L. Hansen et K. M. Rickard : Découvrir Maple V. Traduit de l’anglais par N. Puech. Springer, 1998. [6] E. Kamerich : A Guide to Maple. Springer, 1999. [7] G. Le Bris : Maple Sugar. Une initiation progressive à Maple. Cassini, 1999. [8] M. B. Monagan, K. O. Geddes, K. M. Heal, G. Labahn et S. M. Vorkoetter : Programmer avec Maple V. Traduit de l’anglais par N. Puech. Springer, 1997. [9] M. B. Monagan, K. O. Geddes, K. M. Heal, G. Labahn, S. M. Vorkoetter, J. McCarron et P. DeMarco : Maple 12. Advanced Programming Guide. Maplesoft, 2007. [10] M. B. Monagan, K. O. Geddes, K. M. Heal, G. Labahn, S. M. Vorkoetter, J. McCarron et P. DeMarco : Maple 12. Introductory Programming Guide. Maplesoft, 2007. [11] M. B. Monagan, K. O. Geddes, K. M. Heal, G. Labahn, S. M. Vorkoetter, J. McCarron et P. DeMarco : Maple 12. User Manual. Maplesoft, 2007.
], 59 ‘, 92
Index ’, 92 *, 4, 32, 43, 84, 164 +, 4, 43, 58, 164, 165, 167 -, 4, 43 ->, 69, 123 ., 43, 71, 84, 86 .., 89, 179 /, 4, 43 :, 12, 13, 16, 25, 27, 29 ::, 129–131 :=, 17, 18, 88, 91, 108, 109 ;, 12, 13, 16, 25, 27, 29, 125–127, 130 <, 42 <=, 42 <>, 42 =, 16, 18, 42, 61, 88, 180 >, 42 >=, 42 ?, 16, 25, 26 [, 59 #, 15, 16, 128 $, 16, 60, 61, 89 %, 16, 22, 92 %%, 22 %%%, 22 &, 16, 84 &*, 43, 84, 86 &ˆ, 46 ˆ, 43 _, 16 _params, 62, 138 \, 14
abs, 51, 117 accent grave, 92 accolade, 63 Ada, 44 add, 89 addition, 43 Adjoint, 74 adjoint, 74 affectation, 17, 18, 42, 80 affichage, 151 aide en ligne, 25 dictionnaire des mathématiques, 26 opérateur ?, 25 recherche directe, 25 recherche par sujet, 25 recherche plein texte, 25 algsubs, 189 anames, 110 and, 41 anything, 36, 148 aplatissement des séquences, 60, 105 apostrophe, 92, 93 APPEND, 154, 158 applyop, 189 arbre (représentant une expression Maple), 164 args, 136, 137 argument, 51 d’une fonction, 123, 135, 147 effectif, 135, 137, 138 passage par valeur, 147 passage par variable, 147 transmis, 137, 138 argument, 51 arithmétique, 44 des polynômes, 46 Array, 74, 76, 80–82, 87, 98, 165, 181 array, 74, 77, 80, 82, 87, 98, 181, 182 arrow, 69, 123
196
Maple : règles et fonctions essentielles
assertlevel, 131 assign, 108, 109 assigned, 107, 109, 110, 112 assume, 52, 110 batch, 11 bibliothèque (voir aussi package), 26 BINARY, 154 blocage de l’évaluation, 92 bool, 36 booléen, 41 boucle, 120, 121 for, 60 for, 120 while, 121 branchement conditionnel, 116 break, 121, 125 by, 120 C (langage), 9, 10, 35, 44, 68, 118, 122, 125, 136, 147, 154, 155 calcul formel, 3, 4 numérique, 5 symbolique, 4 calculateur graphique, 11 Caml, 35, 44, 63, 66, 68, 139, 151 caractère de fin de ligne, 154 cat, 71, 73, 89 Catalan (constante de), 38 catch, 125 ceil, 55 chaîne (lecture formatée de), 159 de caractères, 70 CharacteristicPolynomial, 74 charpoly, 74 CHI, 21 close, 154 combinat, 47 commande, 9 active, 21 built-in, 26, 153
inerte, 5, 21, 38, 89 commentaire, 15, 128 complex, 50 complexe (nombre), 50 concaténation (opérateur de), 72 de chaînes, 71 congruence, 46 conjonction, 41 conjugate, 51 conjugué, 51 connecteur logique, 41 constant, 33, 36, 56 constante d’Euler, 20, 38 de Catalan, 38 γ, 38 I, 39 ∞, 39 physique, 41 π, 37 constants, 37, 40 convert, 51, 54, 190 copy, 79, 82, 85, 87 corps (d’une fonction), 122 corps finis, 46 cos, 16 crochet, 59, 66 d’exécution, 12, 13 décimal (nombre), 52 décomposition en facteurs premiers, 44 default, 154, 155 denom, 47 dénominateur, 47 dérivation, 61 dérivée, 6 description, 152, 184 Diff, 5, 21 diff, 5, 21, 61, 69, 70, 107, 108 différence non symétrique, 65 Digits, 54 disjonction, 41
Index disjonction exclusive, 41 dito (opérateur), 22, 23 dito (opérateur), 92 diviseur, 45 division, 43 euclidienne, 45 do, 120, 121 dsolve, 88 e, 31, 39, 40 Eigenvalues, 74 eigenvalues, 74 Eigenvectors, 74 eigenvectors, 74 élément d’un ensemble, 63–65, 70 d’une liste, 66, 67 elif, 118 else, 41, 117–119 end, 122 end do, 120, 121 end if, 117, 119 end proc, 122, 123 ensemble, 63 appartenance, 65 délimiteur, 63 différence non symétrique, 65 élément, 70 intersection, 65 union, 65 vide, 65 entier, 162 (nombre), 43 de Gauss, 50 relatif (nombre), 43 entrées/sorties, 151 entries, 76 Equal, 87 equal, 86 équation, 42, 88 différentielle, 88 membre droit, 88 membre gauche, 88 equation, 30, 88, 181
197
erreur, 130 ERROR, 125 error, 125 et (logique), 41 Euler (constante d’), 20, 38 (fonction gamma d’), 20 eval, 74, 75, 77, 78, 81, 84, 95, 98, 100–104, 112, 113, 180, 181 evala, 114 evalb, 33, 42, 113 evalc, 50, 51, 113 evalf, 19, 33, 42, 48–50, 53, 54, 113, 120 evalhf, 54, 55, 113 evalm, 84, 85, 100, 113, 182 evaln, 107, 112, 113, 150 evalr, 114 évaluation, 17, 91, 93 au dernier nom, 100 complète, 98 d’un objet agrégé, 75 d’un paramètre, 101 d’une variable globale, 101 d’une variable locale, 101 d’une variable non affectée, 92 différée, 92 et affectation, 91 et appel de fonction, 105 et simplification, 93 niveau, 94 règles d’, 97 exception, 130, 131 Expand, 22 expand, 22 exponentiation rapide, 46 exponentielle népérienne, 31, 39, 40 exposant, 171, 176 expression algébrique (type d’une), 163, 167 exprseq, 62 extended_numeric, 57 extension algébrique, 114
198
Maple : règles et fonctions essentielles
Factor, 22 factor, 22 factorial, 44 factorielle, 44 FAIL, 41, 118, 119 false, 33, 41, 118, 119 faux (valeur booléenne), 41 fclose, 154 feof, 154, 159 feuille de travail, 8 classique, 10 enregistrement, 8, 15 exportation, 8 ouverture, 8, 15, 34 standard, 10 fi, 117 fichier, 154 écriture, 156 écriture formatée, 156 binaire, 154 descripteur, 154 fermeture, 154 fin de fichier, 154 fin de ligne, 154 lecture, 155 lecture formatée, 155 nom, 154 non tamponné, 154 ouvert en écriture, 154 ouvert en lecture, 154 ouverture, 154 tamponné, 154 texte, 154 filtrage, 129 float, 36, 56, 162 floor, 55 flot d’entrée, 154 de sortie, 154 flottant, 52 fonction, 9, 122 argument, 147 déclaration, 123
définition, 123 filtrage, 129 gamma d’Euler, 20 informatique, 122 récursive, 139 retour non évalué, 146 variable globale, 132 variable locale, 131 visualiser le corps, 151 fonction, 98 fopen, 154 for, 60, 120, 121, 126, 132, 158, 161, 191 forme algébrique (d’un nombre complexe), 50 forme close, 4 forme trigonométrique (d’un nombre complexe), 51 fprintf, 156, 158, 159 frac, 55 fraction, 48 fraction, 48, 56, 162, 175 fraction continue, 47 from, 120 fscanf, 155, 156, 158, 159 fsolve, 88 function, 90, 164, 184 Γ, 20 γ, 20, 38 GAMMA, 20 Gamma, 20 gamma, 20 Gauss (entier de), 50 GaussInt, 50 Gcd, 22 gcd, 22, 46 globale (variable), 131 grecque (lettre), 18 groupement d’instructions, 13 has, 189 hfloat, 54
Index I, 6, 39, 50 i, 39 if, 41, 117–119 ifactor, 44 igcd, 45 ilcm, 45 Im, 51 imaginaire (nombre), 39, 50 implication, 41 implies, 41 in, 65 inéquation, 42 index,package, 29 indices, 76 infini (∞), 39 infinity, 39, 57 instruction, 13 groupement, 13 Int, 5, 21, 89 int, 5, 21, 89 intégrale, 6 integer, 36, 46, 56, 57, 162 interface, 10 classique, 3, 10 standard, 10 interpréteur, 9, 12, 14 intersect, 65 intersection (ensembliste), 65 inversion modulaire (d’un entier), 46 invite, 12, 13 iquo, 45, 147 irem, 45, 147 is, 33, 118 isprime, 45 itération, 60 conditionnelle, 121 non conditionnelle, 120 ithprime, 45 Jacobi (symbole de), 47 Kronecker (symbole de), 47
199
langage de programmation, 10 lasterror, 125, 130 lcm, 46 Legendre (symbole de), 47 length, 44 lettre grecque, 18 lhs, 88 ligne de commande, 11 Limit, 5, 21, 38 limit, 5, 21, 38 limite, 5 linalg, 74, 82, 83, 86, 100 LinearAlgebra, 27, 28, 74, 86, 87 LinearSolve, 74 linsolve, 74 list, 36, 66, 163 liste, 66, 163 délimiteur, 66 élément, 66 membre, 70 sous-liste, 67 ListTools, 70 locale (variable), 131 logique (à trois états), 41 lprint, 151 mantisse, 176 map, 69, 70, 87, 161, 190 Maplet, 11 matrice, 74, 82 Matrix, 74, 76, 84, 86, 87, 98, 165, 183 matrix, 74, 82–87, 98, 100, 113, 182–184 max, 56, 137 member, 70 membre d’un ensemble, 70 d’une liste, 70 membre (d’une équation), 88 min, 56, 136, 137, 153 minus, 65 mod, 46 module, 51
200
Maple : règles et fonctions essentielles
modulo, 46 mul, 89 multiplication, 43 nargs, 136, 137 négation, 41 negative, 57 negint, 46, 162 niveau d’évaluation, 94 nolist, 76 nom (de variable), 18 nombre à virgule flottante, 52 complexe, 32, 39, 50 argument, 51 conjugué, 51 forme algébrique, 50 forme trigonométrique, 51 module, 51 partie imaginaire, 51 partie réelle, 51 décimal, 52 entier, 56 entier positif, 46 entier relatif, 43, 162 flottant, 56 exposant, 176 mantisse, 176 imaginaire, 32, 39, 50 irrationnel, 49 premier, 45 rationnel, 47, 56, 162 nombres de Stirling, 47 entiers de Gauss, 50 non (logique), 41 nonnegative, 57 nonnegint, 46, 139, 162 nonposint, 46, 162 nonpositive, 57 nops, 63, 66, 69, 147, 161, 164, 177, 178 _noptions, 137 Normal, 22
normal, 22 not, 41, 119 _nparams, 137, 138 _npassed, 136–138 _nrest, 137 NULL, 61, 109, 122, 151, 184, 187 numer, 47 numérateur, 47 numeric, 33, 56, 57, 162 numtheory, 47 od, 120 op, 63, 64, 66, 67, 69, 89, 161, 164– 167, 182–184 open, 154 opérande, 164, 167 opérateur arithmétique, 43 de concaténation, 72 de sélection, 28, 59, 64, 67, 75, 77, 80, 83, 89, 138 dito, 92 dito, 22, 23 relationnel, 42 operator, 123 option, 184 _options, 137 or, 41 ordre et ensemble, 63 et liste, 66 et séquence, 59 ou (logique), 41 Π, 19 π, 19, 37 package, 26, 28, 75 chargement, 26 déchargement, 28 paramètre, 101 paramètre (d’une fonction), 122, 129, 131 _params, 137, 138 partie imaginaire, 51
Index
201
partie réelle, 51 Pascal, 10, 98, 118, 122, 125, 139, 147, 154 passage par valeur, 147 par variable, 147 _passed, 136–138 PI, 19 Pi, 19, 31, 37 pi, 19, 31, 37 plot, 89, 137 plus grand commun diviseur, 45 plus petit commun multiple, 45 polar, 51 polynômes, 46 posint, 46, 139, 162 positive, 57, 162 premiers (décomposition en facteurs), 44 pretty-printer, 9, 10, 18–21, 151 prettyprint, 151 primalité (test de), 45 print, 78, 137, 151, 152 printf, 156, 159, 160 proc, 122, 123 procédure, 122 procedure, 90, 98, 105, 164, 165, 184 Product, 21 product, 21, 89 produit de nombres, 43 matriciel, 43 non commutatif, 43 programmation, 122 programmation (langage de), 10 Prolog, 68 prompt, 12 protect, 16 puissance, 43
récursion, 141 terminale, 145 récursivité, 139 racine carrée, 49 cubique, 6 n-ième, 49 radical, 49, 171 radical, 36, 49 rand, 124 randmatrix, 85 range, 89 Rank, 28 rational, 48, 54, 56, 162 rationnel (nombre), 47 RAW, 154 Re, 51 READ, 154 readlib, 26 readline, 155, 157, 159 realcons, 50 règles d’évaluation, 92, 97, 98, 100, 101, 105, 135 réinitialisation, 23 rem, 46 remember, 142, 144, 145 remove, 68, 69 reserved, 16 _rest, 137 restart, 23, 24, 34, 76, 94, 156 reste (d’une division euclidienne), 45 retour non évalué, 146 RETURN, 124, 125 return, 124, 125 rhs, 88 root, 49 round, 55 rtable, 74, 76 rtablesize, 82
quo, 46 quotient
scanf, 155, 156, 159 ScientificConstants, 41 script, 11
(d’une division dienne), 45
eucli-
202
Maple : règles et fonctions essentielles
SearchText, 73 searchtext, 73 select, 68, 69 sélection (opérateur de), 28, 59, 64, 67, 75, 77, 80, 83, 89, 138 seq, 45, 60, 89, 138 séquence, 59, 60, 63, 66 aplatissement, 60, 105 des arguments effectifs, 105, 135 des arguments transmis, 105, 135 textttexprseq, 62 sous-jacente, 63, 66 vide, 61 set, 36, 63 simplification, 93 simplify, 189 sin, 153 solve, 88, 89 soustraction, 43 sprintf, 159 sqrt, 39, 49 sscanf, 159, 160 Stirling (nombres de), 47 STREAM, 154 string, 36, 70, 129 StringTools, 73 structure de contrôle, 116 subs, 113, 167, 186, 188, 189 subsop, 186–188 substring, 73 suite, 59 Sum, 21, 38, 89 sum, 21, 38, 89 surcharge, 43, 58 symbol, 172, 173, 185 symbole de Jacobi, 47 de Kronecker, 47 de Legendre, 47 système d’équations, 88
différentiel, 88 table, 74, 75 de remember, 142, 145, 153 de vérité (logique), 41 table, 74–77, 98, 181 tableau, 74, 77 terminal, 154 TEXT, 154 then, 41, 117, 119 time, 55 to, 120 toplevel, 9, 12, 97, 116 Trace, 74 trace, 74, 83 Transpose, 74 transpose, 74 traperror, 125, 130 true, 33, 41, 118, 119 trunc, 55 try, 125, 130 type, 35, 43 *, 163 +, 163 agrégé, 98, 180, 181 booléen, 41 chaîne, 176 chaîne de caractères, 70 complexe, 50 constante, 172 de base, 162 ensemble, 63, 177 entier, 43, 56 entier relatif, 43, 48, 174 équation, 30, 42, 85, 88, 179 expression algébrique, 163, 167 flottant, 56 fonction, 90, 163, 164, 184 fraction, 48, 56, 175 imbriqué, 36 inéquation, 42 intervalle, 89, 179 liste, 66, 163, 177 matrice, 74, 98, 182
Index nombre complexe, 175 nombre flottant, 176 nombre rationnel, 175 procédure, 90, 164, 184 radical, 49 rationnel, 47, 48, 56 séquence, 61 simple, 98 structuré, 36, 163 surface, 36 table, 74, 75, 98, 180 tableau, 74, 98, 181 valeur numérique, 56 type, 35, 36, 61, 162 type[definition], 36 type[structured], 36 type[surface], 36 unassign, 111 undefined, 57 union, 65 union (ensembliste), 65 unprotect, 20, 41 unwith, 28 valeur absolue, 51 décimale approchée, 48–50 variable, 15, 16, 122 étiquette, 15 d’interface, 12 désaffectée, 93 globale, 101, 131–134 indexée, 94 intrinsèque, 136, 137 locale, 101, 131, 133 vecteur, 70 verboseproc, 151–153, 184 virgule flottante (nombre à), 52 vrai (valeur booléenne), 41 whattype, 62, 162, 163 while, 121, 159 with, 27, 28, 130
worksheet, 8 WRITE, 154, 158 writeline, 156 X, 21 xor, 41 zip, 70
203
Maple 12 Installation, activation et aide en ligne Installation Il est important de suivre les instructions suivantes. Si vous rencontrez des problèmes lors de l’installation de MapleTM 12, reportez-vous aux instructions contenues dans le fichier Install.html sur le CD Maple 12.
Licence Pour utiliser Maple 12, vous avez besoin d’une licence. Les licences délivrées pour les anciennes versions de Maple ne fonctionnent pas. La licence Maple 12 sera automatiquement délivrée lors de l’activation du produit.
Activation Pour utiliser Maple, vous devez activer le produit. Le processus d’activation fournit et installe automatiquement sur votre ordinateur la licence nécessaire à l’utilisation du logiciel. Vous serez invités à activer Maple durant l’installation. Pour activer le produit, vous aurez besoin du code suivant :
FDWY2BD929JAXFFT
206
Maple : règles et fonctions essentielles
Si vous n’activez pas le produit durant son installation, suivez les étapes suivantes : 1. Assurez-vous que vous êtes connectés à Internet. Sans connexion, l’activation est impossible. 2. Lancez le programme Maple 12 (Standard Worksheet Maple 12 et non Classic Worksheet Maple 12). 3. Pour activer le logiciel, cliquez sur Activate. 4. Il vous sera alors demandé d’entrer le code indiqué ci-dessus. Si vous activez le logiciel à travers un serveur proxy, assurez vous que vous avez bien entré cette information avant de cliquer sur Next. 5. Entrez les informations complémentaires qui vous seront demandées. Cliquez sur Next pour terminer l’installation. Un fichier de licence Maple 12 (license.dat) sera alors automatiquement incorporé dans le dossier licence de votre installation Maple 12. Si vous rencontrez des difficultés lors de l’activation, suivez les instructions situées dans le fichier Install.html du CD Maple 12. Pour plus d’informations sur l’activation, consultez la FAQ (Frequently Asked Questions) à l’adresse : www.maplesoft.com/support/faqs/activation
Achevé d’imprimer sur les presses de l’Imprimerie BARNÉOUD B.P. 44 - 53960 BONCHAMP-LÈS-LAVAL Dépôt légal : février 2009 - N° d’imprimeur : 812020 Imprimé en France