Découvrir Scilab/Programmation
6. Programmation
Scilab est un langage de programmation, il accepte un certain nombre d’instructions autres que mathématiques, permettant l'exécution d'algorithmes.
L'écriture de programmes se fait idéalement avec l'éditeur de texte SciNotes ; celui-ci met en exergue les instructions en couleurs, les parenthésages (correspondance entre les paires de parenthèses et de crochets), et surligne les lignes continuées avec un fond jaune. On peut aussi utiliser un autre éditeur de texte en sauvegardant le fichier avec l'extension codice_1 ou codice_2. Lorsque l'environnement le permet, on peut faire du copier-coller depuis l'éditeur de texte externe vers SciNotes ou bien l'éditeur de ligne de commande.
Différence entre les extensions codice_1 et codice_4.
Les extensions de nom de fichier codice_1 et codice_4 sont toutes deux utilisées pour désigner des fichiers texte contenant des scripts Scilab. D'un point de vue de l'utilisateur, il y a peu de différences fonctionnelles entre les deux. Toutefois, la logique voulue par les concepteurs est la suivante :
Voir les sections suivantes :
Interaction avec l'utilisateur.
Interagir avec l'utilisateur, c'est :
Entrées.
Nous nous contentons dans cette section des entrées au clavier par le biais de la console Scilab. Les interactions plus complexes sont présentées dans le chapitre suivant "Créer une interface graphique GUI".
La fonction codice_11 permet à l'utilisateur de rentrer une valeur. la syntaxe est :
x = input("message")
où "message" est une chaîne de caractères qui s'affiche et "x" est la variable dans laquelle sera mise la valeur (ou la matrice) entrée par l'utilisateur. Si l'utilisateur doit entrer une chaîne de caractères, il faut écrire (deux possibilités) :
x = input("message", "string")
x = input("message", "s")
Lorsqu'une variable "nom_de_variable" existe, alors
editvar nom_de_variable
ouvre une fenêtre permettant de modifier la contenu de cette variable. On peut également définir la valeur d'une variable en affichant une boîte de dialogue
a = x_dialog("message", "a0")
où codice_12 est la valeur initiale de "a". Si l'on veut un message sur plusieurs lignes, on utilise une matrice de chaînes de caractères, par exemple
a = x_dialog(["Entrez un" ; "nombre entier"], "1")
On peut aussi faire cliquer l'utilisateur et récupérer les coordonnées de l'endroit cliqué :
[bouton, x, y] = xclick
la valeur de "bouton" correspond à l'action menée par l'utilisateur.
La fonction codice_13 arrête l'exécution du programme jusqu'à ce que l'utilisateur appuie sur une touche.
Sorties.
Nous avons vu jusqu'ici l'affichage graphique et la génération de fichiers d'image et de son (voir le chapitre précédent "Graphiques et sons"). Nous présentons ici l'affichage dans la console ; d'autres méthodes plus avancées sont présentées dans le chapitre suivant "Créer une interface graphique GUI".
Afficher un message.
La fonction 
print(%io(2),a)
affiche le contenu de la variable "a" à l'écran. On peut aussi utiliser
write(%io(2),a)
La fonction
disp(a)
affiche le contenu de "a" sans faire figurer « codice_14 » devant. On peut afficher un message dans la barre d'information située en bas de la fenêtre graphique courante, avec
xinfo("message")
La fonction codice_15 affiche la chaîne de caractère "message" sous la forme d'un avertissement, c'est-à-dire précédé de « codice_16 ». La fonction codice_17 affiche la chaîne de caractère "message" sous la forme d'un message d'erreur, c'est-à-dire précédé de « codice_18 ».
On peut utiliser la manière codice_19 du langage C pour inclure des variables mises en forme :
a = 1/6
mprintf("La valeur vaut : %.2f", a)
va afficher « La valeur vaut : 0.17 »
Lancer une impression.
Pour lancer une impression, on peut utiliser la fonction codice_20, qui accepte comme paramètre :
la fonction retourne un booléen indiquant si l'impression a réussi ou pas.
Par exemple :
status = toprint("monfichiertexte.txt"); // impression du contenu du fichier
scf(0);
plot2d();
toprint(0); // impression de la fenêtre graphique
toprint(["ligne 1", "ligne 2", "ligne 3"], "en-tête");
Pour les figures, on peut aussi utiliser
scf(0);
plot2d();
printfigure(0); // impression de la fenêtre graphique
qui, en outre, ouvre la boîte de dialogue de configuration de l'impression.
On peut ouvrir cette boîte de configuration pour modifier les paramètres par défaut, avec la commande
printsetupbox
Interaction avec le système d'exploitation.
Il est possible de demander au système d'exploitation (SE) d'effectuer des actions.
Déterminer le SE
La commande
SE = getos()
permet de connaître le système d'exploitation courant.
Sous Microsoft Windows
La commande
winopen("nom_de_fichier")
demande au SE d'ouvrir un fichier.
Pour cela, l'extension de nom de fichier doit être associée à un programme extérieur. On peut connaître ce programme avec la commande codice_23, par exemple
findfileassociation(".txt")
Sous les Unix et Microsoft Windows
La commande codice_24 permet de faire effectuer une commande au SE, que ce soit un unix (dont un Linux, BSD ou MacOS X) ou bien Microsoft Windows. La commande renvoie un entier : si l'exécution est possible, le résultat est le code de l'interpréteur de commande, et si elle est impossible, le résultat est -1.
On peut rediriger le contenu de la sortie standard vers la fenêtre Scilab avec codice_25, ou bien vers une variable avec la commande codice_26, par exemple
if getos() == "Windows" then
 A = unix_g("dir "+SCIHOME);
