| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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 guile dans
un terminal. Certains systèmes, notamment Windows, nécessitent d’avoir
auparavant créé la variable d’environnement GUILE_LOAD_PATH qui
devra pointer vers le répertoire ../usr/share/guile/1.8 de
l’installation de LilyPond – pour connaître le chemin complet d’accès à
ce répertoire, consultez
(learning)Autres sources de documentation section ‘Autres sources de documentation’ dans Manuel d’initiation.
Les utilisateurs de Windows peuvent aussi prendre l’option « Exécuter »
à partir du menu « Démarrer » puis taper guile.
Néanmoins, tous les paquetages de LilyPond disposent d’un bac à sable Scheme, accessible par la commande :
lilypond scheme-sandbox |
Une fois le bac à sable actif, vous obtiendrez l’invite :
guile> |
Vous pouvez dès à présent saisir des expressions Scheme pour vous
exercer. Si vous souhaitez pouvoir utiliser la bibliothèque GNU
readline, qui offre une ligne de commande plus élaborée,
consultez les informations contenues dans le fichier
‘ly/scheme-sandbox.ly’. La bibliothèque readline, dans la
mesure où elle est habituellement activée dans vos sessions Guile,
devrait être effective y compris dans le bac à sable.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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 define :
guile> (define a 2) guile> |
L’évaluation d’une variable Scheme se réalise en saisissant le nom de cette variable à l’invite de Guile :
guile> a 2 guile> |
Une variable Scheme s’affiche à l’écran à l’aide de la fonction
display :
guile> (display a) 2guile> |
Vous aurez remarqué que la valeur 2 et l’invite guile
apparaissent sur une même ligne. On peut améliorer la présentation à
l’aide de la procédure newline ou bien en affichant un caractère
« retour chariot ».
guile> (display a)(newline) 2 guile> (display a)(display "\n") 2 guile> |
Après avoir créé une variable, vous pouvez en modifier la valeur grâce à
un set! :
guile> (set! a 12345) guile> a 12345 guile> |
Vous quitterez proprement le bac à sable à l’aide de l’instruction
quit :
guile> (quit) |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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.
Les valeurs booléennes sont vrai ou faux. En Scheme, ce sera #t
pour vrai, et #f pour faux.
Les nombres se saisissent le plus communément : 1 est le
nombre (entier) un, alors que -1.5 est un nombre à virgule
flottante (un nombre non entier).
Les chaînes de caractères sont bornées par des guillemets informatiques :
"ceci est une chaîne" |
Une chaîne peut s’étendre sur plusieurs lignes :
"ceci est une chaîne" |
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 \n.
"ceci\nest une\nchaîne multiligne" |
Guillemets et obliques inverses dans une chaîne doivent être précédés
d’une oblique inverse. La chaîne \a dit "b" se saisit donc
"\\a dit \"b\"" |
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 http://www.gnu.org/software/guile/manual/html_node/Simple-Data-Types.html.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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.
| Paires | ||
| Listes | ||
| Listes associatives (alists) | ||
| Tables de hachage |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Pairs
Le type de donnée composite fondamental est la paire (pair).
Comme son nom l’indique, il s’agit de lier deux valeurs, à l’aide de
l’opérateur cons.
guile> (cons 4 5) (4 . 5) guile> |
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
(.) 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.
guile> '(4 . 5) (4 . 5) guile> |
Les deux éléments d’une paire peuvent être constitués de n’importe quelle valeur Scheme valide :
guile> (cons #t #f)
(#t . #f)
guile> '("blah-blah" . 3.1415926535)
("blah-blah" . 3.1415926535)
guile>
|
Les premier et second éléments de la paire sont accessibles à l’aide des
procédures Scheme car et cdr.
guile> (define mypair (cons 123 "hello there") … ) guile> (car mypair) 123 guile> (cdr mypair) "hello there" guile> |
Note : cdr se prononce « couldeur », comme l’indiquent Sussman et
Abelson – voir
http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-14.html#footnote_Temp_133.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Lists
Autre structure de donnée commune en Scheme : la liste (list).
Une liste « correcte » se définit comme étant vide (représentée par
'() et de longueur 0) ou une paire dont le cdr est une
liste.
Il existe plusieurs méthodes pour créer une liste, la plus courante
étant l’utilisation de la procédure list :
guile> (list 1 2 3 "abc" 17.5) (1 2 3 "abc" 17.5) |
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 :
(1 . (2 . (3 . ("abc" . (17.5 . ())))))
|
Vous pouvez donc saisir une liste comme elle serait 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) :
guile> '(17 23 "foo" "bar" "bazzle") (17 23 "foo" "bar" "bazzle") |
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.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Association lists (alists)
Il existe un type particulier de liste : la liste associative – ou alist. Une 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 car de chacun des éléments constitue une clé (key) et
chaque cdr une valeur (value). La procédure Scheme
assoc permet de retrouver une entrée de la liste
associative ; son cdr en fournira la valeur :
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> |
LilyPond recourt abondamment aux alists pour stocker des propriétés ou autres données.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Hash tables
Il s’agit d’une structure de données à laquelle LilyPond fait parfois appel. Une table de hachage (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.
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" |
La procédure hashq-ref permet de récupérer une valeur dans la
table de hachage.
guile> (hashq-ref h 3) "valeur3" guile> (hashq-ref h 'cle2) "valeur2" guile> |
La procédure hashq-get-handle permet de retrouver à la fois une
clé et sa valeur. Cette procédure a l’avantage de renvoyer #f
lorsque la clé n’existe pas.
guile> (hashq-get-handle h 'cle1) (cle1 . "valeur1") guile> (hashq-get-handle h 'zut) #f guile> |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Calculations in Scheme
Scheme permet aussi d’effectuer des calculs. Il utilise alors un
préfixe. Additionner 1 et 2 s’écrira (+ 1 2) et
non 1+2 comme on aurait pu s’y attendre.
guile> (+ 1 2) 3 |
Les calculs peuvent s’imbriquer ; le résultat d’une fonction peut servir pour un autre calcul.
guile> (+ 1 (* 3 4)) 13 |
Ces calculs sont un exemple d’évaluation : une expression telle que
(* 3 4) est remplacée par sa valeur, soit 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 :
guile> (/ 7 3) 7/3 guile> (/ 7.0 3.0) 2.33333333333333 |
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 :
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> |
Vous pouvez constater que l’interpréteur a tenté de considérer 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 » (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é (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.
guile> (list 1 2 3) (1 2 3) guile> '(1 2 3) (1 2 3) guile> |
Vous pourrez être confronté à cette erreur lorsque vous intégrerez Scheme à LilyPond.
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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.
| Définition de procédures | ||
| Prédicats | ||
| Valeurs de retour |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Defining procedures
En Scheme, on définit une procédure à l’aide de l’instruction
define :
(define (nom-fonction argument1 argument2… argumentn) expression-scheme-qui-donnera-une-valeur-en-retour) |
Nous pourrions, par exemple, définir une procédure calculant la moyenne de deux nombres :
guile> (define (moyenne x y) (/ (+ x y) 2)) guile> moyenne #<procedure moyenne (x y)> |
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 :
guile> (moyenne 3 12) 15/2 |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Predicates
Une procédure Scheme chargée de retourner une valeur booléenne s’appelle un « prédicat » (predicate). Par convention, plutôt que par nécessité, le nom d’un prédicat se termine par un point d’interrogation :
guile> (define (moins-de-dix? x) (< x 10)) guile> (moins-de-dix? 9) #t guile> (moins-de-dix? 15) #f |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@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 begin,
qui permet l’évaluation de plusieurs expressions et renvoie la valeur de
la dernière expression.
guile> (begin (+ 1 2) (- 5 8) (* 2 2)) 4 |
Une deuxième méthode consiste à combiner les expressions dans un bloc
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 let est la valeur de
retour de la dernière clause de ce bloc :
guile> (let ((x 2) (y 3) (z 4)) (display (+ x y)) (display (- z 4)) … (+ (* x y) (/ z x))) 508 |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof Scheme conditionals
| if | ||
| cond |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof if
Scheme dispose d’une procédure if :
(if expression-test expression-affirmative expression-négative) |
expression-test est une expression qui renverra une valeur
booléenne. Dans le cas où expression-test retourne #t, la
procédure if renvoie la valeur de expression-affirmative,
et celle de expression-négative dans le cas contraire.
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" |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] | [Racine] | [Table des matières] | [Index] | [ ? ] |
@translationof cond
Une autre manière d’introduire une condition en Scheme est d’utiliser
l’instruction cond :
(cond (expression-test-1 expression-résultat-séquence-1)
(expression-test-2 expression-résultat-séquence-2)
…
(expression-test-n expression-résultat-séquence-n))
|
Comme par exemple ici :
guile> (define a 6) guile> (define b 8) guile> (cond ((< a b) "a est plus petit que b") … ((= a b) "a égale b") … ((> a b) "a est plus grand que b")) "a est plus petit que b" |
| [ < ] | [ > ] | [ << ] | [Plus haut] | [ >> ] |
Ce document a été généré le le 13 juillet 2020 par gub en utilisant texi2html 1.82.
Autres langues : English, deutsch, español.
À propos de la sélection automatique de la langue.