lilypond-user-fr
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: débuts difficiles avec scheme


From: Jean Abou Samra
Subject: Re: débuts difficiles avec scheme
Date: Mon, 5 Jul 2021 08:50:42 +0200 (CEST)


Le 04/07/2021 22:25, Martial R <martialr@gmail.com> a écrit :


Depuis le temps que je me disais faudrait que je regarde comment introduire les conditions.
Oui vu pendant le café Lily mais  ... pfuit .... oublié...

En attendant d'avoir pu la mettre au propre dans un tutoriel, je vous envoie la présentation sur laquelle je parlais, qui peut déjà servir pour raviver ses souvenirs.

Cordialement,
Jean

Programmation en Scheme (pour LilyPond)

Scheme ?

  • Langage épuré, minimaliste, inspiré de Lisp.
  • Implémentation Guile: GNU Ubiquitous Intelligent Language For Extensions
  • Langage d'extension de LilyPond. On peut tout faire en Scheme.

    • Raccourcis pratiques.
    • Nouvelles formes de notation.

image.png

image.png

Scheme dans LilyPond

Le # introduit une _expression_ Scheme.

#(set-global-staff-size 18)

\tag #'edition (

\shape #'((0 . 0) (0.1 . 0.3) (0.1 . 0.5) (0 . 1.0)) Slur

À l'intérieur d'une _expression_ Scheme, #{ ... #} passe en syntaxe LilyPond.

#(skip-of-length #{ { c'8 d' e' f' e' d' } #})

Écrire des valeurs simples

  • Nombres.

  • Chaînes de caractères.

In [4]:
"Sonate 4"
Out[4]:
"Sonate 4"

Commentaires

Introduits par le caractère ;.

In [6]:
; Ceci est ignoré.
4 ; Moi aussi, je suis ignoré.
Out[6]:
4

Premières opérations

In [9]:
(/ 2 2)
Out[9]:
1

Syntaxe : (procédure argument1 argument2 ...)

+, -, *, / sont des procédures (fonctions).

LilyPond : \fonction argument1 argument2 ...

\relative c' { c d e d c }

Scheme : (fonction argument1 argument2 ...)

Imbrication des expressions et parenthésage

In [12]:
(+ 2 2)
Out[12]:
4

Les parenthèses ne sont pas facultatives !

