• Aucun résultat trouvé

9. Les macros

9.2. Macros paramétrées

L’idée de base derrière les macros paramétrées est d’obtenir une plus grande souplesse en autorisant de passer une information auxiliaire à une macro. On a déjà vu que les définitions de macros peuvent avoir des paramètres formels qui représentent des ex-pressions transmises lorsque la macro est appelée. Pour le moment, une définition telle que

def rotatedaround(expr z, d) = <texte de remplacement>enddef permet à l’interpréteurMETAPOSTde comprendre des appels de macro sous la forme

rotatedaround(<expression>,<expression>)

Le mot clefexprdans la définition de la macro signifie que les paramètres peuvent être des expressions de n’importe quel type.

Quand la définition spécifie (expr z, d), les paramètres formels z et d se comportent comme des variables de type approprié. À l’intérieur du <texte de remplacement>, ils peuvent être utilisés dans des expressions exactement comme des variables, mais ces dernières ne peuvent pas être redéclarées ou assignées. Il n’y a aucune restriction contre des arguments inconnus, en partie ou en totalité. Ainsi, la définition

def midpoint(expr a, b) = (.5[a,b]) enddef fonctionne parfaitement quandaetbsont inconnues. Une équation telle que

midpoint(z1,z2)=(1,1) peut être utilisée pour aider à la détermination dez1etz2.

Il faut noter que la définition précédente demidpointfonctionne pour des variables numériques, des couples ou des couleurs aussi longtemps que les deux paramètres sont du même type. Si pour une raison quelconque, on veut qu’une macro middle-pointfonctionne avec un chemin ou une figure(picture), il sera nécessaire d’effectuer un testifsur le type des argument. Ceci utilise le fait qu’il existe l’opérateur unaire

path<primaire>

qui retourne un résultat booléen indiquant si l’argument est un chemin.

Puisque le test élémentaireifa la syntaxe

if<expression booléenne>:<tokenséquilibrés>else :<tokens équilibrés>fi où les<tokenséquilibrés>peuvent être n’importe quelle séquence detokensrespectant les emboîtements deifetfi, la macromiddlepointcomplète avec un test sur les types ressemblera à

def middlepoint(expr a) = if path a : (point .5*length a of a) else : .5(llcorner a + ucorner a) fi enddef ;

La syntaxe complète pour les testsifest indiquée dans la figure 41. Elle permet des testsifmultiples comme

ife1: ... else : ife2: ... else : ... fi fi qui peuvent être raccourcis par

ife1: ... elseife2: ... else : ... fi oùe1ete2représentent des expressions booléennes.

Il faut noter que les testsifne sont pas des instructions et les<tokenséquilibrés>

dans les règles syntaxiques peuvent être n’importe quelle séquence detokens équili-brés même si elles ne forment pas une expression complète ni une instruction com-plète. Ainsi, on aurait pu économiser deuxtokens, au dépend de la clarté, en définissant midpointcomme ceci

def midpoint(expr a) = if path a : (point .5*length a of else : .5(llcorner a + urcorner fi a) enddef ;

<test if>→if<expression booléenne>:<tokenséquilibrés><alternatives>fi

<alternatives>→<vide>

|else :<tokenséquilibrés>

|elseif<expression booléenne>:<tokenséquilibrés><alternatives>

FIG. 41 – La syntaxe des testsif

Le but réel des macros et des testsifest d’automatiser les tâches répétitives et de permettre de résoudre séparément des tâches secondaires importantes. Par exemple, on utilise les macrosdraw_marked,mark_angleetmark_rt_anglepour marquer les lignes et les angles qui apparaissent dans la figure 42.

La tâche de la macrodraw_markedest de tracer un chemin avec un nombre donné de marques à proximité de ses points milieux. Un point de départ commode du problème est la résolution du sous-problème du tracé d’une seule marque perpendiculaire au cheminppour une valeur quelconque du « temps »t.

La macrodraw_markréalise ceci dans la figure 43 en déterminant d’abord un vecteur dmnormal àpent. Pour simplifier le positionnement du trait, la macrodraw_marked est définie pour prendre une longueur d’arcale long depet d’utiliser l’opérateur arctimepour calculert.

À l’aide de la résolution du sous-problème du tracé d’une seule marque en dehors du chemin, la macrodraw_marked a uniquement besoin de tracer le chemin et d’ap-peler draw_mark avec les valeurs appropriées de la longueur de l’arc. La macro draw_marked, dans la figure 43, utilise n valeurs équidistantes de a centrées sur .5*arclength p.

beginfig(42) ; pair a, b, c, d ;

b=(0,0) ; c=(1.5in,0) ; a=(0,.6in) ; d-c = (a-b) rotated 25 ;

dotlabel.lft("a",a) ; dotlabel.lft("b",b) ; dotlabel.bot("c",c) ; dotlabel.llft("d",d) ; z0=.5[a,d] ;

z1=.5[b,c] ;

(z.p-z0) dotprod (d-a) = 0 ; (z.p-z1) dotprod (c-b) = 0 ; draw a--d ;

draw b--c ; draw z0--z.p--z1 ; draw_marked(a--b, 1) ;

draw_marked(c--d, 1) ; draw_marked(a--z.p, 2) ; draw_marked(d--z.p, 2) ; draw_marked(b--z.p, 3) ; draw_marked(c--z.p, 3) ; mark_angle(z.p, b, a, 1) ; mark_angle(z.p, c, d, 1) ; mark_angle(z.p, c, b, 2) ; mark_angle(c, b, z.p, 2) ; mark_rt_angle(z.p, z0, a) ; mark_angle(z.p, b, z1, b) ; endfig ;

a

b c

d

FIG. 42 – CodeMETAPOSTet la figure correspondante

marksize=4pt ;

def draw_mark(expr p, a) = begingroup

save t, dm ; pair dm ; t = arctime a of p ;

dm = marksize*unitvector direction t of p rotated 90 ;

draw (-.5dm.. .5dm) shifted point t of p ; endgroup

enddef ;

def draw_marked(expr p, n) = begingroup

save amid ;

amid = .5*arclength p ;

for i=-(n-1)/2 upto (n-1)/2 :

draw_mark(p, amid+.6marksize*i) ; endfor

draw p ; endgroup enddef ;

FIG. 43 – Macro pour le tracé d’un cheminpavecnmarques perpendiculaires

angle_radius=8pt ;

def mark_angle(expr a, b, c, n) = begingroup

save s, p ; path p ;

p = unitvector(a-b){(a-b)rotated 90 }..

unitvector(c-b) ;

s = .9marksize/length(point 1 of p - point 0 of p) ; if s<angle_radius : s :=angle_radius ; fi

draw_marked(pscaled s shifted b, n) ; endgroup

enddef ;

def mark_rt_angle(expr a, b, c) = draw ((1,0)--(1,1)--(0,1))

zscaled (angle_radius*unitvector(a-b)) shifted b enddef ;

FIG. 44 – Macro pour le marquage des angles

Puisque la macrodraw_markedfonctionne pour des courbes, elle peut être utilisée pour tracer des arcs que la macromark_anglecrée. Soient les pointsa,betcqui dé-finissent un angle dans le sens trigonométrique enb, la macromark_anglenécessite de créer un petit arcpreliant le segmentbaau segmentbc. La définition de la macro de la figure 44 fait ceci en créant un arcpde rayon unitaire et ensuite en calculant un facteur d’échellesqui le rend suffisamment grand pour être vu clairement.

La macromark_rt_angleest beaucoup plus simple. Elle considère un coin d’angle droit et utilise l’opérateurzscaled pour le tourner et le mettre à l’échelle comme nécessaire.