1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
|
@c -*- coding: utf-8; mode: texinfo; documentlanguage: fr -*-
@ignore
Translation of GIT committish: 39912f861693f1c24b8833e6e9e6ba82eb3e6746
When revising a translation, copy the HEAD committish of the
version that you are working on. For details, see the Contributors'
Guide, node Updating translation committishes..
@end ignore
@c \version "2.19.22"
@c Translators: Jean-Charles Malahieude
@node Tutoriel Scheme
@chapter Tutoriel Scheme
@cindex Scheme
@cindex GUILE
@cindex Scheme, inclusion de code
@cindex accéder à Scheme
@cindex évaluation Scheme
@cindex LISP
LilyPond recourt abondamment au langage de programmation Scheme, tant au
niveau de la syntaxe de saisie que des mécanismes internes chargés de
combiner les différents modules du logiciel. Les lignes qui suivent
constituent un bref aperçu de la manière de saisir des données en
Scheme. Si vous désirez en apprendre plus sur Scheme, n'hésitez pas à
vous rendre sur @uref{http://@/www@/.schemers@/.org}.
Le Scheme utilisé par LilyPond repose sur l'implémentation GNU
Guile ; celle-ci se base sur le standard Scheme « R5RS ». Si
votre but est d'apprendre Scheme au travers de LilyPond, sachez que
l'utilisation d'une autre implémentation ou d'un autre standard pourrait
être source de désagrément. Vous trouverez plus d'information sur
Guile à la page @uref{http://www.gnu.org/software/guile/} ; le
standard Scheme « R5RS » est quant à lui disponible à la page
@uref{http://www.schemers.org/Documents/Standards/R5RS/}.
@menu
* Introduction à Scheme::
* Scheme et LilyPond::
* Construction de fonctions complexes::
@end menu
@node Introduction à Scheme
@section Introduction à Scheme
@translationof Introduction to Scheme
Nous commencerons par nous intéresser à Scheme et à son fonctionnement,
grâce à l'interpréteur Guile. Une fois plus à l'aise avec Scheme, nous
verrons comment ce langage peut s'intégrer à un fichier LilyPond.
@menu
* Le bac à sable de Scheme::
* Scheme et les variables::
* Types de données Scheme simples::
* Types de données Scheme composites::
* Scheme et les calculs::
* Scheme et les procédures::
* Scheme et les conditions::
@end menu
@node Le bac à sable de Scheme
@subsection Le bac à sable de Scheme
@translationof Scheme sandbox
L'installation de LilyPond comprend l'implémentation Guile de Scheme.
La plupart des systèmes disposent d'un « bac à sable » Scheme pour
effectuer des tests ; vous y accéderez en tapant @code{guile} dans
un terminal. Certains systèmes, notamment Windows, nécessitent d'avoir
auparavant créé la variable d'environnement @code{GUILE_LOAD_PATH} qui
devra pointer vers le répertoire @code{../usr/share/guile/1.8} de
l'installation de LilyPond -- pour connaître le chemin complet d'accès à
ce répertoire, consultez @rlearning{Autres sources de documentation}.
Les utilisateurs de Windows peuvent aussi prendre l'option « Exécuter »
à partir du menu « Démarrer » puis taper @code{guile}.
Néanmoins, tous les paquetages de LilyPond disposent d'un bac à sable
Scheme, accessible par la commande :
@example
lilypond scheme-sandbox
@end example
@noindent
Une fois le bac à sable actif, vous obtiendrez l'invite :
@lisp
guile>
@end lisp
Vous pouvez dès à présent saisir des expressions Scheme pour vous
exercer. Si vous souhaitez pouvoir utiliser la bibliothèque GNU
@code{readline}, qui offre une ligne de commande plus élaborée,
consultez les informations contenues dans le fichier
@file{ly/scheme-sandbox.ly}. La bibliothèque @var{readline}, dans la
mesure où elle est habituellement activée dans vos sessions Guile,
devrait être effective y compris dans le bac à sable.
@node Scheme et les variables
@subsection Scheme et les variables
@translationof Scheme variables
Une variable Scheme peut contenir n'importe quelle valeur valide en
Scheme, y compris une procédure Scheme.
Une variable Scheme se crée avec la fonction @code{define} :
@lisp
guile> (define a 2)
guile>
@end lisp
L'évaluation d'une variable Scheme se réalise en saisissant le nom de
cette variable à l'invite de Guile :
@lisp
guile> a
2
guile>
@end lisp
Une variable Scheme s'affiche à l'écran à l'aide de la fonction
@code{display} :
@lisp
guile> (display a)
2guile>
@end lisp
@noindent
Vous aurez remarqué que la valeur @code{2} et l'invite @code{guile}
apparaissent sur une même ligne. On peut améliorer la présentation à
l'aide de la procédure @code{newline} ou bien en affichant un caractère
« retour chariot ».
@lisp
guile> (display a)(newline)
2
guile> (display a)(display "\n")
2
guile>
@end lisp
Après avoir créé une variable, vous pouvez en modifier la valeur grâce à
un @code{set!} :
@lisp
guile> (set! a 12345)
guile> a
12345
guile>
@end lisp
Vous quitterez proprement le bac à sable à l'aide de l'instruction
@code{quit} :
@lisp
guile> (quit)
@end lisp
@node Types de données Scheme simples
@subsection Types de données Scheme simples
@translationof Scheme simple data types
L'un des concepts de base de tout langage est la saisie de données,
qu'il s'agisse de nombres, de chaînes de caractères, de listes, etc.
Voici les différents types de données Scheme simples utilisées
couramment dans LilyPond.
@table @asis
@item Booléens
Les valeurs booléennes sont vrai ou faux. En Scheme, ce sera @code{#t}
pour vrai, et @code{#f} pour faux.
@funindex ##t
@funindex ##f
@item Nombres
Les nombres se saisissent le plus communément : @code{1} est le
nombre (entier) un, alors que @w{@code{-1.5}} est un nombre à virgule
flottante (un nombre non entier).
@item Chaînes
Les chaînes de caractères sont bornées par des guillemets
informatiques :
@example
"ceci est une chaîne"
@end example
Une chaîne peut s'étendre sur plusieurs lignes :
@example
"ceci
est
une chaîne"
@end example
@noindent
auquel cas les retours à la ligne seront inclus dans la chaîne.
Un caractère de retour à la ligne peut s'ajouter dans la chaîne, sous la
forme d'un @code{\n}.
@example
"ceci\nest une\nchaîne multiligne"
@end example
Guillemets et obliques inverses dans une chaîne doivent être précédés
d'une oblique inverse. La chaîne @code{\a dit "b"} se saisit donc
@example
"\\a dit \"b\""
@end example
@end table
Il existe bien d'autres types de données Scheme, dont nous ne parlerons
pas ici. Vous en trouverez une liste exhaustive dans le guide de
référence de Guile, à la page
@uref{http://www.gnu.org/software/guile/manual/html_node/Simple-Data-Types.html}.
@node Types de données Scheme composites
@subsection Types de données Scheme composites
@translationof Scheme compound data types
Scheme prend aussi en charge des types de données composites. LilyPond
utilise beaucoup les paires, listes, listes associatives et tables de
hachage.
@menu
* Paires::
* Listes::
* Listes associatives (alists)::
* Tables de hachage::
@end menu
@node Paires
@unnumberedsubsubsec Paires
@translationof Pairs
Le type de donnée composite fondamental est la paire (@code{pair}).
Comme son nom l'indique, il s'agit de lier deux valeurs, à l'aide de
l'opérateur @code{cons}.
@lisp
guile> (cons 4 5)
(4 . 5)
guile>
@end lisp
Vous aurez noté que la paire s'affiche sous la forme de deux éléments
bornés par des parenthèses et séparés par une espace, un point
(@code{.}) et une autre espace. Le point n'est en aucune manière un
séparateur décimal ; il s'agit de l'indicateur d'une paire.
Vous pouvez aussi saisir littéralement les valeurs d'une paire, en la
faisant précéder d'une apostrophe.
@lisp
guile> '(4 . 5)
(4 . 5)
guile>
@end lisp
Les deux éléments d'une paire peuvent être constitués de n'importe
quelle valeur Scheme valide :
@lisp
guile> (cons #t #f)
(#t . #f)
guile> '("blah-blah" . 3.1415926535)
("blah-blah" . 3.1415926535)
guile>
@end lisp
Les premier et second éléments de la paire sont accessibles à l'aide des
procédures Scheme @code{car} et @code{cdr}.
@lisp
guile> (define mypair (cons 123 "hello there")
@dots{} )
guile> (car mypair)
123
guile> (cdr mypair)
"hello there"
guile>
@end lisp
@noindent
Note : @code{cdr} se prononce « couldeur », comme l'indiquent Sussman et
Abelson -- voir
@uref{http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133}.
@node Listes
@unnumberedsubsubsec Listes
@translationof Lists
Autre structure de donnée commune en Scheme : la liste (@emph{list}).
Une liste « correcte » se définit comme étant vide (représentée par
@code{'()} et de longueur 0) ou une paire dont le @code{cdr} est une
liste.
Il existe plusieurs méthodes pour créer une liste, la plus courante
étant l'utilisation de la procédure @code{list} :
@lisp
guile> (list 1 2 3 "abc" 17.5)
(1 2 3 "abc" 17.5)
@end lisp
La représentation d'une liste par la succession de ses éléments, séparés
par des espaces, bornée par des parenthèses, n'est en fait qu'une vue
compacte des paires qui la constituent. Les paires sont ainsi
dépourvues du point de séparation et de la parenthèse ouvrante qui le
suit et des parenthèses fermantes. Sans ce « compactage », cette liste
serait ainsi présentée :
@lisp
(1 . (2 . (3 . ("abc" . (17.5 . ())))))
@end lisp
Vous pouvez donc saisir une liste comme elle serrait présentée, en
entourant ses éléments par des parenthèses à la suite d'une apostrophe
(afin que ce qui suit ne soit pas interprété comme un appel à une
fonction) :
@lisp
guile> '(17 23 "foo" "bar" "bazzle")
(17 23 "foo" "bar" "bazzle")
@end lisp
Les listes ont une importance considérable en Scheme. Certains vont
d'ailleurs jusqu'à considérer Scheme comme un dialecte du lisp, où
« lisp » serait une abréviation de « List Processing ». Il est vrai
que toute expression Scheme est une liste.
@node Listes associatives (alists)
@unnumberedsubsubsec Listes associatives (alists)
@translationof Association lists (alists)
Il existe un type particulier de liste : la @emph{liste associative} --
ou @emph{alist}. Une @emph{alist} permet de stocker des données dans le
but de les réutiliser.
Une liste associative est une liste dont les éléments sont des paires.
Le @code{car} de chacun des éléments constitue une clé (@emph{key}) et
chaque @code{cdr} une valeur (@emph{value}). La procédure Scheme
@code{assoc} permet de retrouver une entrée de la liste
associative ; son @code{cdr} en fournira la valeur :
@lisp
guile> (define mon-alist '((1 . "A") (2 . "B") (3 . "C")))
guile> mon-alist
((1 . "A") (2 . "B") (3 . "C"))
guile> (assoc 2 mon-alist)
(2 . "B")
guile> (cdr (assoc 2 mon-alist))
"B"
guile>
@end lisp
LilyPond recourt abondamment aux @emph{alists} pour stocker des
propriétés ou autres données.
@node Tables de hachage
@unnumberedsubsubsec Tables de hachage
@translationof Hash tables
Il s'agit d'une structure de données à laquelle LilyPond fait parfois
appel. Une table de hachage (@emph{hash table}) peut se comparer à une
matrice ou un tableau dont l'index peut être n'importe quel type de
valeur Scheme et ne se limitant pas à des nombres entiers.
Les tables de hachage sont un moyen plus efficace que les listes
associatives lorsqu'il s'agit d'enregistrer de nombreuses données qui ne
changeront que peu fréquemment.
La syntaxe permettant de créer une table de hachage peut paraître
complexe, mais vous en trouverez de nombreux exemples dans les sources
de LilyPond.
@lisp
guile> (define h (make-hash-table 10))
guile> h
#<hash-table 0/31>
guile> (hashq-set! h 'cle1 "valeur1")
"valeur1"
guile> (hashq-set! h 'key2 "valeur2")
"valeur2"
guile> (hashq-set! h 3 "valeur3")
"valeur3"
@end lisp
La procédure @code{hashq-ref} permet de récupérer une valeur dans la
table de hachage.
@lisp
guile> (hashq-ref h 3)
"valeur3"
guile> (hashq-ref h 'cle2)
"valeur2"
guile>
@end lisp
La procédure @code{hashq-get-handle} permet de retrouver à la fois une
clé et sa valeur. Cette procédure a l'avantage de renvoyer @code{#f}
lorsque la clé n'existe pas.
@lisp
guile> (hashq-get-handle h 'cle1)
(cle1 . "valeur1")
guile> (hashq-get-handle h 'zut)
#f
guile>
@end lisp
@node Scheme et les calculs
@subsection Scheme et les calculs
@translationof Calculations in Scheme
@ignore
We have been using lists all along. A calculation, like @code{(+ 1 2)}
is also a list (containing the symbol @code{+} and the numbers 1
and@tie{}2). Normally lists are interpreted as calculations, and the
Scheme interpreter substitutes the outcome of the calculation. To enter a
list, we stop the evaluation. This is done by quoting the list with a
quote @code{'} symbol. So, for calculations do not use a quote.
Inside a quoted list or pair, there is no need to quote anymore. The
following is a pair of symbols, a list of symbols and a list of lists
respectively,
@example
#'(stem . head)
#'(staff clef key-signature)
#'((1) (2))
@end example
@end ignore
Scheme permet aussi d'effectuer des calculs. Il utilise alors un
@emph{préfixe}. Additionner 1 et 2 s'écrira @code{(+ 1 2)} et
non @math{1+2} comme on aurait pu s'y attendre.
@lisp
guile> (+ 1 2)
3
@end lisp
Les calculs peuvent s'imbriquer ; le résultat d'une fonction peut
servir pour un autre calcul.
@lisp
guile> (+ 1 (* 3 4))
13
@end lisp
Ces calculs sont un exemple d'évaluation : une expression telle que
@code{(* 3 4)} est remplacée par sa valeur, soit @code{12}.
En matière de calcul, Scheme fait la différence entre des nombres entiers
ou non. Les calculs sur des nombres entiers seront exacts, alors que
s'il s'agit de nombres non entiers, les calculs tiendront compte de la
précision mentionnée :
@lisp
guile> (/ 7 3)
7/3
guile> (/ 7.0 3.0)
2.33333333333333
@end lisp
Lorsque l'interpréteur Scheme rencontre une expression sous forme de
liste, le premier élément de cette liste est considéré en tant que
procédure qui prendra en argument le restant de la liste. C'est la
raison pour laquelle, en Scheme, tous les opérateurs sont en préfixe.
Le fait que le premier élément d'une expression Scheme sous forme de
liste ne soit pas un opérateur ou une procédure déclenchera une
erreur de la part de l'interpréteur :
@lisp
guile> (1 2 3)
Backtrace:
In current input:
52: 0* [1 2 3]
<unnamed port>:52:1: In expression (1 2 3):
<unnamed port>:52:1: Wrong type to apply: 1
ABORT: (misc-error)
guile>
@end lisp
Vous pouvez constater que l'interpréteur a tenté de considérer @code{1}
comme étant un opérateur ou une procédure, ce qu'il n'a pu réaliser. Il
a donc renvoyé l'erreur « Wrong type to apply: 1 » (@emph{Application
d'un type erroné : 1}).
C'est pourquoi il est impératif, pour créer une liste, soit d'utiliser
l'opérateur consacré (@code{list}), soit de faire précéder la liste
d'une apostrophe, de telle sorte que l'interpréteur ne tente pas de
l'évaluer.
@lisp
guile> (list 1 2 3)
(1 2 3)
guile> '(1 2 3)
(1 2 3)
guile>
@end lisp
Vous pourrez être confronté à cette erreur lorsque vous intégrerez
Scheme à LilyPond.
@ignore
The same assignment can be done in completely in Scheme as well,
@example
#(define twentyFour (* 2 twelve))
@end example
@c this next section is confusing -- need to rewrite
The @emph{name} of a variable is also an expression, similar to a
number or a string. It is entered as
@example
#'twentyFour
@end example
@funindex #'symbol
@cindex quoting in Scheme
The quote mark @code{'} prevents the Scheme interpreter from substituting
@code{24} for the @code{twentyFour}. Instead, we get the name
@code{twentyFour}.
@end ignore
@node Scheme et les procédures
@subsection Scheme et les procédures
@translationof Scheme procedures
Une procédure Scheme est une expression Scheme qui renverra une valeur
issue de son exécution. Les procédures Scheme sont capables de
manipuler des variables qui ne sont pas définies en leur sein.
@menu
* Définition de procédures::
* Prédicats::
* Valeurs de retour::
@end menu
@node Définition de procédures
@unnumberedsubsubsec Définition de procédures
@translationof Defining procedures
En Scheme, on définit une procédure à l'aide de l'instruction
@code{define} :
@example
(define (nom-fonction argument1 argument2@dots{} argumentn)
expression-scheme-qui-donnera-une-valeur-en-retour)
@end example
Nous pourrions, par exemple, définir une procédure calculant la moyenne
de deux nombres :
@lisp
guile> (define (moyenne x y) (/ (+ x y) 2))
guile> moyenne
#<procedure moyenne (x y)>
@end lisp
Une fois la procédure définie, on l'appelle en la faisant suivre, dans
une liste, des arguments qui doivent l'accompagner. Calculons
maintenant la moyenne de 3 et 12 :
@lisp
guile> (moyenne 3 12)
15/2
@end lisp
@node Prédicats
@unnumberedsubsubsec Prédicats
@translationof Predicates
Une procédure Scheme chargée de retourner une valeur booléenne s'appelle
un « prédicat » (@emph{predicate}). Par convention, plutôt que par
nécessité, le nom d'un prédicat se termine par un point
d'interrogation :
@lisp
guile> (define (moins-de-dix? x) (< x 10))
guile> (moins-de-dix? 9)
#t
guile> (moins-de-dix? 15)
#f
@end lisp
@node Valeurs de retour
@unnumberedsubsubsec Valeurs de retour
@translationof Return values
Une procédure Scheme doit toujours renvoyer une valeur de retour, en
l'occurrence la valeur de la dernière expression exécutée par cette
procédure. La valeur de retour sera une valeur Scheme valide, y compris
une structure de donnée complexe ou une procédure.
On peut avoir besoin de regrouper plusieurs expressions Scheme dans une
même procédure. Deux méthodes permettent de combiner des expressions
multiples. La première consiste à utiliser la procédure @code{begin},
qui permet l'évaluation de plusieurs expressions et renvoie la valeur de
la dernière expression.
@lisp
guile> (begin (+ 1 2) (- 5 8) (* 2 2))
4
@end lisp
Une deuxième méthode consiste à combiner les expressions dans un bloc
@code{let}. Ceci aura pour effet de créer une série de liens, puis
d'évaluer en séquence les expressions susceptibles d'inclure ces
liens. La valeur renvoyée par un bloc @emph{let} est la valeur de
retour de la dernière clause de ce bloc :
@lisp
guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4))
@dots{} (+ (* x y) (/ z x)))
508
@end lisp
@node Scheme et les conditions
@subsection Scheme et les conditions
@translationof Scheme conditionals
@menu
* if::
* cond::
@end menu
@node if
@unnumberedsubsubsec if
@translationof if
Scheme dispose d'une procédure @code{if} :
@example
(if expression-test expression-affirmative expression-négative)
@end example
@var{expression-test} est une expression qui renverra une valeur
booléenne. Dans le cas où @var{expression-test} retourne @code{#t}, la
procédure @code{if} renvoie la valeur de @var{expression-affirmative},
et celle de @var{expression-négative} dans le cas contraire.
@lisp
guile> (define a 3)
guile> (define b 5)
guile> (if (> a b) "a est plus grand que b" "a n'est pas plus grand que b")
"a n'est pas plus grand que b"
@end lisp
@node cond
@unnumberedsubsubsec cond
@translationof cond
Une autre manière d'introduire une condition en Scheme est d'utiliser
l'instruction @code{cond} :
@example
(cond (expression-test-1 expression-résultat-séquence-1)
(expression-test-2 expression-résultat-séquence-2)
@dots{}
(expression-test-n expression-résultat-séquence-n))
@end example
Comme par exemple ici :
@lisp
guile> (define a 6)
guile> (define b 8)
guile> (cond ((< a b) "a est plus petit que b")
@dots{} ((= a b) "a égale b")
@dots{} ((> a b) "a est plus grand que b"))
"a est plus petit que b"
@end lisp
@node Scheme et LilyPond
@section Scheme et LilyPond
@translationof Scheme in LilyPond
@menu
* Syntaxe Scheme dans LilyPond::
* Variables LilyPond::
* Saisie de variables et Scheme::
* Import de code Scheme dans LilyPond::
* Propriétés des objets::
* Variables LilyPond composites::
* Représentation interne de la musique::
@end menu
@node Syntaxe Scheme dans LilyPond
@subsection Syntaxe Scheme dans LilyPond
@translationof LilyPond Scheme syntax
@funindex $
@funindex #
L'installation de LilyPond comprenant l'interpréteur Guile, les fichiers
source LilyPond peuvent contenir du Scheme. Vous disposez de plusieurs
méthodes pour inclure du Scheme dans vos fichiers LilyPond.
La méthode la plus simple consiste à insérer un @emph{hash} (le caractère
@code{#}, improprement appelé dièse) avant l'expression Scheme.
Rappelons-nous qu'un fichier source LilyPond est structuré en jetons et
expressions, tout comme le langage humain est structuré en mots et
phrases. LilyPond dispose d'un analyseur lexical (appelé @emph{lexer})
qui sait identifier les jetons -- nombres, chaînes, éléments Scheme,
hauteurs, etc. -- ainsi que d'un analyseur syntaxique (appelé
@emph{parser}) -- voir
@rcontribnamed{LilyPond grammar, Grammaire de LilyPond}. Dès lors
que le programme sait quelle règle grammaticale particulière doit
s'appliquer, il exécute les consignes qui lui sont associées.
Le recours à un @emph{hash} pour mettre en exergue du Scheme est tout à
fait approprié. Dès qu'il rencontre un @code{#}, l'analyseur lexical
passe le relais au lecteur Scheme qui va alors déchiffrer l'intégralité
de l'expression Scheme -- ce peut être un identificateur, une expression
bornée par des parenthèses ou bien d'autres choses encore. Une fois
cette expression lue, elle est enregistrée en tant que valeur d'un
élément grammatical @code{SCM_TOKEN}. Puisque l'analyseur syntaxique
sait comment traiter ce jeton, il charge Guile d'évaluer l'expression
Scheme. Dans la mesure où le @emph{parser} requiert une lecture en
avance de la part du @emph{lexer} pour prendre une décision, cette
distinction entre lecture et évaluation -- @emph{lexer} et @emph{parser}
-- révèle toute sa pertinence lorsqu'il s'agit d'exécuter conjointement
des expressions LilyPond et des expressions Scheme. C'est la raison
pour laquelle nous vous recommandons, dans toute la mesure du possible,
d'utiliser un signe @emph{hash} lorsque vous faites appel à Scheme.
Une autre manière de faire appel à l'interpréteur Scheme à partir de
LilyPond consiste à introduire une expression Scheme par un caractère
dollar au lieu d'un caractère dièse -- un @code{$} au lieu
d'un @code{#}. En pareil cas, LilyPond évalue le code dès sa lecture
par l'analyseur lexical, vérifie le type d'expression Scheme qui en
résulte et détermine un type de jeton (l'un des @code{xxx_IDENTIFIER} de
la grammaire) qui lui corresponde, puis en fait une copie qui servira à
traiter la valeur de ce jeton. Lorsque la valeur de l'expression est
@emph{void}, autrement dit une valeur Guile @code{*unspecified*} (pour
@emph{non spécifiée}), aucune information n'est transmise à l'analyseur
grammatical.
C'est, en réalité, la manière dont LilyPond opère lorsque vous rappelez
une variable ou une fonction par son nom -- au travers d'un @code{\nom}
--, à la seule différence que sa finalité est déterminée par l'analyseur
lexical de LilyPond sans consultation du lecteur Scheme ; le nom de
la variable rappelée doit donc être en corrélation avec le mode LilyPond
actif à ce moment là.
L'immédiateté de l'opérateur @code{$} peut entraîner des effets
indésirables dont nous reparlerons à la rubrique
@ref{Saisie de variables et Scheme} ; aussi est-il préférable
d'utiliser un @code{#} dès que l'analyseur grammatical le supporte.
Dans le cadre d'une expression musicale, une expression qui aura été
créée à l'aide d'un @code{#} sera interprétée comme étant de la musique.
Elle ne sera cependant pas recopiée avant utilisation. Si la structure
qui l'abrite devait être réutilisée, un appel expicite à
@code{ly:music-deep-copy} pourrait être requis.
@funindex $@@
@funindex #@@
Les opérateurs @code{$@@} et @code{#@@} agissent comme des « colleurs de
liste » : leur fonction consiste à insérer tous les éléments d'une liste
dans le contexte environnant.
Examinons à présent du vrai code Scheme. Nous pouvons définir des
procédures Scheme au milieu d'un fichier source LilyPond :
@example
#(define (moyenne a b c) (/ (+ a b c) 3))
@end example
Pour mémoire, vous noterez que les commentaires LilyPond (@code{%} ou
@code{%@{…%@}}) ne peuvent s'utiliser dans du code Scheme, même si
celui-ci se trouve au sein d'un fichier LilyPond. Ceci tient au fait
que l'expression Scheme est lue par l'interpréteur Guile, et en aucune
façon par l'analyseur lexical de LilyPond. Voici comment introduire des
commentaires dans votre code Scheme :
@example
; ceci n'est qu'une simple ligne de commentaire
#!
Ceci constitue un bloc de commentaire (non imbricable)
dans le style Guile.
En fait, les Schemeurs les utilisent très rarement,
et vous n'en trouverez jamais dans le code source
de LilyPond.
!#
@end example
Dans la suite de notre propos, nous partons du principe que les données
sont incluses dans un fichier musical, aussi toutes les expressions
Scheme seront introduites par un @code{#}.
Toutes les expressions Scheme de haut niveau incluses dans un fichier
LilyPond peuvent se combiner en une expression Scheme unique à l'aide de
la clause @code{begin} :
@example
#(begin
(define foo 0)
(define bar 1))
@end example
@node Variables LilyPond
@subsection Variables LilyPond
@translationof LilyPond variables
Les variables LilyPond sont enregistrées en interne sous la forme de
variables Scheme. Ainsi,
@example
douze = 12
@end example
@noindent
est équivalant à
@example
#(define douze 12)
@end example
Ceci a pour conséquence que toute variable LilyPond peut être utilisée
dans une expression Scheme. Par exemple, nous pourrions dire
@example
vingtQuatre = #(* 2 douze)
@end example
@noindent
ce qui aurait pour conséquence que le nombre @emph{24} sera stocké dans
la variable LilyPond (et Scheme) @code{vingtQuatre}.
Scheme autorise la modification d'expressions complexes au fil de l'eau,
ce que réalise LilyPond dans le cadre des fonctions musicales.
Toutefois, lorsqu'une expression musicale est stockée dans une variable,
plutôt que saisie au fur et à mesure, on s'attend, alors qu'elle est
passée à une fonction musicale, à ce que sa valeur originale ne soit en
rien modifiée. C'est la raison pour laquelle faire référence à une
variable à l'aide d'une oblique inverse -- autrement dit saisir
@code{\vingtQuatre} -- aura pour effet que LilyPond créera une copie de
la valeur musicale de cette variable aux fins de l'utiliser au sein de
l'expression musicale au lieu d'utiliser directement la valeur de cette
variable.
Par voie de conséquence, une expression musicale introduite par @code{#}
ne devrait pas contenir de matériau inexistant auparavant ou bien
littéralement recopié, mais plutôt une référence explicite.
@seealso
Manuel d'extension :
@ref{Syntaxe Scheme dans LilyPond}.
@node Saisie de variables et Scheme
@subsection Saisie de variables et Scheme
@translationof Input variables and Scheme
Le format de saisie prend en charge la notion de variable -- ou
identificateur. Dans l'exemple suivant, une expression musicale se voit
attribuer un identificateur qui portera le nom de @code{traLaLa}.
@example
traLaLa = @{ c'4 d'4 @}
@end example
@noindent
Une variable a aussi une portée. Dans l'exemple suivant, le bloc
@code{\layout} contient une variable @code{traLaLa} tout à fait
indépendante de l'autre @code{\traLaLa}.
@example
traLaLa = @{ c'4 d'4 @}
\layout @{ traLaLa = 1.0 @}
@end example
Dans les faits, chaque fichier a un domaine de compétence, et les
différents blocs @code{\header}, @code{\midi} et @code{\layout} ont leur
propre champ de compétence, imbriqué dans ce domaine principal.
Variables et champs de compétence sont implémentés par le système de
modules de Guile. Un module anonyme Scheme est attaché à chacun de ces
domaines. Une assertion telle que
@example
traLaLa = @{ c'4 d'4 @}
@end example
@noindent
est convertie, en interne, en une définition Scheme :
@example
(define traLaLa @var{valeur Scheme de `@code{@dots{}}'})
@end example
Cela signifie que variables LilyPond et variables Scheme peuvent tout à
fait se mélanger. Dans l'exemple suivant, un fragment musical est
stocké dans la variable @code{traLaLa} puis dupliqué à l'aide de Scheme.
Le résultat est alors importé dans un bloc @code{\score} au moyen d'une
seconde variable @code{twice}.
@lilypond[verbatim]
traLaLa = { c'4 d'4 }
#(define newLa (map ly:music-deep-copy
(list traLaLa traLaLa)))
#(define twice
(make-sequential-music newLa))
\twice
@end lilypond
@c Due to parser lookahead
Cet exemple est particulièrement intéressant. L'assignation
n'interviendra qu'une fois que l'analyseur grammatical aura l'assurance
que rien du type de @code{\addlyrics} ne suit ; il doit donc
vérifier ce qui vient après. Le @emph{parser} lit le @code{#} et
l'expression Scheme qui le suit @strong{sans} l'évaluer, de telle sorte
qu'il peut procéder à l'assignation, et @strong{ensuite} exécuter le
code Scheme sans problème.
@node Import de code Scheme dans LilyPond
@subsection Import de code Scheme dans LilyPond
@translationof Importing Scheme in LilyPond
@funindex $
@funindex #
L'exemple précédent illustre la manière « d'exporter » une expression
musicale à partir des saisies et à destination de l'interpréteur Scheme.
L'inverse est aussi réalisable : en la plaçant derrière un @code{$}, une
valeur Scheme sera interprétée comme si elle avait été saisie en
syntaxe LilyPond. Au lieu de définir @code{\twice}, nous aurions tout
aussi bien pu écrire
@example
@dots{}
$(make-sequential-music (list newLa))
@end example
Vous pouvez utiliser @code{$} suivi d'une expression Scheme partout où
vous auriez utilisé @code{\@var{nom}}, dès lors que vous aurez assigné à
cette expression Scheme le nom de variable @var{nom}. La substitution
intervenant au niveau de l'analyseur lexical (le @emph{lexer}), LilyPond
ne saurait faire la différence.
Cette manière de procéder comporte cependant un inconvénient au niveau
de la temporisation. Si nous avions défini @code{newLa} avec un
@code{$} plutôt qu'un @code{#}, la définition Scheme suivante aurait
échoué du fait que @code{traLaLa} n'était pas encore défini. Pour plus
d'information quant au problème de synchronisation, voir la rubrique
@ref{Syntaxe Scheme dans LilyPond}.
@funindex $@@
@funindex #@@
Une autre façon de procéder serait de recourir aux « colleurs de liste »
@code{$@@} et @code{#@@} dont la fonction est d'insérer les éléments
d'une liste dans le contexte environnant. Grâce à ces opérateurs, la
dernière partie de notre fonction pourrait s'écrire ainsi :
@example
@dots{}
@{ #@@newLa @}
@end example
Ici, chaque élément de la liste stockée dans @code{newLa} est pris à
son tour et inséré dans la liste, tout comme si nous avions écrit
@example
@{ #(premier newLa) #(deuxième newLa) @}
@end example
Dans ces deux dernières formes, le code Scheme est évalué alors même que
le code initial est en cours de traitement, que ce soit par le
@emph{lexer} ou par le @emph{parser}. Si le code Scheme ne doit être
exécuté que plus tard, consultez la rubrique
@ref{Fonctions Scheme fantômes}, ou stockez le dans une procédure comme
ici :
@example
#(define (nopc)
(ly:set-option 'point-and-click #f))
@dots{}
#(nopc)
@{ c'4 @}
@end example
@knownissues
L'imbrication de variables Scheme et LilyPond n'est pas possible
avec l'option @option{--safe}.
@node Propriétés des objets
@subsection Propriétés des objets
@translationof Object properties
Les propriétés des objets sont stockées dans LilyPond sous la forme
d'enchaînements de listes associatives, autrement dit des listes de
listes associatives. Une propriété se détermine par l'ajout de valeurs
en début de liste de cette propriété. Les caractéristiques d'une
propriété s'ajustent donc à la lecture des différentes valeurs des
listes associatives.
La modification d'une valeur pour une propriété donnée requiert
l'assignation d'une valeur de la liste associative, tant pour la clé que
pour la valeur associée. Voici comment procéder selon la syntaxe de
LilyPond :
@example
\override Stem.thickness = #2.6
@end example
Cette instruction ajuste l'apparence des hampes. Une entrée
@w{@code{'(thickness . 2.6)}} de la @emph{alist} est ajoutée à la liste
de la propriété de l'objet @code{Stem}. @code{thickness} devant
s'exprimer en unité d'épaisseur de ligne, les hampes auront donc une
épaisseur de 2,6 ligne de portée, et à peu près le double de leur
épaisseur normale. Afin de faire la distinction entre les variables que
vous définissez au fil de vos fichiers -- tel le @code{vingtQuatre} que
nous avons vu plus haut -- et les variables internes des objets, nous
parlerons de « propriétés » pour ces dernières, et de « variables »
pour les autres. Ainsi, l'objet hampe possède une propriété
@code{thickness}, alors que @code{vingtQuatre} est une variable.
@cindex propriétés ou variables
@cindex variables ou propriétés
@c todo -- here we're getting interesting. We're now introducing
@c LilyPond variable types. I think this deserves a section all
@c its own
@node Variables LilyPond composites
@subsection Variables LilyPond composites
@translationof LilyPond compound variables
@menu
* Décalages (offsets)::
* Fractions::
* Étendues (extents)::
* Propriété en alists::
* Chaînes d'alist::
@end menu
@node Décalages (offsets)
@unnumberedsubsubsec Décalages (@emph{offsets})
@translationof Offsets
Les décalages (@emph{offset}) sur deux axes (coordonnées X et Y) sont
stockés sous forme de @emph{paires}. Le @code{car} de l'offset
correspond à l'abscisse (coordonnée X) et le @code{cdr} à l'ordonnée
(coordonnée Y).
@example
\override TextScript.extra-offset = #'(1 . 2)
@end example
Cette clause affecte la paire @code{(1 . 2)} à la propriété
@code{extra-offset} de l'objet @code{TextScript}. Ces nombres sont
exprimés en espace de portée. La commande aura donc pour effet de
déplacer l'objet d'un espace de portée vers la droite, et de deux
espaces vers le haut.
Les procédures permettant de manipuler les offsets sont regroupées dans
le fichier @file{scm/lily-library.scm}.
@node Fractions
@unnumberedsubsubsec Fractions
@translationof Fractions
Les fractions, telles que LilyPond les utilise, sont aussi stockées sous
forme de @emph{paire}. Alors que Scheme est tout à fait capable de
représenter des nombres rationnels, vous conviendrez que, musicalement
parlant, @samp{2/4} et @samp{1/2} ne se valent pas ; nous devrons
donc pouvoir les distinguer. Dans le même ordre d'idée, LilyPond ne
connaît pas les « fractions » négatives. Pour ces raisons, @code{2/4}
en LilyPond correspond à @code{(2 . 4)} en Scheme, et @code{#2/4} en
LilyPond correspond à @code{1/2} en Scheme.
@node Étendues (extents)
@unnumberedsubsubsec Étendues (@emph{extents})
@translationof Extents
Les paires permettent aussi de stocker des intervalles qui représentent
un ensemble de nombres compris entre un minimum (le @code{car}) et un
maximum (le @code{cdr}). Ces intervalles stockent l'étendue, tant au
niveau horizontal (X) que vertical (Y) des objets imprimables. En
matière d'étendue sur les X, le @code{car} correspond à la coordonnée de
l'extrémité gauche, et le @code{cdr} à la coordonnée de l'extrémité
droite. En matière d'étendue sur les Y, le @code{car} correspond à la
coordonnée de l'extrémité basse, et le @code{cdr} à la coordonnée de
l'extrémité haute.
Les procédures permettant de manipuler les offsets sont regroupées dans
le fichier @file{scm/lily-library.scm}. Nous vous recommandons
l'utilisation de ces procédures dans toute la mesure du possible afin
d'assurer la cohérence du code.
@node Propriété en alists
@unnumberedsubsubsec Propriété en @emph{alists}
@translationof Property alists
Les propriétés en @emph{alists} sont des structures de données
particulières à LilyPond. Il s'agit de listes associatives dont les
clés sont des propriétés et les valeurs des expressions Scheme
fournissant la valeur requise pour cette propriété.
Les propriétés LilyPond sont des symboles Scheme, à l'instar de
@code{'thickness}.
@node Chaînes d'alist
@unnumberedsubsubsec Chaînes d'@emph{alist}
@translationof Alist chains
Une chaîne d'@emph{alist} est une liste contenant les listes
associatives d'une propriété.
L'intégralité du jeu de propriétés qui doivent s'appliquer à un objet
graphique est en fait stocké en tant que chaîne d'@emph{alist}. Afin
d'obtenir la valeur d'une propriété particulière qu'un objet graphique
devrait avoir, on examinera chacune des listes associatives de la
chaîne, à la recherche d'une entrée contenant la clé de cette propriété.
Est renvoyée la première entrée d'@emph{alist} trouvée, sa valeur étant
la valeur de la propriété.
L'obtention des valeurs de propriété des objets graphiques se réalise en
principe à l'aide de la procédure Scheme @code{chain-assoc-get}.
@node Représentation interne de la musique
@subsection Représentation interne de la musique
@translationof Internal music representation
Dans les entrailles du programme, la musique se présente comme une liste
Scheme. Cette liste comporte les différents éléments qui affecteront la
sortie imprimable. L'analyse grammaticale (l'opération @emph{parsing})
est le processus chargé de convertir la musique représentée par le code
LilyPond en présentation interne Scheme.
L'analyse d'une expression musicale se traduit par un jeu d'objets
musicaux en Scheme. Une objet musical est déterminé par le temps qu'il
occupe, que l'on appelle @emph{durée}. Les durées s'expriment par des
nombres rationnels représentant la longueur d'un objet musical par
rapport à la ronde.
Un objet musical dispose de trois types :
@itemize
@item
un nom de musique : toute expression musicale a un nom. Par exemple,
une note amène à un @rinternals{NoteEvent}, un @code{\simultaneous} à un
@rinternals{SimultaneousMusic}. Une liste exhaustive des différentes
expressions est disponible dans la référence des propriétés internes, à
la rubrique @rinternals{Music expressions}.
@item
un « type » ou interface : tout nom de musique dispose de
plusieurs types ou interfaces. Ainsi, une note est tout à la fois un
@code{event}, un @code{note-event}, un @code{rhythmic-event} et un
@code{melodic-event}. Les différentes classes musicales sont
répertoriées à la rubrique @rinternals{Music classes} de la référence
des propriétés internes.
@item
un objet C++ : tout objet musical est représenté par un objet de la
classe C++ @code{Music}.
@end itemize
L'information réelle d'une expression musicale est enregistrée sous
forme de propriétés. Par exemple, un @rinternals{NoteEvent} dispose des
propriétés @code{pitch} et @code{duration}, respectivement chargées de
stocker la hauteur et la durée de cette note. Les différentes
propriétés sont répertoriées à la rubrique @rinternals{Music properties}
de la référence des propriétés internes.
Une expression composite est un objet musical dont les propriétés
contiennent d'autres objets musicaux. S'il s'agit d'une liste d'objets,
elle sera stockée dans la propriété @code{elements} d'un objet
musical ; s'il n'y a qu'un seul objet « enfant », il sera stocké
dans la propriété @code{element}. Ainsi, par exemple, les enfants de
@rinternals{SequentialMusic} iront dans @code{elements}, alors que
l'argument unique de @rinternals{GraceMusic} ira dans @code{element}.
De même, le corps d'une répétition ira dans la propriété @code{element}
d'un @rinternals{RepeatedMusic}, les alternatives quant à elles dans la
propriété @code{elements}.
@node Construction de fonctions complexes
@section Construction de fonctions complexes
@translationof Building complicated functions
Nous allons voir dans cette partie les moyens dont vous disposez pour
obtenir les informations qui vous permettront de créer vos propres
fonctions musicales complexes.
@menu
* Affichage d'expressions musicales::
* Propriétés musicales::
* Doublement d'une note avec liaison (exemple)::
* Ajout d'articulation à des notes (exemple)::
@end menu
@node Affichage d'expressions musicales
@subsection Affichage d'expressions musicales
@translationof Displaying music expressions
@cindex stockage interne
@cindex expression musicale, affichage
@cindex représentation interne, affichage
@cindex displayMusic
@funindex \displayMusic
Lorsque l'on veut écrire une fonction musicale, il est intéressant
d'examiner comment une expression musicale est représentée en interne.
Vous disposez à cet effet de la fonction musicale @code{\displayMusic}.
@example
@{
\displayMusic @{ c'4\f @}
@}
@end example
@noindent
affichera
@example
(make-music
'SequentialMusic
'elements
(list (make-music
'NoteEvent
'articulations
(list (make-music
'AbsoluteDynamicEvent
'text
"f"))
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 0 0))))
@end example
Par défaut, LilyPond affichera ces messages sur la console, parmi toutes
les autres informations. Vous pouvez, afin de les isoler et de garder
le résultat des commandes @code{\display@{TRUC@}}, spécifier un
port optionnel à utiliser pour la sortie :
@example
@{
\displayMusic #(open-output-file "display.txt") @{ c'4\f @}
@}
@end example
Ceci aura pour effet d'écraser tout fichier précédemment généré.
Lorsque plusieurs expressions doivent être retranscrites, il suffit de
faire appel à une variable pour le port puis de la réutiliser :
@example
@{
port = #(open-output-file "display.txt")
\displayMusic \port @{ c'4\f @}
\displayMusic \port @{ d'4 @}
#(close-output-port port)
@}
@end example
La documentation de Guile fournit une description détaillée des ports.
Clôturer un port n'est requis que si vous désirez consulter le fichier
avant que LilyPond n'ait fini, ce dont nous ne nous sommes pas préoccupé
dans le premier exemple.
L'information sera encore plus lisible après un peu de mise en forme :
@example
(make-music 'SequentialMusic
'elements (list
(make-music 'NoteEvent
'articulations (list
(make-music 'AbsoluteDynamicEvent
'text
"f"))
'duration (ly:make-duration 2 0 1/1)
'pitch (ly:make-pitch 0 0 0))))
@end example
Une séquence musicale @code{@{ @dots{} @}} se voit attribuer le nom de
@code{SequentialMusic}, et les expressions qu'elle contient sont
enregistrées en tant que liste dans sa propriété @code{'elements}. Une
note est représentée par un objet @code{NoteEvent} -- contenant les
propriétés de durée et hauteur -- ainsi que l'information qui lui est
attachée -- en l'occurrence un @code{AbsoluteDynamicEvent} ayant une
propriété @code{text} de valeur @code{"f"} -- et stockée dans sa
propriété @code{articulations}.
@funindex{\void}
La fonction @code{\displayMusic} renvoie la musique qu'elle
affiche ; celle-ci sera donc aussi interprétée. L'insertion d'une
commande @code{\void} avant le @code{\displayMusic} permet de
s'affranchir de la phase d'interprétation.
@node Propriétés musicales
@subsection Propriétés musicales
@translationof Music properties
Nous abordons ici les propriétés @emph{music}, et non pas les propriétés
@emph{context} ou @emph{layout}.
Partons de cet exemple simple :
@example
someNote = c'
\displayMusic \someNote
===>
(make-music
'NoteEvent
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 0 0))
@end example
L'objet @code{NoteEvent} est la représentation brute de @code{someNote}.
Voyons ce qui se passe lorsque nous plaçons ce @notation{c'} dans une
construction d'accord :
@example
someNote = <c'>
\displayMusic \someNote
===>
(make-music
'EventChord
'elements
(list (make-music
'NoteEvent
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 0 0))))
@end example
L'objet @code{NoteEvent} est maintenant le premier objet de la propriété
@code{'elements} de @code{someNote}.
@code{\displayMusic} utilise la fonction @code{display-scheme-music}
pour afficher la représentation en Scheme d'une expression musicale :
@example
#(display-scheme-music (first (ly:music-property someNote 'elements)))
===>
(make-music
'NoteEvent
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 0 0))
@end example
La hauteur de la note est accessible au travers de la propriété
@code{'pitch} de l'objet @code{NoteEvent} :
@example
#(display-scheme-music
(ly:music-property (first (ly:music-property someNote 'elements))
'pitch))
===>
(ly:make-pitch 0 0 0)
@end example
La hauteur de la note se modifie en définissant sa propriété
@code{'pitch} :
@funindex \displayLilyMusic
@example
#(set! (ly:music-property (first (ly:music-property someNote 'elements))
'pitch)
(ly:make-pitch 0 1 0)) ;; set the pitch to d'.
\displayLilyMusic \someNote
===>
d'4
@end example
@node Doublement d'une note avec liaison (exemple)
@subsection Doublement d'une note avec liaison (exemple)
@translationof Doubling a note with slurs (example)
Supposons que nous ayons besoin de créer une fonction transformant une
saisie @code{a} en @w{@code{@{ a( a) @}}}. Commençons par examiner
comment le résultat est représenté en interne.
@example
\displayMusic@{ a'( a') @}
===>
(make-music
'SequentialMusic
'elements
(list (make-music
'NoteEvent
'articulations
(list (make-music
'SlurEvent
'span-direction
-1))
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 5 0))
(make-music
'NoteEvent
'articulations
(list (make-music
'SlurEvent
'span-direction
1))
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 5 0))))
@end example
Mauvaise nouvelle ! Les expressions @code{SlurEvent} doivent
s'ajouter « à l'intérieur » de la note -- dans sa propriété
@code{articulations}.
Examinons à présent la saisie :
@example
\displayMusic a'
===>
(make-music
'NoteEvent
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch 0 5 0))))
@end example
Nous aurons donc besoin, dans notre fonction, de cloner cette expression
-- de telle sorte que les deux notes constituent la séquence -- puis
d'ajouter un @code{SlurEvent} à la propriété @code{'articulations} de
chacune d'elles, et enfin réaliser un @code{SequentialMusic} de ces deux
éléments @code{NoteEvent}. En tenant compte du fait que, dans le cadre
d'un ajout, une propriété non définie est lue @code{'()} (une liste
vide), aucune vérification n'est requise avant d'introduire un nouvel
élément en tête de la propriété @code{articulations}.
@example
doubleSlur = #(define-music-function (note) (ly:music?)
"Renvoie : @{ note ( note ) @}.
`note' est censé être une note unique."
(let ((note2 (ly:music-deep-copy note)))
(set! (ly:music-property note 'articulations)
(cons (make-music 'SlurEvent 'span-direction -1)
(ly:music-property note 'articulations)))
(set! (ly:music-property note2 'articulations)
(cons (make-music 'SlurEvent 'span-direction 1)
(ly:music-property note2 'articulations)))
(make-music 'SequentialMusic 'elements (list note note2))))
@end example
@node Ajout d'articulation à des notes (exemple)
@subsection Ajout d'articulation à des notes (exemple)
@translationof Adding articulation to notes (example)
Le moyen d'ajouter une articulation à des notes consiste à fusionner
deux expressions musicales en un même contexte. L'option de réaliser
nous-mêmes une fonction musicale à cette fin nous offre l'avantage de
pouvoir alors ajouter une articulation, telle qu'une instruction de
doigté, individuellement à l'une des notes d'un accord, ce qui est
impossible dans le cadre d'une simple fusion de musique indépendante.
Un @code{$variable} au milieu de la notation @code{#@{ @dots{} #@}} se
comporte exactement comme un banal @code{\variable} en notation LilyPond
traditionnelle. Nous savons déjà que
@example
@{ \musique -. -> @}
@end example
@noindent
n'est pas admis par LilyPond. Nous pourrions tout à fait contourner ce
problème en attachant l'articulation à un accord vide,
@example
@{ << \musique <> -. -> >> @}
@end example
@noindent
mais, pour les besoins de la démonstration, nous allons voir comment
réaliser ceci en Scheme. Commençons par examiner une saisie simple et
le résultat auquel nous désirons aboutir :
@example
% saisie
\displayMusic c4
===>
(make-music
'NoteEvent
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch -1 0 0))))
=====
% résultat attendu
\displayMusic c4->
===>
(make-music
'NoteEvent
'articulations
(list (make-music
'ArticulationEvent
'articulation-type
"accent"))
'duration
(ly:make-duration 2 0 1/1)
'pitch
(ly:make-pitch -1 0 0))
@end example
Nous voyons qu'une note (@code{c4}) est représentée par une expression
@code{NoteEvent}. Si nous souhaitons ajouter une articulation
@notation{accent}, nous devrons ajouter une expression
@code{ArticulationEvent} à la propriété @code{articulations} de
l'expression @code{NoteEvent}.
Construisons notre fonction en commençant par
@example
(define (ajoute-accent note-event)
"Ajoute un accent (ArticulationEvent) aux articulations de `note-event',
qui est censé être une expression NoteEvent."
(set! (ly:music-property note-event 'articulations)
(cons (make-music 'ArticulationEvent
'articulation-type "accent")
(ly:music-property note-event 'articulations)))
note-event)
@end example
La première ligne est la manière de définir une fonction en Scheme : la
fonction Scheme a pour nom @code{ajoute-accent} et elle comporte une
variable appelée @code{note-event}. En Scheme, le type d'une variable
se déduit la plupart de temps de par son nom -- c'est d'ailleurs une
excellente pratique que l'on retrouve dans de nombreux autres langages.
@example
"Ajoute un accent@dots{}"
@end example
@noindent
décrit ce que fait la fonction. Bien que ceci ne soit pas primordial,
tout comme des noms de variable évidents, tâchons néanmoins de prendre
de bonnes habitudes dès nos premiers pas.
Vous pouvez vous demander pourquoi nous modifions directement
l'événement note plutôt que d'en manipuler une copie -- on pourrait
utiliser @code{ly:music-deep-copy} à cette fin. La raison en est qu'il
existe un contrat tacite : les fonctions musicales sont autorisées
à modifier leurs arguments -- ils sont générés en partant de zéro (comme
les notes que vous saisissez) ou déjà recopiés (faire référence à une
variable musicale avec @samp{\name} ou à de la musique issue
d'expressions Scheme @samp{$(@dots{})} aboutit à une copie). Dans la
mesure où surmultiplier les copies serait contre productif, la valeur de
retour d'une fonction musicale n'est @strong{pas} recopiée. Afin de
respecter ce contrat, n'utilisez pas un même argument à plusieurs
reprises, et n'oubliez pas que le retourner compte pour une utilisation.
Dans un exemple précédent, nous avons construit de la musique en
répétant un certain argument musical. Dans ce cas là, l'une des
répétitions se devait d'être une copie. Dans le cas contraire,
certaines bizarreries auraient pu survenir. Par exemple, la présence
d'un @code{\relative} ou d'un @code{\transpose}, après plusieurs
répétitions du même élément, entraînerait des « relativisations » ou
transpositions en cascade. Si nous les assignons à une variable
musicale, l'enchaînement est rompu puisque la référence à @samp{\nom}
créera une nouvelle copie sans toutefois prendre en considération
l'identité des éléments répétés.
Cette fonction n'étant pas une fonction musicale à part entière, elle
peut s'utiliser dans d'autres fonctions musicales. Il est donc sensé de
respecter le même contrat que pour les fonctions musicales :
l'entrée peut être modifiée pour arriver à une sortie, et il est de la
responsabilité de l'appelant d'effectuer des copies s'il a réellement
besoin de l'argument dans son état originel. Vous constaterez, à la
lecture des fonctions propres à LilyPond, comme @code{music-map}, que ce
principe est toujours respecté.
Revenons à nos moutons… Nous disposons maintenant d'un @code{note-event}
que nous pouvons modifier, non pas grâce à un @code{ly:music-deep-copy},
mais plutôt en raison de notre précédente réflexion. Nous ajoutons
@notation{l'accent} à la liste de ses propriétés @code{'articulations}.
@example
(set! emplacement nouvelle-valeur)
@end example
L'emplacement est ce que nous voulons ici définir. Il s'agit de la
propriété @code{'articulations} de l'expression @code{note-event}.
@example
(ly:music-property note-event 'articulations)
@end example
La fonction @code{ly:music-property} permet d'accéder aux propriétés
musicales -- les @code{'articulations}, @code{'duration}, @code{'pitch},
etc. que @code{\displayMusic} nous a indiquées. La nouvelle valeur sera
l'ancienne propriété @code{'articulations}, augmentée d'un
élément : l'expression @code{ArticulationEvent}, que nous
recopions à partir des informations de @code{\displayMusic}.
@example
(cons (make-music 'ArticulationEvent
'articulation-type "accent")
(ly:music-property result-event-chord 'articulations))
@end example
@code{cons} permet d'ajouter un élément en tête de liste sans pour
autant modifier la liste originale. C'est exactement ce que nous
recherchons : la même liste qu'auparavant, plus la nouvelle
expression @code{ArticulationEvent}. L'ordre au sein de la propriété
@code{'articulations} n'a ici aucune importance.
Enfin, après avoir ajouté l'articulation @notation{accent} à sa
propriété @code{articulations}, nous pouvons renvoyer le
@code{note-event}, ce que réalise la dernière ligne de notre fonction.
Nous pouvons à présent transformer la fonction @code{ajoute-accent} en
fonction musicale, à l'aide d'un peu d'enrobage syntaxique et mention du
type de son argument.
@example
ajouteAccent = #(define-music-function (note-event)
(ly:music?)
"Ajoute un accent (ArticulationEvent) aux articulations de `note-event',
qui est censé être une expression NoteEvent."
(set! (ly:music-property note-event 'articulations)
(cons (make-music 'ArticulationEvent
'articulation-type "accent")
(ly:music-property note-event 'articulations)))
note-event)
@end example
Par acquis de conscience, vérifions que tout ceci fonctione :
@example
\displayMusic \ajouteAccent c4
@end example
@ignore
@menu
* Tweaking with Scheme::
@end menu
@c @nod e Tweaking with Scheme
@c @sectio n Tweaking with Scheme
We have seen how LilyPond output can be heavily modified using
commands like
@code{\override TextScript.extra-offset = ( 1 . -1)}. But
we have even more power if we use Scheme. For a full explanation
of this, see the @ref{Scheme tutorial}, and
@ref{Interfaces for programmers}.
We can use Scheme to simply @code{\override} commands,
TODO Find a simple example
@c This isn't a valid example with skylining
@c It works fine without padText -td
@end ignore
@ignore
@lilypond[quote,verbatim,ragged-right]
padText = #(define-music-function (padding) (number?)
#{
\once \override TextScript.padding = #padding
#})
\relative {
c'''4^"piu mosso" b a b
\padText #1.8
c4^"piu mosso" d e f
\padText #2.6
c4^"piu mosso" fis a g
}
@end lilypond
@end ignore
@ignore
We can use it to create new commands:
@c Check this is a valid example with skylining
@c It is - 'padding still works
@lilypond[quote,verbatim,ragged-right]
tempoPadded = #(define-music-function (padding tempotext)
(number? markup?)
#{
\once \override Score.MetronomeMark.padding = #padding
\tempo \markup { \bold #tempotext }
#})
\relative {
\tempo \markup { "Low tempo" }
c''4 d e f g1
\tempoPadded #4.0 "High tempo"
g4 f e d c1
}
@end lilypond
Even music expressions can be passed in:
@lilypond[quote,verbatim,ragged-right]
pattern = #(define-music-function (x y) (ly:music? ly:music?)
#{
#x e8 a b $y b a e
#})
\relative c''{
\pattern c8 c8\f
\pattern {d16 dis} { ais16-> b\p }
}
@end lilypond
@end ignore
|