In [11]:
+ 2 2
Out[11]:
2
In [14]:
(4)
wrong-type-arg
(#f Wrong type to apply: ~S (4) (4))
  • $2/3 - 3/5$
In [15]:
(- (/ 2 3) (/ 3 5))
Out[15]:
1/15

$1 - (5 \times 6) - (3 \times (4 + 3/12))$

In [17]:
(- 1 (* 5 6) (* 3 (+ 4 (/ 3 12))))
Out[17]:
-167/4

En Scheme, tout est _expression_.

           ()(((((()))))((                    )((())((()(    )())         )(()   ())()((()(((((    ())(     ()()  ())()(()(())(( 
       )))((())((())))(                  (()())((()))        ))))         )(()   ))()()(()(()(     ()((() ))(((   ((()()(())(()  
     ))()))(()))()                   (())((())()(            ))()         ())(   (())              ))   )((       (())           
   )))))((((()((              ))()()()))()))                 )(()         )(()   ()((              )((   (  ()(   (())           
   )()(((()))(()(             )()(()()()()()(                ((()         ()()   )())              )))   )   ()(  ()))           
       )()(()()((())(         (()()(((()))))                 ()))())))(((()()(   ))))())((         ))(       (((  ((((()())      
               ))(((())(()((  (())((()))(()))                )(((((()(((()())(   )())              )()       )))  )))(           
                 )()))()()()  ))()))))((()))                 ()()         (())   ()()              ()(       )((  (())           
              )(((())(())         )))()()((()(()(            )))(         ))((   ))((              ())       )()  ()((           
           ))((((()()))               )(()(()())))           ()((         ()((   )()(              (((       ))(  (()(           
        ()()))((()()()                   ((()())))))())))    ()()          ))()  ()()()(())()      (((       )()  ())()(()())(   
 ((())))(((())(()                            )(()()((())(((  ()()           )()( )))))())((()((   )()        ()(  ())(()(()))))) 

Formatage du code

Trois formes possibles pour une _expression_ :

1.

(fonction argument1 argument2 ...)

2.

(fonction argument1
          argument2
          ...)

3.

(fonction
  argument1
  argument2
  ...)

Exemple

In [19]:
(+ (* 3 456 123)
   (/ 6 (- 10 7))
   (-
     (* 30 4012 (+ 123 5642))
     20
     50003))
Out[19]:
694043646

Définition de variables

Syntaxe :

(define nom valeur)

Équivalent LilyPond : clarinette = { ... }.

On peut ensuite réutiliser la valeur.

In [17]:
(define x+|^ 34)
x+|^
Out[17]:
34

Conditions

Un moyen de prendre des décisions.

Syntaxe :

(if condition _expression_-si-oui _expression_-si-non)
In [20]:
(if (= 3 4)
    "3 = 4, ça alors !"
    "3 ≠ 4, vraiment ?")
Out[20]:
"3 ≠ 4, vraiment ?"
In [22]:
(= 3 3)
Out[22]:
#t

Deux valeurs booléennes : #t (true, vrai) et #f (false, faux).

Tests d'égalité

= : égalité numérique.

In [23]:
(= 3 3.0)
Out[23]:
#t

equal? : le plus général, s'applique à tous les types.

In [25]:
(equal? "Bonjour" "Bonjour")
Out[25]:
#t

Tests de comparaison

Toutes les possibilités : <, <=, >, >=.

In [30]:
(>= 3 3)
Out[30]:
#t

Combiner des tests

not : négation.

and : toutes les conditions sont vérifiées.

or : au moins une des conditions est vérifiée.

In [33]:
(define x 50)
(if (and (>= x 0)
         (<= x 100))
    "x est bien un pourcentage."
    "Pourcentage suspect (pas entre 0 et 100).")
Out[33]:
"x est bien un pourcentage."
In [35]:
(define x 1000000000000000)

(if (or (>= x 100000)
        (<= x -100000))
     "C'est trop grand pour moi."
     "Je peux m'en sortir.")
Out[35]:
"C'est trop grand pour moi."
In [37]:
(define x 0)

(if (not (= x 0))
    "Erreur : x devrait valoir 0.")

La dernière _expression_ n'a pas de valeur particulière lorsque $x$ est entre -100000 et 100000. Mais elle a tout de même une valeur !

In [38]:
*unspecified*
In [39]:
(define x 42)

(unspecified?
  (if (or (>= x 100000)
          (<= x -100000))
      "C'est trop grand pour moi."))
Out[39]:
#t

*unspecified* prend son intérêt lorsque l'_expression_ ne renvoie pas de valeur particulière.

Exemple : display affiche une chaîne et renvoie *unspecified*.

In [41]:
(define x 10)

(if (or (>= x 100000)
        (<= x -100000))
     (display "Erreur : c'est trop grand pour moi."))

Plus de branches

Cas typique :

(if (= direction LEFT)
    [aligne grob à gauche...]
    (if (= direction CENTER)
        [aligne grob au milieu...]
        (if (= direction RIGHT)
            [aligne grob à droite...]
            [erreur : direction invalide...])))

Syntaxe plus pratique :

(cond
  (condition1 valeur1)
  (condition2 valeur2)
  (condition3 valeur3)
  ...)

La dernière condition peut être else.

In [43]:
(define n -1)

(cond
  ((= n 0)
   "Aucun")
  ((= n 1)
    "Un seul")
  ((> n 1)
   "Plusieurs")
  (else
   "On attendait un nombre positif"))
Out[43]:
"On attendait un nombre positif"

Définition d'une fonction

Syntaxe :

(define (nom argument1 argument2 ...)
  ...)
In [44]:
(define (double x)
  (* 2 x)
  )

(double 4)
Out[44]:
8
In [45]:
(define c 299792458)

(define (E m)
  (* m (expt c 2)))

(E 56)
Out[45]:
5033029000926178784

Le corps de la fonction est une suite d'expressions. La valeur de la dernière est renvoyée.

In [47]:
(define (double x)
  (display "Fonction double appelée avec le paramètre : ")
  (display x)
  (newline)
  (* 2 x)
  )

(double 4)
Out[47]:
Fonction double appelée avec le paramètre : 4

Variables locales

« Locales » : disponibles seulement dans une certaine partie du code.

Syntaxe :

(let ((variable1 valeur1)
      (variable2 valeur2)
      (variable3 valeur3)
      ...)
  expression1
  expression2
  ...)

let se reconnaît visuellement.

(let (xxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxx
      xxxxxxxxxxxxxxxxxxxxxxxxxx)
  xxxxxxxxxxxxxxxxxx
  xxxxxxxxxxxxxxxxxx
  xxxxxxxxxxxxxxxxxx
  xxxxxxxxxxxxxxxxxx)
In [73]:
(define (loto)
  (let ((x (random 2))
        (y (random 2)))
    (display
      (if (and (= x 1)
               (= y 1))
          "Chance !"
          "Perdu."))))

(loto)
Out[73]:
Perdu.
In [74]:
(let ((tirage1 (random 10))
      (tirage2 (random (+ tirage1 1))))
  (display
    (if (eqv? tirage1 tirage2)
        "Chance !"
        "Perdu.")))
unbound-variable
(#f Unbound variable: ~S (tirage1) #f)

Avec let, toutes les valeurs sont évaluées, puis les variables sont affectées.

let* affecte les variables l'une après l'autre.

In [81]:
(let* ((tirage1 (random 10))
       (tirage2 (random (+ tirage1 1))))
  (display
    (if (= tirage1 tirage2)
        "Chance !"
        "Perdu.")))
Out[81]:
Perdu.

Forme typique d'une fonction :

(define (fonction paramètres...)
  (let* ((variable1 ...)
         (variable2 ...)
         (variable3 ...)
         (variable4 ...)
         encore beaucoup de variables...)
    une _expression_ simple et courte))

Exemple réel :

In [205]:
(define (highlight::markup grob)
   (let* ((x-extent (ly:grob-property grob 'X-extent))
          (x-start (interval-start x-extent))
          (x-end (interval-end x-extent))
          (y-extent (ly:grob-property grob 'Y-extent))
          (y-start (interval-start y-extent))
          (y-end (interval-end y-extent))
          (blot (ly:grob-property grob 'blot))
          (color-gradient (ly:grob-property grob 'color-gradient))
          (left-color (ly:grob-property grob 'left-color))
          (right-color (ly:grob-property grob 'right-color))
          (path
            `((moveto ,(+ x-start blot) ,y-start)
              (lineto ,(- x-end blot) ,y-start)
              (curveto ,x-end ,y-start ,x-end ,y-start ,x-end ,(+ y-start blot))
              (lineto ,x-end ,(- y-end blot))
              (curveto ,x-end ,y-end ,x-end ,y-end ,(- x-end blot) ,y-end)
              (lineto ,(+ x-start blot) ,y-end)
              (curveto ,x-start ,y-end ,x-start ,y-end ,x-start ,(- y-end blot))
              (lineto ,x-start ,(+ y-start blot))
              (curveto ,x-start ,y-start ,x-start ,y-start ,(+ x-start blot) ,y-start))))
     (if (null? color-gradient)
         (make-path-markup 0 path)
         (make-gradient-path-markup left-color right-color path))))

Le parenthésage !

In [174]:
(let ((a 5))
  (+ a 15))
Out[174]:
20

Oublier une parenthèse.

In [83]:
(let ((a 5))
  (+ a 15))
Out[83]:
20
In [90]:

Déplacer une parenthèse.

In [92]:
(let
  (
     (a 5)
     (+ a 15)
   )
  
  )
syntax-error
(let bad let #(#f 0 7) (let ((a 5) (+ a 15))) #f)

Omettre des parenthèses.

In [95]:
(let ((a 5))
  (+ a 15))
Out[95]:
20

Listes

Forme la plus simple :

(list élément1 élément2 élément3 ...)
In [97]:
(list 0 1 2 3 4 "Bonjour")
Out[97]:
(0 1 2 3 4 "Bonjour")

Autre forme, qui fonctionne pour les nombres, chaînes et booléens littéraux :

'(élément1 élément2 élément3 ...)
In [98]:
'(0 1 2 3 "Bonjour")
Out[98]:
(0 1 2 3 "Bonjour")
In [100]:
(list (+ 1 1)
      (+ 2 3))
Out[100]:
(2 5)
In [101]:
'((+ 1 1)
  (+ 2 2))
Out[101]:
((+ 1 1) (+ 2 2))

Opérations de base

length : longueur d'une liste.

In [102]:
(length '(0 1 2 3 "Bonjour"))
Out[102]:
5

reverse : la liste à l'envers.

In [103]:
(reverse '(0 1 2 3))
Out[103]:
(3 2 1 0)

list-ref : accès à un élément (le premier à l'index 0).

In [104]:
(list-ref '(1 2 3 4)
          2)
Out[104]:
3

list-ref est à apprendre… et oublier !

Parcours d'une liste

map : applique une transformation à tous les éléments d'une liste.

On passe la transformation comme une fonction.

(map fonction liste)
In [107]:
(define (double x)
  (* 2 x))

(map double '(0 1 2 3))
Out[107]:
(0 2 4 6)

On peut même passer plusieurs listes.

In [110]:
(define (anniversaire personne date)
  (display personne)
  (display " fête son anniversaire le ")
  (display date)
  (newline))

(map anniversaire
     '("Amélie" "Jules" "Jonathan")
     '("13 janvier" "9 novembre" "30 février"))
Out[110]:
Amélie fête son anniversaire le 13 janvier
Jules fête son anniversaire le 9 novembre
Jonathan fête son anniversaire le 30 février
(#<unspecified> #<unspecified> #<unspecified>)

for-each est comme map, mais le résultat de la fonction n'est pas conservé.

In [116]:
(define (anniversaire personne date)
  (display personne)
  (display " fête son anniversaire le ")
  (display date)
  (newline))

(for-each anniversaire
          '(Amélie Jules Jonathan)
          (circular-list "13 janvier"))
Out[116]:
Amélie fête son anniversaire le 13 janvier
Jules fête son anniversaire le 13 janvier
Jonathan fête son anniversaire le 13 janvier

filter : garde seulement les éléments d'une liste qui vérifient une condition.

In [191]:
(even? 2)
Out[191]:
#t
In [192]:
(filter even? '(0 1 2 3))
Out[192]:
(0 2)

Fonctions anonymes

lambda sert à créer une fonction sans lui donner de nom.

(lambda (paramètre1 paramètre2 ...)
  ...)

Ainsi,

(define (fonction paramètre1 paramètre2 ...)
  ...)

est un raccourci pour

(define fonction
        (lambda (paramètre1 paramètre2 ...)
          ...))
In [117]:
(map
  (lambda (x)
    (* 2 x))
  '(0 1 2 3))
Out[117]:
(0 2 4 6)
In [118]:
(for-each
  (lambda (personne date)
    (display personne)
    (display " fête son anniversaire le ")
    (display date)
    (newline))
  '(Amélie Jules Jonathan)
  '("13 janvier" "9 novembre" "30 février"))
Out[118]:
Amélie fête son anniversaire le 13 janvier
Jules fête son anniversaire le 9 novembre
Jonathan fête son anniversaire le 30 février

Pour finir : une application à LilyPond

Les objets graphiques (Graphical Objects, grobs) possèdent des propriétés.

\override NoteHead.color = "red"

Une propriété peut voir sa valeur calculée à partir d'autres propriétés.

Pour cela, on lui affecte une fonction.

\override NoteHead.color =
  #(lambda (grob)
     ...)
\override NoteHead.color =
  #(lambda (grob)
     ...)

grob est un objet graphique, avec des propriétés. On peut les lire :

(ly:grob-property grob propriété)

propriété est le nom d'une propriété précédé d'un '.

(ly:grob-property grob 'staff-position)
\version "2.22.0"

\layout {
  \context {
    \Voice
    \override NoteHead.color =
      #(lambda (grob)
          (let ((ligne-portée (ly:grob-property grob 'staff-position)))
            (cond
              ((= ligne-portée -8) "red")
              ((= ligne-portée -7) "blue")
              ((= ligne-portée -6) "green")
              ((= ligne-portée -5) "orange")
              ((= ligne-portée -4) "purple")
              ((= ligne-portée -3) "grey")
              ((= ligne-portée -2) "salmon")
              (else "black"))))
  }
}

\relative {
  \time 3/4
  a8 b cis b a4
  b fis' b,
  c8 d e d c4
  d2.
  e8 fis g g fis e
  fis cis cis dis e4
  e8 fis g fis e fis
  a b cis b a4
  g8 a b a g4
  fis8 g a g fis4
}

Ressources sur Scheme

  • Documentation Guile :

https://www.gnu.org/software/guile/learn/

  • Un tutoriel adapté à LilyPond :

https://scheme-book.ursliska.de/introduction/index.html

  • Documentation officielle :

https://lilypond.org/doc/v2.22/Documentation/extending/

  • Des explications sur les interfaces Scheme de LilyPond :

https://extending-lilypond.readthedocs.io/en/latest/


reply via email to

[Prev in Thread] Current Thread [Next in Thread]