Programmation

## 6 Programmation

### 6.1 Le langage

Xcas permet d’écrire des programmes, comme n’importe quel langage de programmation. Voici ses principales caractéristiques.

- C’est un langage fonctionnel. L’argument d’une fonction peut être une autre fonction. Si c’est le cas, on peut soit donner le nom de la fonction argument dans la commande, soit sa définition : par exemple `function_diff(f)` ou bien `function_diff(x->x^2)`.
- Il n’y a pas de distinction entre programme et fonction : une fonction renvoie la valeur de la dernière instruction évaluée ou ce qui suit le mot réservé `return`. Comme pour tous les environnements de calcul, programmer consiste à étendre Xcas en lui rajoutant les fonctions souhaitées. Structurer la programmation consiste à hiérarchiser les différentes fonctions qui s’appellent entre elles.
- Le langage est non typé. On distingue seulement les variables globales, qui ne sont pas déclarées, et les variables locales, déclarées en début de fonction.

Dans un programme, lorsqu’on appelle une variable munie d’un indice qui n’est pas affectée à une liste, séquence ou matrice, c’est une table qui est créée, et non une liste. Une table est un conteneur d’objets analogue aux listes et aux séquences. La différence est qu’elle peut être indicée par autre chose qu’un entier, par exemple une chaîne de caractères… Si `a` est une variable formelle, la commande `a[4]:=2` crée une table `a`.
Pour que `a` soit une liste, il faut d’abord affecter `a` à une liste par exemple `a:=[0$10]` (si la taille de la liste est connue) ou `a:=[]` puis `a[4]:=2`. Même si le langage est non typé, il est donc recommandé d’initialiser les variables avant de les utiliser. La syntaxe de déclaration d’une fonction est la suivante.

    nom_fonction(var1,var2,...):={
    local var_loc1, var_loc2,... ;
      instruction1;
      instruction2;
      ...
    }

La syntaxe est soit avec des mots clef en français soit celle du langage C++.

Instructions en fançais
affectation | `a:=2;`
entrée expression | `saisir("a=",a);`
entrée chaine | `saisir_chaine("a=",a);`
sortie | `afficher("a=",a);`
valeur retournée | `retourne(a);`
arrêt dans boucle | `break;`
alternative | `si <condition> alors <inst> fsi;`
| `si <condition> alors <inst1> sinon <inst2> fsi;`
boucle pour | `pour j de a jusque b faire <inst> fpour;`
| `pour j de a jusque b pas p faire <inst> fpour;`
boucle répéter | `repeter <inst> jusqua <condition>;`
boucle tantque | `tantque <condition> faire <inst> ftantque;`
boucle faire | `faire <inst1> si <condition> break;<inst2>`
| `ffaire;`

Instructions comme en C++
affectation | `a:=2;`
entrée expression | `input("a=",a);`
entrée chaine | `textinput("a=",a);`
sortie | `print("a=",a);`
valeur retournée | `return(a);`
arrêt dans boucle | `break;`
alternative | `if (<condition>) {<inst>};`
| `if (<condition>) {<inst1>} else {<inst2>};`
boucle pour | `for (j:= a;j<=b;j++) {<inst>};`
| `for (j:= a;j<=b;j:=j+p) {<inst>};`
boucle répéter | `repeat <inst> until <condition>;`
boucle tantque | `while (<condition>) {<inst>};`
boucle faire | `do <inst1> if (<condition>) break;<inst2> od;`

Pour les tests, une condition est un booléen, résultat d’une expression logique, utilisant les opérateurs habituels.

Opérateurs logiques
`==` | teste l’égalité | `!=` | teste la différence
`<` | teste la stricte infériorité | `>` | teste la stricte supériorité
`<=` | teste l’infériorité ou l’égalité | `>=` | teste la supériorité ou l’égalité
`&&, et` | opérateur booléen infixé et | `||, ou` | opérateur booléen infixé ou
`vrai` | est le booléen true ou 1 | `faux` | est le booléen false ou 0
`non, !` | inverse logique |  |

Attention, `i` désigne √−1 et ne peut pas être utilisé comme variable de boucle. L’instruction `break;` permet de sortir d’une boucle et `continue;` de passer immédiatement à l’itération suivante. De nombreuses variantes sont reconnues en particulier en mode de compatibilité avec Maple, Mupad et les TI89/Voyage 200. On peut capturer des erreurs d’exécution par

    try {bloc_erreurs_capturees}
    catch (variable)
        {bloc_execute_si_erreur}