else
 A = unix_g("ls "+SCIHOME);
end
Structures de contrôle (boucles et branchements conditionnés).
Exécution conditionnelle.
L'exécution conditionnelle se fait de manière classique avec les commandes codice_27
if condition1 then
 instructions
elseif condition2 then
 instructions
else
 instructions
end
Si une variable peut prendre plusieurs valeurs, on peut utiliser la structure codice_28 :
select nom_variable
 case valeur1 then
 instructions
 case valeur2 then
 instructions
 case valeurn then
 instructions
else
 instructions
end
Boucle itérative.
La syntaxe d'une boucle codice_29 est la suivante :
for variable=expression
do
 instructions
end
Le mot clef codice_30 est optionel. Il est possible d'écrire la boucle sur une seule ligne :
for variable=expression, instruction, …, instruction, end
L'codice_31 est un vecteur ou une liste contenant les différentes valeurs qui seront successivement prises par codice_32.
"Exemples :"
for i=1:10 ...
 la variable codice_33 prend successivement les valeurs 1, 2, ..., 10.
for i=list (1, 2, "a") ...
 la variable codice_33 prend successivement les valeurs 1, 2 et "a".
Boucle itérative antéconditionnée.
while condition
 instructions1
[else
 instructions2
end
while condition, instructions1, … [, else instructions2, …], end
Le bloc d'instructions codice_35 est exécuté tant que codice_36 est vraie. Le bloc d'instructions codice_37 n'est exécuté qu'une seule fois dès que codice_36 est fausse.
Interruption d'une boucle.
Lorsque le mot clé codice_39 est rencontré dans une boucle codice_29 ou codice_41, l'exécution de la boucle est interrompue, et l'exécution du programme se poursuit par les instructions suivants immédiatement le mot clé codice_42 terminant la boucle.
Tri, recherche et sélection.
Dans un certain nombre de cas, il faut trier les éléments d'un ensemble, ou bien rechercher des éléments répondant à certaines conditions au sein d'un ensemble. Par « ensemble », nous entendons ici un vecteur ou une matrice.
Tri.
La fonction codice_43 permet de trier ; elle utilise l'algorithme de tri rapide ("quick sort", diviser pour régner). Si V est un vecteur, alors
A = gsort(V)
trie les éléments par ordre décroissant. Pour trier par ordre croissant, il faut ajouter l'option codice_44 "(increasing ordre)" :
codice_45
Si M est une matrice, alors codice_46 trie la matrice en classant les éléments de haut en bas puis de gauche à droite. Si M est une matrice "m"×"n" ("m" lignes, "n" colonnes), alors l'élément M("i", "j") ("i"-ème ligne, "j"-ème colonne) donnera l'indice de classement
Si l'on veut trier les colonnes, c'est-à-dire que les éléments restent dans la même colonne mais changent de ligne "(row)", on utilise l'option codice_47. Et si l'on veut trier les lignes, on utilise l'option codice_48 ("column", les éléments changent de colonne, mais pas de ligne). Par exemple
M = rand(3,3)
A = gsort(M, "r", "i")
On peut aussi vouloir conserver les lignes intègres, ou les colonnes intègres. Pour cela, on utilise le tri « lexicographique » (tri par le premier élément, puis en cas d'égalité par le deuxième éléments, …), avec les options codice_49 ou codice_50 :
A = gsort(M, "lr", "i")
Recherche.
La recherche se fait avec la fonction codice_51.
Par exemple, pour cherche une valeur "a" au sein d'un vecteur V :
I = find(V == a);
la variable I retournée est alors un vecteur contenant les numéros d'indices "i" tels que V("i") = "v". On peut ensuite extraire les valeurs
A = V(I);
voire les supprimer du vecteur initial
V(I) = [];
Pour être un peu plus précis : codice_52 est un vecteur de booléens. On peut fabriquer un vecteur de booléens avec tous types de conditions, par exemple codice_53…
Avec une matrice booléenne M, la commande
I = find(M);
renvoit un vecteur d'indice obtenu en parcourant la matrice par colonnes, de haut en bas, puis de gauche à droite. Comme pour le tri, si M est une matrice "m"×"n" ("m" lignes, "n" colonnes), alors l'élément M("i", "j") ("i"-ème ligne, "j"-ème colonne) donnera l'indice
Si l'on veut des indices « classiques », il faut utiliser la syntaxe
[I, J] = find(M);
alors I("k"), J("k") sont les indices du "k"-ième élément trouvé. La valeur de l'élément est extraite par codice_54.
Dans l'exemple suivant, les deux matrices A et B sont identiques
M = rand(10, 10);
M > 0.5
[I, J] = find(M > 0.5);
for k = 1:size(I, "*")
 A(k) = M(I(k), J(k));
end
L = find(M > 0.5);
B = M(L);
[A, B]
La commande codice_55 travaille uniquement sur des matrices de booléens. Cela explique que la commande ne marche pas avec les listes (voir "Structures de données avancées").
La commande codice_56 regarde si chaque élément d'une matrice est dans un intervalle donné ; l'intervalle est donné sous forme de matrice. Par exemple
dsearch([1 3 5 7], [2 4])
va regarder quels sont les éléments du vecteur (1 ; 3 ; 5 ; 7) qui sont dans l'intervalle [2 ; 4]. La réponse est
0 1 0 0
car seul le second élément est dans l'intervalle. On peut indiquer des intervalles conjoints, par exemple codice_57 correspond aux intervalles I1 = [2 ; 4] et I2 = ]2 ; 6], et l'on a
-->dsearch([1 3 5 7], [2 4 6])
 ans =
 0. 1. 2. 0. 
indiquant que le deuxième élément est dans l'intervalle 1, et le troisième éléments est dans l'intervalle 2.
Avec l'option codice_58, codice_56 considère que le deuxième argument est un ensemble de nombres disjoints. Il cherche donc si chaque élément du premier argument est un élément du second :
-->dsearch([1 3 5 7], [3 7], "d")
 ans =
 0. 1. 0. 2. 
indique que le premier élément de (1 ; 3 ; 5 ; 7) est le premier élément de (3 ; 7), et que le quatrième élément de (1 ; 3 ; 5 ; 7) est le second élément de (3 ; 7).
On peut aussi localiser un vecteur-ligne ou un vecteur-colonne V au sein d'une matrice M :
vectorfind(M, V, "r") // vecteur-ligne
vectorfind(M, V, "c") // vecteur-colonne
la fonction retourne un vecteur d'indices correspondant aux lignes ou colonnes trouvées.
Scilab propose la fonction codice_60 pour chercher une « aiguille dans un botte de foin » "(needle in a haystack)", c'est-à-dire une hypermatrice dans une hypermatrice d'hypermatrices. Bien sûr, on peut s'en servir pour chercher un scalaire dans une matrice de scalaires, mais aussi une ligne ou une colonne particulière :
nb = members(A, BF)
[nb, pos] = members(A, BF)
cherche l'aiguille A dans la botte de foin BF, qui doit être une matrice ou hypermatrice d'éléments du même type que A ; la variable codice_61 est le nombre d'occurrences et codice_62 les indices de la première position trouvée. Par exemple
mois = ["jan", "fév", "mar", "avr", "mai", "jun", "jul", "aou", "sep", "oct", "nov", "déc"]; 
dates = ["déc", "avr" ; "fév", "déc" ; "jun", "jul"];
[tmp, datesnum] = members(dates, mois);
disp(datesnum)
-->12. 4. 
 2. 12.
 6. 7.
Ici, le fait d'aller cherche la position des aiguilles dans la botte permet de transformer les noms des mois en numéros.
On peut ajouter l'option codice_63 pour avoir la dernière occurrence :
[nb, pos] = members(A, BF, "last")
Si l'on veut chercher une ligne, alors A doit être une matrice ligne, et l'on utilise
members(A, BF, "rows")
on peut aussi chercher les permutations de A avec l'option codice_64 :
members(A, BF, "rows", "shuffle")
On peut chercher des colonnes avec l'option codice_65.
Opérations sur les ensembles.
Certaines matrices contiennent des éléments dupliqués. La commande codice_66 permet de supprimer les doublons, ou bien, vu d'une autre manière, détermine l'ensemble des valeurs dont est constituée la matrice.
Dans sa syntaxe la plus simple
unique(M)
renvoie une matrice colonne contenant chacun des éléments, classés par ordre croissant. On peut obtenir l'indice de la première occurrence de chaque éléments avec la syntaxe
[N, k] = unique(M)
Par exemple
A = [0, 0, 1, 1;
 0, 1, 1, 1;
 2, 0, 1, 1;
 0, 2, 2, 2;
 2, 0, 1, 1;
 0, 0, 1, 1];
-->unique(A)
 ans =
 0. 
 1. 
 2. 
-->[N, k] = unique(A)
 k =
 1. 
 8. 
 3. 
 N =
 0. 
 1. 
 2. 
Nous voyons que la matrice A, de dimension 6×4, comporte les valeurs 0, 1 et 2 — donc qu'elle est construite sur l'ensemble {0 ; 1 ; 2} —, et que la première occurrence de 0 est à l'indice 1, la première occurrence de 1 est à l'indice 8, et la première occurrence de 2 est à l'indice 3 (comptés comme d'habitude de haut en bas, et de gauche à droite).
On peut aussi considérer les lignes uniques, avec l'option codice_47 "(rows)", ou les colonnes uniques, avec l'option codice_48.
-->unique(A, "r")
ans =
 0. 0. 1. 1. 
 0. 1. 1. 1. 
 0. 2. 2. 2. 
 2. 0. 1. 1.
La matrice A comporte donc quatre lignes originales, et deux lignes qui sont des répétitions.
La commande codice_69 fait la même opération, mais sur deux matrices : elle détermine la réunion des ensembles des valeurs des deux matrices, et le cas échéant indique l'indice de première occurrence pour chacune des matrices.
Par exemple :
A = [0 1 2
 0 2 1];
B = [3 2 4;
 4 3 2];
-->union(A, B)
 ans =
 0. 1. 2. 3. 4. 
-->[N, ka, kb] = union(A, B)
 kb =
 1. 2. 
 ka =
 1. 3. 4. 
 N =
 0. 1. 2. 3. 4. 
On voit donc que la réunion des ensembles est {0 ; 1 ; 2 ; 3 ; 4}. On peut de même utiliser les options codice_48 ou codice_47.
Temporisation.
Dans certains cas, il est nécessaire de suspendre l'exécution du script, par exemple pour laisser des opérations extérieures (appels système, …) s'exécuter. On peut utiliser les commandes suivantes :
Analyse et construction de chaînes de caractères.
Les chaînes de caractères sont un moyen pratique et robuste d'échanger des informations, entre Scilab et l'utilisateur (clavier, écran) ou entre logiciels (fichiers de texte) : les codes ASCII ou UTF-8 sont normalisés et reconnus sous toutes les plateformes par la plupart des logiciels.
Nous avons déjà vu précdemment l'utilisation des commandes :
Localiser une sous-chaîne.
La commande codice_86 permet de localiser la position (le "n"-ième caractère) où se trouve une sous-chaîne : 
-->strindex("bonjour Marcel", "r M")
 ans =
 7.
car la sous-chaîne « r M » commence au 7e caractère. La sous-chaîne peut également être décrite par une expression régulière (voir ci-après), il faut alors rajouter le fanion codice_47 ; on peut également utiliser la commande codice_88 dans ce contexte. Par exemple
-->strindex("bonjour Marcel", "/r\ M/", "r")
 ans =
 7. 
-->regexp("bonjour Marcel", "/r\ M/")
 ans =
 7.
Couper à des positions données.
La commande codice_89 découpe une chaîne et crée un vecteur-colonne des sous-chaînes. Sans autre argument que la chaîne elle-même, elle la découpe en caractère individuels :
-->strsplit("abcd")
 ans =
!a !
!b !
!c !
!d !
Si l'on indique un vecteur de valeurs, il coupe après les endroits indiqués :
-->A = strsplit("aei ou y", [4, 7])
 A =
!aei !
!ou !
!y !
-->length(A(1))
 ans =
 4. 
La première sous-chaîne fait 4 caractères (l'espace en position 4 est inclus) ; la deuxième sous-chaîne fait trois caractères (l'espace en position 7 est inclus).
Couper à des délimiteurs.
Enfin, on peut indiquer des points de découpe soit sous la forme d'une chaîne de caractères, dite « délimiteur » ; il s'agit typiquement de virgules (le délimiteur décimal étant le point), points-virgules, d'espaces, de tabulations ou d'un caractère qui n'est pas utilisé dans les expressions (point d'exclamation, arobase, …). Les parties de la chaîne de caractère entre les délimiteurs sont appelés « jetons » "(tokens)".
Les délimiteurs par défaut sont l'espace et la tabulation, mais on peut définir soi-même le délimiteur. La commande codice_90 cherche les délimiteurs dans une chaîne de caractères, découpe la chaîne en "jeton1 — délimiteur1 — jeton2 — délimiteur2…" et crée une matrice colonne avec chaque sous-chaîne.
Par exemple, la commande
t = tokens("a = 5", "=")
crée la matrice codice_91. La commande codice_92 indique la position des jetons, sous la forme d'une matrice : la première colonne indique la position du premier caractère des jetons, la seconde la position du dernier caractère.
-->tokenpos("a = 5", "=")
 ans =
 1. 2. 
 4. 5. 
