Date de mise à jour : 15 juin 2011
Ce document présente des possibilités d'application de blt::vector. Il est nécessairement incomplet. Pour de plus amples informations, consulter http://tcltk.free.fr/blt/vector.html
L'usage des vecteurs a pour principal avantage la simplicité d'écriture du code et donc sa maintenance et il évite d'écrire de très nombreuses boucles.
La plupart des exemples présentés peuvent être saisis directement dans la Console d'Aud'ACE et le lecteur peut vérifier le résultat.
La réponse est simple : C'est une liste (au sens Tcl) de
valeurs numériques.
En Tcl on l'écrit d'une des deux manières :
set liste_de valeurs [ list 1 2 3 4 ]
set liste_de valeurs { 1 2 3 4 }
Pour déclarer un vecteur, on écrit (le nom du vecteur doit obligatoirement commencer par une lettre et contenir des lettres, des nombres ou des underscores) :
blt::vector create v_1
Au fait est-ce qu'il existe ?
blt::vector names
Surprise, il n'est pas tout seul.
Combien d'éléments a-t-il ?
set a [ v_1 length ]
Nouvelle surprise : Le vecteur existe mais il a 0 élément, exactement comme une liste qui aurait été créée par [ list ].
Ces commandes sont utiles lors de la mise au point de scripts pour vérifier les créations/destructions de vecteurs.
Pour le remplir on peut utiliser plusieurs manières :
v_1 set [ list 1 2 3 4 ]
v_1 set { 1 2 3 4 }
v_1 set $liste_de valeurs
v_1 append ...(un vecteur, une liste, une valeur, une
succession des trois)
Pour traiter une liste Tcl, par exemple rechercher son minimum, son maximum, des valeurs négatives, traiter les éléments, il faut écrire une procédure qui lit chaque valeur de la liste, la compare, éventuellement sort de la boucle si une condition est remplie et retourne le résultat final. Ce n'est pas très compliqué mais harassant d'écrire des boucles du type :
for { set i 0 } { $i < [ length $liste_valeurs ] } {
incr i } {
faire quelque chose
}
Pour connaître le minimum ou le maximum d'un vecteur, il suffit d'écrire :
set minimum $v_1(min)
set maximum $v_1(max)
La réponse est immédiate.
En pur Tcl, le calcul de la somme des éléments
nécessiterait de créer une boucle d'accumulation.
Avec un vecteur, on écrit :
set sum $v_1(sum)
set mean [ expr { $sum/[llength $liste_de valeurs ] } ]
pour connaître la moyenne. Il suffit d'écrire :
set mean $v_1(mean)
Ce type de commande fonctionne pour min, max, mean, sum et prod, hélas pas pour med.
::console::affiche_resultat "$v_1(:)"
permet d'éditer la totalité du vecteur. Le (:) équivaut à (début : end).
Pour éditer les valeurs comprises entre l'indice i et j :
::console::affiche_resultat "$v_1($i:$j)"
ou mieux :
::console::affiche_resultat "[ v_1 range $i $j ]"
Cette commande retourne une sous-liste. Elle peut donc aussi servir pour créer ou compléter un autre vecteur :
vecteur2 append [ v_1 range $i $j ]
v_1 expr { v_1+10 }
Ajoute la valeur 10 à chaque élément du
vecteur.
Les opérations usuelles + - / * % ^ sont valables.
D'autres opérations nécessitent un style analogue à celui du Tcl :
abs round random norm
acos cos hypot sinh
asin cosh log sqrt
atan exp log10 tan
ceil floor sin tanh
Par exemple :
v_1 expr { log(v_1) }
Il est aussi possible d'opérer sur deux vecteurs de longueur identique (les éléments étant traités deux à deux), à condition de ne pas oublier de créer le second vecteur :
blt::vector create resultat
resultat expr { v_1*vecteur2 }
Les comparateurs < > <= >= == =! et les opérateurs logiques && || peuvent être utilisés : Le vecteur résultat contient des 1 pour les éléments remplissant la condition, sinon 0.
Des opérations de déplacements circulaires vers la droite >> ou vers la gauche << des éléments sont possibles :
v_1 expr { v_1 >> 2 }
Le dernier élément devient le second et l'avant-dernier le dernier.
On remarquera que :
Pour isoler la valeur située à l'indice j du vecteur :
set valeur $v_1($j)
Pour repérer les positions ayant une valeur
donnée :
set liste_des_index [ v_1 search $cette_valeur ]
Pour rechercher les valeurs comprises dans un intervalle :
set liste_des_index [ v_1 search $borne1 $borne2 ]
Qui n'a pas oublié une fois qu'une liste Tcl démarre à l'indice 0 et que end est en fait égal à [ expr {[length $liste_de valeurs]-1 }] ?
Pour les vecteurs c'est pareil (par défaut) sauf que l'indice de début est modifiable par :
v_1 offset 1
L'exemple ci-dessus est particulièrement pratique lorsque
le but est d'analyser les valeurs d'une ligne ou d'une colonne de
pixels car celles-ci commence toujours à l'index 1.
Dans cet exemple, la position d'un point dans la ligne
serait exactement égale à l'indice de la valeur du
point dans le vecteur.
Comment extraire une sous-liste d'une liste, la modifier puis
replacer les valeurs au bon endroit ?
En Tcl il faut mémoriser les indices puis,
ultérieurement, ne pas se tromper sur leur rappel.
vecteur2 set [ v_1 range $i $j ]
vecteur2 offset $i
vecteur2 est le reflet fidèle (indices et valeurs) du
morceau de vecteur.
Après traitement, on peut le remettre en place avec
:
v_1 index $i $j [ vecteur2 range $i $j ]
ou
set start [ vecteur2 offset ]
v_1 index $i $j [ vecteur2 range $start end ]
Pour connaître l'indice initial d'un vecteur :
set offset [ v_1 offset ]
D'autres fonctions particulières (adev, kurtosis, median,
q1, q3, prod, sdev, skew, var) existent mais elles ne peuvent pas
être appelées directement comme min, max, mean et sum.
La raison en est que les vecteurs disposent d'indices
spéciaux pour min, max , sum et mean permettant d'appeler
directement les valeurs. La documentation n'est pas très
explicite sur la manière de mettre en œuvre les autres
fonctions.
Une solution qui marche est de considérer qu'elles
retournent un vecteur à une seule composante :
resultat expr { adev(v_1) }
set adev $resultat(:) (écart moyen par rapport à
la moyenne)
resultat expr { kurtosis(v_1) }
set kurtosis $resultat(:) (moment d'ordre 4)
resultat expr { median(v_1) }
set median $resultat(:) (valeur médiane)
resultat expr { q1(v_1) }
set q1 $resultat(:) (premier quartile)
resultat expr { q3(v_1) }
set q3 $resultat(:) (troisième quartile)
resultat expr { prod(v_1) }
set prod $resultat(:) (produit des éléments)
resultat expr { sdev(v_1) }
set sdev $resultat(:) (racine carrée de la variance
var)
resultat expr { skew(v_1) }
set skew $resultat(:) (moment d'ordre 3)
resultat expr { var(v_1) }
set var $resultat(:) (somme des carrés des écarts
à la moyenne, divisée par le nombre
d'éléments)
D'autres fonctions retournent un vecteur de longueur identique au vecteur initial :
resultat expr { norm(v_1) } (vecteur normé entre 0 et
1)
resultat expr { sort(v_1) } (vecteur trié par ordre
croissant)
Il existe une autre solution pour sort :
v_1 sort (tri par ordre croissant)
v_1 sort -reverse (tri par ordre décroissant)
v_1 sort vecteur2 (tri par ordre croissant de v_1 et
réarrangement du vecteur2 pour que les indices restent
cohérents).
Le vecteur v_1 créé plus haut n'existait que par son nom et avait une longueur initiale nulle (si si).
La commande :
blt::vector create vecteur(20)
crée un vecteur de 20 éléments initialisés à la valeur 0, l'indice initial étant 0 et l'indice final 19 (soit 20 valeurs).
Si on veut créer un vecteur dont l'index initial est 1 (0 par défaut) et de longueur 20 on écrit :
blt::vector create vecteur(1:20)
Plusieurs modes d'affectation de valeurs sont disponibles :
set v_1(:3) 12 (le premier élément ayant pour
indice 1, les éléments de rang 1 à 3 valent 12)
set v_1(4:5) 2 (les éléments de rang 4 à 5
valent 2)
v_1 index 2 0.5 (le second élément vaut
maintenant 0.5)
set i 6
set v_1($i) 10 (le sixième élément vaut 10)
set v_1{7:2*$i) 20 (les éléments de rang 7 à
rang 12 valent 20)
set v_1(2*$i+1:) 0.34 (les éléments de rang
supérieur 12 valent 0.34)
set v_1(end) 45 (le dernier élément, le
vingtième, vaut 45)
set v_1(++end) 9 (rajoute un élément de valeur 9
au vecteur)
v_1 delete 11 15 (supprime le 11ème et le 15ème
élément, la longueur du vecteur diminue)
v_1 length 20 (rajoute des 0 pour que le nombre
d'éléments redevienne égal à 20)
v_1 length 5 (supprime tous les éléments de rang
> 5)
Il est aussi possible de créer un vecteur avec des valeurs échelonnées :
v_1 seq 5 10 1
Il est aussi possible de fusionner des vecteurs :
v_1 merge v_2 v_3
Contrairement aux variables locales d'une procédure Tcl,
qui sont détruites à la fin de la procédure, les
vecteurs subsistent ce qui conduit à encombrer inutilement
l'espace mémoire et surtout à des résultats
inattendus si on a recours à des 'append' ou si on utilise
le nom d'un vecteur existant.
Il est donc prudent de déclarer les vecteurs avec
l'option -watchunset 1 :
blt::vector create v_1 vecteur2 -watchunset 1
Et à la fin de la procédure de tuer les vecteurs :
blt::vector destroy v_1 vecteur2
ou à tout le moins, dans les procédures itératives, à utiliser la commande :
v_1 length 0
Ce qui a pour effet de supprimer toutes les valeurs antérieures : Le vecteur est comme au moment de sa création.
Le nom des commandes
set, range, index, split, etc.
évoque celui
concernant les listes.
Pour utiliser
lassign
il faut utiliser la notation
array comme par exemple :
lassign $vector(:) x0 y0 x1 y1
Les fonctions min et max constituent des solutions
alternatives à des fonctions telles que lmin et lmax.
Il est aussi possible d'obtenir la liste des indices des
valeurs égales au minimum :
set indexes [ v_1 search $v_1(min) ]
Puis de demander le niveau de cette valeur :
set min $v_1([ lindex $indexes 0 ])
L'équivalent de int n'existe pas et round fait l'arrondi à la valeur la plus proche. Il faut donc utiliser le % :
v1 set { 12.5 13.4 }
v2 expr {v1 % 1} ;#-- v2 est égal à { 0.5 0.4 }
v1 expr {v1-v2} ;#-- v1 est égal à { 12.0 13.0 }
vecteur_mesure append $nouvelle_mesure
set moyenne $vecteur_mesure(mean)
Un vecteur n'admet que des valeurs numériques, autrement il y a erreur.
set liste_a_analyser [ list 1 2 3 blabla ]
v_1 set $liste_a_analyser
Retourne :
invalid bareword "blabla" .....
On peut mettre à profit cette erreur pour détecter l'absence d'erreur sans avoir à analyser chaque élément :
if ![ catch { v_1 set $liste_a_analyser } ErrInfo ] {
#-- c'est bien une liste de valeurs numériques
todo ...
} else {
#-- ce n'est pas une liste de valeurs numériques
}
La somme des termes d'une suite arithmétique est
donnée par la formule
Somme=Nombre_de_termes*(Premier_terme+Dernier_terme)/2.
On pourra donc écrire :
v_1 set $liste_de_valeurs
v_1 sort
if { $v_1(sum) == [ expr { [ v_1 length
]*($v_1(0)+$v_1(end))/2 } ] } {
#-- c'est une série sans trou
todo
}
set seuil_bas 20
set seuil_haut 50
v_1 expr { v_1 < $seuil_bas || v_1 > $seuil_haut
}
Toutes les valeurs sont remplacées par des 0 ou des 1
selon que la valeur est dans l'intervalle ou en dehors.
Pour connaître les positions des éléments en
dehors :
set indexes [ v_1 search 1 ]
Pour calculer 100 points d'une courbe y=a+bx+c*x², on crée deux vecteurs :
set a 1.2
set b 3.5
set c -1.56
blt::vector create x y -watchunset 1
x seq 1 100 1
y expr { $a+$b*x+$c*x^2}
Et pour afficher le résultat :
::plotxy::plot $x(:) $y(:)
Pour indiquer à une procédure les valeurs à traiter, il suffit de lui donner le nom du vecteur à traiter au lieu de lui passer la liste des valeurs :
proc todo { nom_du_vecteur } {
$nom_du_vecteur expr { log($nom_du_vecteur^3) }
}
todo v_1
Les valeurs de v_1 sont modifiées sans qu'il soit nécessaire de retourner la liste des valeurs.
Une autre application concerne blt::graph. Pour créer une courbe, on écrit :
$w element create courbe -xdata -x $liste_des abscisses
-ydata $liste_des ordonnées
Avec les vecteurs il suffit d'écrire :
$w element create courbe -xdata vecteur_abscisses -ydata
vecteur_ordonnees
La modification du contenu des vecteurs modifie automatiquement le graphique.
Admettons l'existence d'un vecteur n'ayant que deux valeurs ayant pour abscisses 150 et 151 (pixels).
v_1 set [ list 102 98 ]
v_1 offset 150
Pour déterminer l'abscisse intermédiaire ayant pour valeur 100 (le cas est simple = 150.5) :
v_1 populate z 9 (création d'un vecteur 'z' comportant
11 éléments et donc 10 intervalles)
z offset [ expr { [ v_1 offset ]*10 } ] (l'indice de
début de z vaut 10 fois l'indice soit 1500)
set index [ expr { [ z search 99.9 100.1 ]/10 } ] (la
réponse est divisée par 10 et vaut 150.5)
Une matrice est une liste de listes :
set matrice { {1 2 3} {4 5 6} }
Il est facile d'en déduire qu'une matrice est une liste de vecteurs :
v1 set [list 1 2 3]
v2 set [list 4 5 6]
La notation suivante ne fonctionne pas :
set matrice [list un deux]
par contre la suivante fonctionne :
set matrice [list $v1(:) $v2(:)]
[gsl_mindex $matrice 1 2] --> 5
La matrice ne tient pas compte des éventuels offsets des vecteurs.
Pour extraire une ligne de matrice vers un vecteur :
v2 set [lindex $matrice 0]
Dans le calcul matriciel gsl_mfitmultilin on recourt à un
vecteur w de pondération des valeurs.
Comment obtenir ce vecteur en sachant qu'il doit avoir le
même nombre d'éléments que le vecteur des mesures
:
blt::vector create w([vecteur_mesures length])
Le vecteur est créé mais tous ses éléments valent 0.
w expr { w == 0 }
Maintenant ils sont tous égaux à 1 !
On aurait aussi pu écrire :
set w(:) 1
Que vaut le binaire 1 0 1 1 0 0 1 1 0 0 0 0 1 1 1 1 en décimal ?
base seq 0 15 1 ; #-- exposant de 2
base expr {2^base} ; #-- puissances de 2
set base(0) 0 ; #-- patch pour que 2^0 = 0 et non pas 1
v set { 1 0 1 1 0 0 1 1 0 0 0 0 1 1 1 1 } ; #-- binaire
à convertir
v expr { base*v} ; #-- multiplication
set result $v(sum) ; #-- résultat en décimal
Comment alterner des doublets ou des triplets de valeurs au sein d'une seule liste ?
v1 set { 1 4 7 }
v2 set { 2 5 8 }
v3 set { 3 6 9 }
foreach a $v(1:) b $v2(:) c $v3(:) {
result append $a $b $c
} ; #-- result vaut { 1 2 3 4 5 6 7 8 9 }
L'opération de démultiplexage est aussi simple :
foreach {a b c} $result(:) {
v4 append $a ; #-- v4 vaut { 1 4 7 }
v5 append $b ; #-- v5 vaut { 2 5 8 }
v6 append $c ; #-- v6 vaut { 3 6 9 }
}