Par exemple :

    try{A:=idn(2)*idn(3)}
    catch(erreur)
    {print("l'erreur est "+erreur)}

### 6.2 Quelques exemples

Pour écrire un programme, il est conseillé d’ouvrir un éditeur de programme avec le menu `Prg->Nouveau programme`. Le menu `Prg` de l’éditeur permet d’entrer facilement les structures de programmation. On peut ensuite sauvegarder le texte du programme indépendamment de la session de travail pour l’utiliser ensuite dans une autre session de travail.

Voici un programme qui donne le quotient et le reste de la division euclidienne de 2 entiers en utilisant les fonctions `iquo` qui renvoie le quotient et `irem` qui renvoie le reste (c’est la fonction `iquorem` de Xcas).

idiv2
`idiv2(a,b):={` | `idiv2(a,b):={`
`local q,r;` | `local q,r;`
`if (b!=0) {` | `si b!=0 alors`
`q:=iquo(a,b);` | `q:=iquo(a,b);`
`r:=irem(a,b);}` | `r:=irem(a,b);`
`else {` | `sinon`
`q:=0;` | `q:=0;`
`r:=a;` | `r:=a;`
`}` | `fsi`
`return [q,r];` | `retourne [q,r];`
`}` | `}`

Saisissez cette fonction idiv2 dans un éditeur de programme, testez-la (bouton `OK`) puis sauvegardez par exemple sous le nom `idiv2.cxx`. Vous pouvez utiliser cette fonction dans une ligne de commande, en tapant par exemple `idiv2(25,15)`. Vous pourrez utiliser cette fonction dans une autre session Xcas, en utilisant la commande `read("idiv2.cxx")` ou en l’ouvrant depuis un éditeur de programme (et en le validant par OK).

Voici maintenant deux versions du calcul du PGCD de deux entiers, une version itérative, puis une version récursive.

pgcd_iteratif
`pgcdi(a,b):={` | `pgcdi(a,b):={`
`local r;` | `local r;`
`while (b!=0) {` | `tantque b!=0 faire`
`r:=irem(a,b);` | `r:=irem(a,b);`
`a:=b;` | `a:=b;`
`b:=r;` | `b:=r;`
`}` | `ftantque`
`return a;` | `retourne a;`
`}:;` | `}:;`

pgcd_recursif
`pgcdr(a,b):={` | `pgcdr(a,b):={`
`if (b!=0) return a;` | `si b!=0 alors retourne a;fsi`
`return pgcdr(b,irem(a,b));` | `retourne pgcdr(b,irem(a,b));`
`}:;` | `}:;`

Il arrive parfois qu’un programme ne fonctionne pas du premier coup comme prévu (!) Il est alors possible de l’exécuter en mode pas-à-pas pour le mettre au point, avec la commande `debug`. Pour plus de détails consulter le menu `Aide->Interface`. Par exemple, pour le programme `idiv2`, on lance la mise au point en tapant :
`debug(idiv2(25,15))`
Le débuggueur affiche automatiquement la valeur des paramètres `a,b` puis des variables locales `q,r` lors de l’exécution instruction par instruction avec le bouton `sst`.

### 6.3 Style de programmation

Xcas est interprété et non compilé. Plus que le nombre de lignes du programme, c’est le nombre d’instructions réellement exécutées qui influence le temps de calcul. En règle générale, il est plus rapide de créer des listes ou des séquences que de programmer des boucles. Voici quelques manières de calculer 5000! : comparez leurs temps d’exécution.

    5000!
    product([n$(n=1..5000)])
    product(cumSum([1$5000]))
    f:=1; (f:=f*n)$(n=2..5000):;f
    f:=1; for(n:=1;n<=5000;n++) {f:=f*n}
    f:=1;n:=1; while(n<5000) {n:=n+1; f:=f*n}
    f:=1; (f:=f*n)$(n=2..5000)

La rapidité d’exécution est parfois contradictoire avec la clarté du programme, et on doit accepter des compromis. Dans une utilisation courante, le temps de calcul n’est pas réellement un enjeu : on utilise en général les langages interprétés comme Xcas pour tester des algorithmes et réaliser des maquettes. Les applications en vraie grandeur sont codées dans des langages compilés comme C++ (en utilisant par exemple la librarie `giac` pour les fonctions de calcul formel).