Aller au contenu

Boucle \for à syntaxe souple

De nombreuses boucles de type for ont été écrites, la plus célèbre est \foreach de l’extension pgffor qui a de très nombreuses fonctionnalités dont les principales sont :

  • itérer une variable sur un ou plusieurs intervalles avec choix du pas ;
  • itérer plusieurs variables simultanément.

Les inconvénients sont connus :

  • l’exécution d’une boucle se déroule dans un groupe ;
  • il n’est pas possible de choisir la syntaxe des variables itérées qui doivent être séparées les unes des autres par /.

Nous allons donc créer une macro \for qui s’affranchira des limitations citées, sans pour autant être aussi puissante que \foreach, puisqu’elle ne permettra pas l’itération sur des intervalles autres que des nombres décimaux.

Syntaxe

La syntaxe est la suivante :

\for<*>[<sep>]<liste de variables> in {<liste des valeurs>}{<code>}

où :

  • l’étoile, qui doit immédiatement suivre \for indique, si elle est présente, que la boucle doit se dérouler dans un groupe ;
  • l’argument optionnel <sep> est le séparateur entre les valeurs dans la <liste des valeurs>. C’est la virgule par défaut ;
  • la <liste de variables> contient une ou plusieurs variables organisées en une syntaxe qui se retrouvera dans les valeurs présentes dans la liste. Ainsi, si la <liste de variables> est « ( \x;\y) » alors cela indique qu’il y a 2 variables itérées simultanément \x et \y, et que dans la <liste des valeurs>, cette syntaxe doit se retrouver. Pour 3 itérations, la <liste des valeurs> pourrait être « (3;4)(-1;2)(0;-7) » ;
  • si la <liste des variables> est une macro, celle-ci sera développée ;
  • le <code> est exécuté à chaque itération et contient les variables présentes dans la <liste de variables> ;
  • des espaces seront ignorés s’ils sont de part et d’autre de <*> , [<sep>], et  in ;
  • si une valeur est de la forme <a><signe><i>...<b><a> et <b> sont des décimaux signés, où <signe><i> représente un décimal obligatoirement signé, alors une boucle intermédiaire sera mise en place dans laquelle la variable (forcément unique) parcourra l’intervalle [<a> ; <b>] avec un incrément <signe><i>. Si <signe><i> est omis, <i> vaut 1 et le <signe> est celui de <b>-<a> ;
  • une macro \break permettra de sortir de la boucle en cours et une macro \allbreak sortira de toutes les boucles si cette instruction est rencontrée dans une boucle imbriquée.

Lorsque la macro n’est pas étoilée, c’est-à-dire que les variables ne seront pas modifiées dans un groupe, la macro \for devra restaurer les variables à leur état antérieur à la fin de la boucle.

Méthode

Toute la difficulté sera de parcourir la <liste de variables>, et d’en analyser la syntaxe, c’est-à-dire trouver les variables et préserver la syntaxe dans laquelle elles sont écrites, pour parcourir plus tard la <liste des valeurs>.

Prenons par exemple l’appel à \for suivant :

\for (\x;\y) in {(2;6),(1;2),(5;3)}{somme = \number\numexpr\x+\y\relax\par}

Il va falloir que notre macro \for construise une macro capable de lire les valeurs dans la <liste des valeurs>. Cette macro sera à argument délimité et son texte de paramètre sera (#1;#2),#3\@nil. Les arguments #1 et #2 seront les valeurs en cours alors que #3 représente les valeurs restantes.

Voici dans quoi seront contenus les différents textes utiles à la macro \for :

  • le registre de token \_vartoks contient les variables impliquées dans la boucle, par exemple «  \x\y » ;
  • le compteur \_varcnt contient le nombre de variables pour la boucle en cours ;
  • le registre de tokens \_deftoks contient le texte de paramètre de la macro chargée de lire les valeurs. Par exemple (#1,#2) ;
  • le compteur \_nstcnt contient la profondeur d’imbrication des boucles \for entre-elles. Ce compteur est essentiel pour que les imbrications fonctionnent et que les définitions d’une boucle interne n’effacent pas celles d’une boucle mère. De nombreuses macros auront la valeur de ce compteur dans leur nom. Pour plus de facilité de lecture, la valeur de ce compteur sera désormais notée <i> ;
  • le token \_startok_<i> contient le premier token après \for : il est utile pour savoir si on doit ouvrir un groupe ou non ;
  • le booléen \if_spacefound est mis à vrai si un espace est lu alors que la <liste des variables> est parcourue ;
  • la macro \_cslist_<i> regroupe la liste des variables utilisées dans la boucle de profondeur <i> : elle sert à restaurer les variables à la fin de la boucle ;
  • la macro \_valuesep_<i> contient le séparateur de la boucle en cours ;
  • la macro \_readvalue_<i> est la macro à argument délimités capable de lire une valeur et d’assigner les valeurs lues aux variables ;
  • la macro \_valuelist_<i> est la liste des valeurs n’étant pas encore lues ;
  • la macro \_loopcode_<i> est le code devant être exécuté à chaque itération de la macro \for ;

Le code

Le token «  _ » prendra le catcode 11 pour pouvoir faire partie du nom d’une macro, un peu à la mode de LaTeX3. Le code est abondamment commenté, et il est facile de suivre les différentes étapes dans l’élaboration de la boucle.

Ce code, compilé avec pdftex donne la sortie suivante : for.pdf

Laisser un commentaire