Sommaire Carte Index Recherche Nouvelles Archives Liens A propos
[Barre Superieure]
[Barre Inferieure]
[Photo de l'auteur]
par Emre Demiralp

L´auteur:

Je suis étudiant au collège américain Robert d'Istanbul,et en même temps,un des administrateurs du département informatique de la faculté des arts et sciences de l'Université des Techniques d'Istanbul. Le système d'exploitation dominant dans ces départements est LINUX. Centres d'intêrets: PovRay et PostScript,animation,conception de CD,programmation,holographie etc... Utilisateur Linux depuis 1994.


Sommaire:

Postscript III:La pile des opérandes de PostScript: Tableaux,Variables,Boucles et Définitions de Macros

[Illustration]

Résumé:

Celui qui en sait beaucoup rate aussi beaucoup
-Proverbe Turc-.
L'auteur poursuit sa description de la pile des opérandes du langage PostScript. La définition des tableaux,les opérateurs de tableaux,la définition des variables,les boucles et les définitions de macros sont tous présentés ici en détail et accompagnés d'exemples et de questions auxquels il sera répondu dans le prochain article. Cet article ne termine pas la série consacrée à la pile des opérandes. Cette histoire se continuera dans les prochains articles.



 

Introduction

Voici le troisième article de la série sur PostScript. Nous approfondirons ici nos connaissances sur la pile des opérandes de PostScript. Nous aborderons plus particulièrement la définition des tableaux, les opérateurs de tableaux, la définition des variables, les boucles et les définitions de macros. Cette présentation est aussi illustrée d'exemples. L'histoire de la pile des opérandes se continuera dans les prochains articles en traitant de sujets associés à la pile des opérandes.

 

Tableaux et opérateurs

Dans les articles précédents, de cette série, nous avons parlé de la structure de la pile des opérandes et des opérateurs capables d'en la changer la structure. Toutes les quantités stockées dans la pile étaient des valeurs entières à l'exception d'un élément spécial destiné à créer des repères dans la pile. Cet élément, appelé -marktype-, et les opérateurs cleartomark et counttomark étaient utilisés pour effacer ou compter les éléments depuis ce repère. Il s'agit bien sûr, d'une facilité de regroupement, mais qui n'est pas la seule. Il est possible de créer une entité unique capable de regrouper plusieurs éléments. Cette entité est appelée tableau (array) et il est possible d'opérer sur ses éléments en utilisant les opérateurs de tableaux (array operators) du langage PostScript. Voici le détail trié de ces opérateurs et quelques exemples.

[  :Ceci crée un élément repère (marktype) dans la pile. Sans la présence de son compagnon, ] il joue le même rôle que la commande mark. Tous les éléments qui entrent dans la pile situé après cet opérateur sont considérés comme des éléments individuels bien qu'il y ait un repère avant eux dans la pile. La session suivante explique la relation entre [ et mark

GS>[ pstack
-marktype
GS<1>2 3 mark pstack
-marktype-
3
2
-marktype-
GS<4>

]  :C'est le pendant de l'opérateur ci-dessus [. La pile doit contenir un élément repère avant que celui ci soit donné. En fait, il est considéré comme clôturant la définition d'un tableau et termine sa construction. Comme chaque fin nécessite un début, PostScript recherche son pendant, le repère de début [, quand ] est introduit dans la pile. Si [ est manquant, alors PostScript retourne une erreur et aucune action n'est engagée. Si cet élément est introduit juste après [, alors un tableau vide est créé et stocké dans une seule entité de la pile (l'élément repère existant devient alors partie intégrante du tableau et n'apparaît plus comme un élément distinct) comme le montre la session suivante:

GS>[ pstack
-marktype-
GS<1>] pstack
[]
GS<1>

Après tout cela, la pile des opérandes contient un unique élément, un tableau vide. Un tableau non vide peut directement être créé en utilisant [ et ] avec les éléments du tableau lors d'une seule entrée dans l'interpréteur comme ci-dessous.

GS>[ 1 2 3 ] pstack
[1 2 3]
GS<1>

Comme nous pouvons le voir, le tableau est considéré comme une entité unique.

On peut créer un tableau avec un nombre donné d'éléments, même si l'on ne souhaite pas spécifier chacun d'eux. Ceci est réalisé au moyen de l'élément null qui signifie rien comme suit.

GS>[ null null ] pstack
[null null]
GS<1>

array:Cette commande nécessite un paramètre entier. Si le paramètre est n alors la commande est n array. A l'exécution, elle crée un tableau qui contient exactement n éléments null. Le résultat est le même qu'avec les repères de début et de fin de tableau. Par exemple, 3 array est équivalent à [ null null null ]. Elle recherche son paramètre sur le dessus de la pile. S'il est donné avant la commande, alors il est introduit sur la pile et devient l'élément supérieur qui sera utilisé par cette commande. Si le paramètre n'est pas donné avant la commande, alors l'élément supérieur de la pile sera utilisé pour dimensionner le tableau si c'est un entier. Dans le cas contraire une erreur est générée à cause de l'incompatibilité.

length:Cet opérateur calcule le nombre d'éléments d'un tableau. Les éléments null sont pris en compte dans ce calcul. L'opérateur a besoin d'un paramètre qui doit être un tableau. Il prend le paramètre sur le dessus de la pile. Le paramètre disparaît de la pile après exécution. Ainsi, si le paramètre est introduit avant la commande et qu'il s'agit d'un tableau, alors tout se passe bien et un nombre est situé en partie supérieure de la pile. Par exemple

GS>[ 1 2 3 4 5 ] length pstack
5
GS<1>

Si nous ne ne donnons pas de paramètre pour l'opérateur length, l'élément supérieur de la pile est utilisé, puis remplacé par son nombre d'éléments

GS<1>pstack
[1 2 3 6 7]
GS<1>length pstack
5
GS<1>

si l'élément supérieur de la pile n'est pas un tableau, un message d'erreur est émis.

get:Cet opérateur nécessite deux paramètres qui sont respectivement un tableau et un entier. La position de l'élément à récupérer est donnée par l'entier. Les positions sont données par les nombres naturels, c'est à dire à partir de zéro. En fait, ces règles s'appliquent à tous les paramètres de tous les opérateurs. Ils sont utilisés par la commande puis retirés de la pile. Leur type doit être compatible avec les valeurs données. Nous n'attirerons plus l'attention sur ce point maintenant. get s'utilise comme suit.

GS[1 6 3 0 9] 0 get pstack
1
GS<1>

put:Cet opérateur nécessite trois paramètres qui sont respectivement un tableau, un entier et l'élément à insérer dans le tableau. La commande recherche le tableau donné par le premier paramètre, se positionne à l'endroit pointé par le second paramètre et remplace le l'élément de cet position par celui donné comme troisième paramètre. Le tableau résultant n'est pas stocké dans la pile. Aussi pour un usage explicite de l'opérateur put, nous pouvons définir une variable tableau (une key en terminologie PostScript Ndt: Clef en français). L'opération est réalisée sur cette variable et le résultat introduit sur la pile d'où il peut être affiché: Regardez l'exemple suivant

GS>[1 2 3] 0 8 put
GS>

Rien ne se passe, dans la pile des opérandes. Mais il n'y a pas de message d'erreur non plus. En fait, put fait son travail mais le résultat n'est pas stocké sur la pile. Pour voir le résultat de cette même action sur la pile, voici la marche à suivre.

GS>/ar [ 1 2 3 ] def
GS>ar 0 8 put
GS>ar pstack
[8 2 3]
GS<1>

D'abord une variable de tableau ou une clef (en terminologie PostScript) est définie. Le nom de la variable est ar. La deuxième étape change le premier élément (index zéro) avec 8 en utilisant l'opérateur put. Après cela, ar pstack insère la valeur de la variable ar sur la pile des opérandes et affiche son contenu. Nous reparlerons des définitions de variables un peu plus tard dans cet article. Nous parlerons aussi des dictionnaires et de la pile des dictionnaires dans de prochains articles de cette série.

getinterval:Cet opérateur crée un sous tableau. Il nécessite trois paramètres qui sont respectivement le tableau à partir duquel le sous tableau sera créé,l'index qui détermine le premier élément du sous tableau et un entier qui donne le nombre des éléments du sous tableau. Le nouveau tableau (sous tableau) est placé sur la pile. Par exemple:

GS>[1 2 3 4 5 6 7 8 9] 2 3 getinterval pstack
[3 4 5]
GS<1>

putinterval:Remplace une partie d'un tableau avec un autre tableau. Trois paramètres sont nécessaires: d'abord le tableau à changer, ensuite un index entier qui précise la position de départ, et enfin le tableau qui remplacera les éléments désignés dans le premier tableau à partir de la position du second paramètre. La commande est similaire à put. Elle ne place pas le résultat sur la pile des opérandes. Pour voir comment afficher le résultat, méditez la session suivante:

GS>/ar [1 2 3 4 5 6 7 8 9] def
GS>ar 3 [0 0 0] putinterval
GS>ar pstack
[1 2 3 0 0 0 7 8 9]
GS<1>

aload:Prend un tableau comme paramètre et copie ses éléments comme une seule entité sur la pile. L'élément supérieur de la pile devient donc le tableau. Soit,

[1 2 3] aload pstack
[1 2 3]
3
2
1
GS<4>

astore:Cette commande remplace tous les élément d'un tableau donné comme second paramètre avec une séquence d'éléments pris sur la pile, dont le nombre est égal à la taille du tableau. Le résultat est un nouveau tableau.

GS>1 2 3 [null null null] astore pstack
[1 2 3]
GS<1>

copy:Copie le premier paramètre qui doit être un tableau dans le premier sous tableau du second paramètre qui doit aussi être un tableau. Le résultat affiché est le sous tableau copié et non le second tableau. Pour voir la forme finale du second tableau, une définition de variable peut être utilisée comme suit.

GS>[1 2 3] [4 5 6 7 8] copy pstack
[1 2 3]
GS<1>/ar [4 5 6 7 8] def
GS<1>[1 2 3] ar copy
GS<2>ar pstack
[1 2 3 7 8]
[1 2 3]
[1 2 3]
GS<3>

Les éléments du tableau ne sont pas nécessairement des entiers. Ils peuvent être des chaînes qui sont aussi des tableaux. Ceci signifie que les structures imbriquées sont autorisées en PostScript. Cet avantage nous permet de réaliser des opérations de matrices et des définitions de macros avec des matrices. Il est même possible, en principe, de traiter des tenseurs ou des séquences multi-dimensionnelles. Nous nous contenterons de cette information pour l'instant.

 

Clefs et Variables

Il est possible de définir des variables dans tous les langages de programmation. Leur utilisation permet de traiter des quantités sans s'occuper de leur position en mémoire. On peut obtenir une valeur stockée dans un segment de mémoire soit en donnant son adresse, soit en nommant une clef qui référence son contenu. La première approche consiste à utiliser des pointeurs comme en C... Si les adresses ne vous intéressent pas, alors utilisez simplement les clefs. Toutefois, le compilateur doit traiter les adresses mémoire et d'autre opérations dans ce cas. Pour cela, on définit juste un nom et lui assigne une valeur. Toutes ces actions servent en fait à définir au langage de programmation ce qu'est la variable et quelle est sa valeur de votre point de vue. Le compilateur, ou l'interpréteur, définit une portion de mémoire pour cette variable et toutes les assignations iront à cet endroit. Une structure similaire est disponible en PostScript. PostScript a des dictionnaires qui contiennent des noms ou des clefs et leur définitions relatives. En terminologie PostScript, un dictionnaire est composé de paires dont le premier élément est appelé une clef et le second une valeur. Par exemple, add est un nom (clef)qui réalise une addition (valeur). PostScript connaît le sens de add car il est stocké dans un dictionnaire qui est appelé systemdict. Si l'on tape la commande 1 2 add pstack, on obtient le résultat 3 parce que PostScript recherche les trois noms et agit comme suit. Il trouve 1 et 2 puis add. Les deux premiers objets sont des entiers qui sont stockés sur la pile des opérandes. Le troisième objet est une chaîne qui peut être un nom (key) ou pas. PostScript recherche ce nom dans ses dictionnaires. S'il est trouvé, l'action définie est engagée. Puisque add existe dans le dictionnaire système, systemdict l'action est (value) pour ce nom (key). Cela correspond respectivement à récupérer les deux éléments supérieurs de la pile, pour évaluer leur somme, puis à pousser le résultat sur la pile. Le reste de la commande est la chaîne pstack qui existe dans le dictionnaire système et signifie "afficher le résultat courant de la pile sur la sortie standard". D'un autre coté, nous pourrions donner la ligne suivante à l'interpréteur (intentionnellement ou non): 1 2 dad pstack. Dans ce cas, l'interpréteur émettra un message d'erreur car il n'existe aucune clef du nom de dad définie dans le dictionnaire de PostScript.

Nous ne sommes pas limité par les définitions existantes du dictionnaire système de PostScript. Il est possible de définir des procédures ou des identifications comme des actions de commandes définies par l'utilisateur. Si la définition est une identification alors le nom, ou clef, s'appelle une variable bien qu'il ne soit pas utilisé par la terminologie PostScript. Notre objectif est de faire des appels depuis d'autres langages de programmation bien connus. Pour définir des variables, tout ce que nous avons à faire est d'émettre /x value def ou value est un objet de PostScript comme un entier, un tableau, une chaîne... Par exemple si nous entrons /x 12 def à l'invite de l'interpréteur PostScript voit trois objets, /x, 12 et def. Les objets qui commencent par un slash sont reconnus comme des clefs ou des noms. Il peuvent être poussés sur la pile des opérandes sans s'occuper de savoir s'ils existent dans le dictionnaire. La commande def existe en tant que paire clef-valeur dans le dictionnaire système de PostScript et requiert deux paramètres, la clef ou le nom qui va être définit et la valeur qui lui sera assignée. PostScript crée ainsi une paire key-value après cette commande, /x 12 def, et la stocke dans un dictionnaire spécifique qui est appelé current dictionary. Ce sera l'élément supérieur de la pile du dictionnaire dont nous parlerons plus tard en détail dans cette série. Passé ce point, x sera reconnu par PostScript comme 12 pendant toute la session.

En principe, toute chaîne qui commence par un slash peut être utilisée comme une valeur de clef. Toutefois, il est préférable d'éviter d'utiliser d'autres caractères que les chiffres et les lettres. Les autres caractères comme la ponctuation, les slasch, etc... pourraient avoir des effets indésirables car ils peuvent avoir une signification spéciale pour PostScript comme le slash. Les limitations en nombre de caractères dans les chaînes utilisées comme clefs viennent des possibilités et limitations de l'interpréteur que vous utilisez. En fait, il n'est pas très pratique d'utiliser des noms d'une centaine de caractères de long bien que cela soit possible. PostScript est sensible à la casse ce qui permet une grande flexibilité. Les noms ne doivent pas être choisis parmi les clefs système de PostScript, faute de quoi les commandes système seront écrasées. Ainsi, si vous écrivez /add 13 def alors add devient une constante qui paralyse les capacités de PostScript pour le reste de la session. Il y aurait encore beaucoup de choses à dire sur ce sujet, mais nous nous contenterons de ces paragraphes en reportant le reste à de futurs articles.

 

Boucles

PostScript possède des schémas répétitifs, autrement dit des boucles. Elles fournissent un moyen de répéter plusieurs fois les mêmes types d'instructions. Le nombre de répétition peut atteindre des milliers, des millions, des milliards voire plus. Cela n'a aucune importance. En une simple commande, des procédures entières peuvent être exécutées en utilisant des boucles.

repeat:Cette commande requiert deux paramètres. Le premier est un entier et décrit le nombre de répétitions tandis que le second est généralement une procédure qui est un bloc d'actions. Dans PostScript, un bloc est définit par les délimiteurs et . Les instructions ou les commandes peuvent être alignées entre ces délimiteurs. La syntaxe de la commande est n ... repeat. PostScript insère le premier paramètre sur la pile, puis la procédure incluse dans le bloc ... est évaluée. Finalement repeat est recherché dans le dictionnaire et exécuté. Puisque l'action est juste une répétition, la procédure définie par le second paramètre est exécutée n fois. Voici un exemple.

GS>3 1 repeat
1
1
1
GS<3>

Trois valeurs entières 1 sont poussées sur la pile par cette commande. En fait, cette procédure est très simple. Elle consiste seulement à entrer 1. Voici un exemple un peu plus compliqué.

GS>1 5 1 add repeat pstack
6
GS<1>

Dans cet exemple, d'abord on entre 1 sur la pile des opérandes puis la procédure 1 add est exécutée 5 fois sur la pile des opérandes. Ces 5 étapes se passent comme suit. D'abord, 1 add est exécuté. add n'a pas besoin de paramètres, car le second est donné par la procédure. Le premier paramètre (ou opérande) en terminologie PostScript) est pris sur la pile des opérandes. Ainsi lors du premier pas de repeat, 1 1 add est exécuté. Le seul élément de la pile, 1 est détruit après cette exécution et le résultat qui est 2 est poussé sur la pile. Le second pas est alors 2 1 add ce qui donne comme unique résultat 3. Ceci conduit au troisième pas 3 1 add. Les deux pas restant font appel à 4 1 add et 5 1 add. Par conséquent le seul élément qui reste sur la pile après exécution est 6.

for:Cette commande à besoin d'une variable de contrôle entière pour l'exécution répétitive d'une procédure. La variable de contrôle démarre d'une valeur initiale et s'incrémente à chaque exécution de la procédure. Cette action se poursuit jusqu'à ce que la valeur limite prescrite soit atteinte. C'est pourquoi cette commande prend quatre opérandes dont les trois premières sont Initial, Increment, et Limite. Ces trois paramètres doivent être des valeurs numériques, entières ou décimales. La quatrième opérande est le corps de la procédure qui peut être une commande unique ou un bloc compris respectivement entre et . La syntaxe complète de la commande est Initial Increment Limit Procedure for. Quand elle est exécutée, PostScript crée un compteur (variable de contrôle en terminologie PostScript) et lui affecte la valeur Initial. Cette valeur est insérée sur la pile des opérandes. Elle peut être utilisée par la Procedure en tant qu'opérande. Dans ce cas elle est extraite de la pile, sinon elle y est stockée. Une fois le compteur initialisé avec Initial la Procedure est exécutée. La variable est ensuite incrémentée de la valeur Increment. Puis le cycle continue de la sorte jusqu'à ce que la variable excède la valeur de Limit.Si Increment est positif alors l'exécution de for se termine quand le compteur dépasse Limit. Sinon, l'exécution de for s'arrête quand le compteur devient plus petit que Limit. L'étendue de l'interval du compteur ne doit pas être vide. Cela signifie que Initial doit être plus petit que Limit quand Increment est positif et vice-versa. Exemple:

GS>1 -0.5 -1  for pstack
-1.0
-0.5
0.0
0.5
1.0
GS<5>clear 0 1 1 23 add for pstack
276
GS<1>

Ici la première commande ne fait rien car il n'y a aucune procédure de définie, elle est nulle. Par conséquent, toutes les valeurs du compteur sont stockées et y restent car aucune procédure n'utilise ces valeurs. La seconde commande inclus quant à elle la procédure add qui nécessite deux opérandes. La première est toujours le premier élément sur la pile et la seconde est la valeur du compteur de boucle qui est poussé sur la pile à chaque pas. La seconde commande évalue la somme des 23 premiers entiers positifs. Elle nécessite une valeur de départ extérieure qui est fournie ici comme étant 0. En d'autres termes, 0 ne fait pas partie de la commande for.

forall:Cette commande exécute une procédure pour chaque élément d'un tableau. Pour cela, elle énumère chaque élément du tableau à partir de zéro. Elle utilise un compteur temporaire pour contrôler la boucle. La valeur initiale du compteur est 0, son incrément est 1 et sa limite supérieure la longueur du tableau. Le fonctionnement de la boucle est très similaire à celui de for. La seule différence concerne l'utilisation des éléments d'un tableau au lieu d'un compteur. La commande nécessite deux paramètres dont le premier est le tableau et le second la procédure. La syntaxe complète de la commande est Array Procedure forall . La session PostScript suivante évalue la somme d'un tableau. L'arrangement de tous les éléments d'un autre tableau dans la pile des opérandes est aussi présenté et les explications viennent ensuite.

GS>0 [11 23 45 -89 26 12 0 -34] add forall pstack
-6
GS<1>[1 22 -12 0]  forall pstack
0
-12
22
1
-6
GS<5>

loop:Cette commande n'utilise qu'un paramètre qui est la procédure à exécuter. Elle s'exécute infiniment et ne peut être arrêtée que par une interruption extérieure telle que Ctrl-C si la procédure ne possède pas de structure spéciale. Si la procédure contient exit ou stop quelque part dans la boucle, alors celle ci est arrêtée quand une de ces commandes est rencontrée. Le contrôle est alors passé à l'objet suivant. La syntaxe de la commande estProcedure loop.

 

Définitions de Procédure ou de Macros

Dans PostScript, Procédure ou Macro réfère à un ensemble ordonné d'objets qui doivent être regroupés entre et . Ils peuvent être nommés en utilisant une définition de clef comme /Macro1 1 add 2 mul def. Dans ce cas, la clef /Macro et sa valeur 1 add 2 mul sont ajoutés au dictionnaire courant et situés au dessus dans la pile des dictionnaires. Quand l'objet Macro1 est tapé à l'invite de l'interpréteur, son action est exécutée. Les procédures définies dans les blocs peuvent être simples ou compliquées à volonté. Dans un prochain article nous reviendrons sur les macros.

 

Exercices

A partir de cet article nous donnerons quelques exercices au lecteur. Les réponses seront données dans les articles suivants.

  • 1)  Ecrire une procédure qui prend une opérande entière et évalue la somme des carrés des entiers compris entre 1 et cette opérande incluse.

  • 2)  Écrire une procédure qui prend deux opérandes entière et évalue le cube des entiers compris entre ces deux opérandes incluses. La procédure doit créer un tableau et y ranger les cubes.

  • 3)  Écrire une procédure qui prend un tableau comme opérande et qui calcule la moyenne de ses éléments. Elle doit aussi calculer la racine carrée de la somme des carrés des éléments du tableau

  • 4)  Assumer que PostScript ne contient pas de procédure exp et écrire une procédure qui prend deux valeurs numériques comme opérandes. La première sera la base et la seconde l'exposant. La procédure calculera la base à la puissance exposant. Les opérandes devront rester sur la pile après l'opération.

  • 5)  L'objet mathématique, matrice, peut être considéré comme un tableau de tableaux. Une matrice carrée de rang N peut être représentée par un tableau à N éléments dont les éléments sont aussi des tableaux à N éléments. Appeler ce type de tableau "Square Matrix". Écrivez une procédure qui évalue la trace (somme des éléments diagonaux) d'une matrice carrée. Le résultat et la matrice carrée originale doivent être conservés après l'opération.

  • 6)  Écrire une procédure qui transpose une matrice carrée (échange des colonnes et des lignes). Le résultat et la matrice carrée originale doivent être conservés après l'opération.

  • 7)  Écrire une procédure qui somme deux matrices carrée. Le résultat et la matrice carrée originale doivent être conservés après l'opération.

  • 8)  Écrire une procédure qui multiplie deux matrices carrées. Le résultat et la matrice carrée originale doivent être conservés après l'opération.


  • Site Web maintenu par l´équipe d´édition LinuxFocus
    © Emre Demiralp
    LinuxFocus 1999
    Translation information:
    en -> -- Emre Demiralp
    en -> fr John Perr

    1999-11-24, generated by lfparser version 0.7