-->tokenpos("a=5", "=")
 ans =
 1. 1. 
 3. 3. 
La commande codice_93 fait le même travail de découpe, mais avec plus de possibilités ; le point de découpe peut être une chaîne de caractères — on coupe là où se trouve la chaîne —, mais aussi un vecteur de chaînes — on coupe là où se trouve "un des" éléments du vecteur —, ou bien une expression régulière (voir ci-après).
-->strsplit("aei ou y", " ")
 ans =
!aei !
!ou !
!y !
Ici, les caractères de séparation — les espaces — sont supprimés. La première chaîne fait 3 caractères et la deuxième en fait 2.
Les expressions régulières permettent une plus grande souplesse. Par exemple, sil l'on veut couper un nombre indéterminé d'espaces :
-->strsplit("aei ou y", "/\ +/")
 ans =
!aei !
!ou !
!y !
Là encore, les caractères de séparation sont supprimés.
Mentionnons encore les commandes :
--> strchr("une chaîne ; un délimiteur", ";")
 ans =
 ; un délimiteur
--> strtok("une chaîne ; un délimiteur", ";")
 ans =
 une chaîne 
Expressions régulières.
Une expression régulière est une description d'une chaîne de caractères. C'est elle-même une chaîne, qui commence et finit par une barre de fraction codice_96. Par exemple, l'expression régulière qui décrit « blabla » est codice_97.
Il existe un certain nombre de caractères réservés, outre la barre de fraction :
On les appelle « métacaractères ». Si l'on veut utiliser un de ces caractères, il faut le faire précéder d'une oblique inversée. Par exemple, la chaîne « 2+2 » est décrite par codice_98, et « /slash » par codice_99.
L'oblique inversée permet également de représenter des caractères d'échappement. Par exemple, une tabulation est représentée par codice_100, une nouvelle ligne par codice_101 et un retour-chariot par codice_102.
Si un caractère est répété, on peut le faire suivre par :
Si une portion de chaîne doit se trouver en début de ligne, l'expression régulière commence par codice_112 ; si elle doit se trouver en fin de ligne, elle se termine par codice_113. Par exemple, codice_114 et codice_115 sont tous les deux vrais pour « bonjour ».
On peut placer plusieurs caractères substituables entre crochets ; on parle de classe de caractères. Par exemple, codice_116 décrit les chaînes de caractère « béton » et « téton ». L'expression codice_117 correspond à un chiffre hexadécimal (chiffre décimal de 0 à 9, et lettre de a à f, en minuscule et en majuscule). On peut aussi interdire une classe en la commençant par l'accent circonflexe : codice_118 est tout sauf un chiffre décimal.
Enfin, il existe des classes toutes faites :
Ainsi, codice_132 décrit un ou plusieurs espaces.
On peut avoir des chaînes alternatives : elles sont séparées par un tube codice_133, et en cas d'ambiguïté, elles sont entre parenthèses : codice_134 décrit les chaînes « bonjour Marcel » et « au revoir Marcel ».
Fonctions extérieures.
Syntaxe pour définir une fonction.
Comme indiqué dans le chapitre "Calcul numérique", la définition d'une fonction commence par le mot clé codice_135 et se termine par le mot clé codice_136, selon la syntaxe suivante :
function [<arguments de sortie>] = <nom de la fonction>(<arguments d'entrée>)
 <instructions>
endfunction
Les instructions pouvant comporter des branchements conditionnels (tests), des boucles, … ce qui recoupe la notion de sous-programme.
Par exemple, la fonction calculant la puissance n-ième peut s'écrire :
function [y] = puissance(x,n)
// calcule la puissance n-ieme 
 y = 1 // valeur de y^0, et valeur initiale si n>0
 if n > 0 then 
 for i = 1:n
 y = y*x // on multiplie n fois x par lui-même
 end
 end 
endfunction
Cette fonction peut ensuite être utilisée en mode interactif ou à l'intérieur d'un programme : 
-->puissance(2,3)
 ans =
 8. 
Le résultat d'un calcul à l'intérieur d'une fonction n'est jamais affiché, l'utilisation de virgules ou de points-virgules est donc indifférente.
Fonction définie dans un fichier extérieur
Lorsque la fonction est définie dans un fichier texte, celui-ci peut être chargé au moyen de la commande codice_137 :
exec("fichier.sci")
Un fichier peut contenir plusieurs fonctions. Le nom de fichier peut contenir un chemin d'accès si celui-ci n'est pas dans le répertoire (dossier) courant.
La commande codice_9 permet de charger tous les fichiers codice_4 d'un répertoire, avec la syntaxe :
getd("chemin/")
où codice_140 est le chemin d'accès au répertoire.
Les commandes permettant de manipuler les répertoires sont décrites au chapitre "Gestion des fichiers > Manipulation des répertoires".
Fonction définie en ligne de commande
On peut définir une fonction en mode interactif, en ligne de commande. La syntaxe est identique, il suffit d'entrer les différentes lignes dans l'interpréteur. Toutes les instructions avant le mot clef codice_136 sont considérées comme appartenant à la fonction et ne sont pas interprétées immédiatement.
Passage d'une fonction en paramètre
d'un point de vue de la représentation des données, une fonction est une variable. On peut donc la passer en paramètre. Par exemple :
function [y] = ce_que_je_veux(action, parametre)
 y = action(parametre)
endfunction
peut ensuite s'utiliser ainsi :
-->ce_que_je_veux(sqrt, 2)
 ans =
 1.4142136 
-->ce_que_je_veux(max, [2 -1 3 0])
 ans =
 3. 
Et l'on peut ainsi renommer des fonctions, par exemple :
-->racine_carree = sqrt
 racine_carree =