next up previous contents index
Next: Ordre d'évaluation de Scheme Up: Premiers pas Previous: Les outils   Contents   Index

Subsections

Premiers pas...

Dans cette section, nous allons examiner quelques concepts de base du langage, comme les fonctions, les types de données primitifs, la gestion des erreurs et l'alternative.


Les fonctions

Avant de décrire succinctement les différents types de donnée de Scheme, introduisons les fonctions. Une fonction est un objet qui peut être appliqué à des arguments. Par exemple, on écrira :

Osm> (+ 1 2)
  => 3

qui signifie appliquer la fonction + aux arguments 1 et 2. On remarquera que Scheme utilise la notation infixe, ce qui signifie que l'opérateur est placé en premier. Cela procure au langage, on le verra, une grande homogénéité dans l'écriture des programmes.

En Scheme, les fonctions peuvent avoir 0, 1 ou plusieurs arguments. Certaines fonctions ont un nombre fixé d'arguments, d'autres ont un nombre variable d'arguments. C'est le cas de la fonction + qui permet les écritures :

Osm> (+)
  => 0

Osm> (+ 1)
  => 1

Osm> (+ 1 2 3 4 5 6 7 8 9)
  => 45

Nous verrons plus tard comme définir nos propres fonctions, avec un nombre d'arguments fixe ou variable.


Les booléens

Les booléens sont en général le résultat des comparaisons. En Scheme, ``vrai'' s'écrit #t (pour true) et ``faux'' s'écrit #f (pour false). Se sont les deux seules valeurs possibles pour les booléens. Faisons un peu d'arithmétique binaire à l'aide des opérateurs booléens :

Osm> (and #t #t) 
  => #t

Osm> (and #t #t #f #t)
  => #f

Osm> (or #t #f #t) 
  => #t

Osm> (not #t) 
  => #f

Osm> (not #f)
  => #t

Osm> (boolean? #t) 
  => #t

Osm> (boolean? 1)
  => #f

Avec ces exemples, on remarque que and et or sont des fonctions avec un nombre d'arguments variable. Quant à not, elle n'accepte qu'un seul argument.

Nous introduisons aussi la notion de prédicat. Ces fonctions retournent un booléen et permettent de savoir si l'argument est d'un certain type. Ainsi la fonction boolean? retourne ``vrai'' si son argument est #t ou #f, et ``faux '' dans les autres cas. Tous les types de base de Scheme sont associés à un prédicat. Par convention, le nom des fonctions retournant un booléen se termine par un point d'interrogation.


Les caractères

Les caractères s'écrivent #\xx est le caractère souhaité. Par exemple, le caractère `a' s'écrit en Scheme #\a.

Nous pouvons écrire :

Osm> (char? #\a) 
  => #t

Osm> (char=? #\a #\A) 
  => #f

Osm> (char-ci=? #\x #\X) 
  => #t

Les caractères peuvent principalement être comparés et convertis. La fonction char=? compare deux caractères, en tenant compte de la casse (majuscule, minuscule), alors que char-ci=? est insensible à la casse.

Dans le RxRS[3] sont décrites toutes les autres fonctions relatives aux caractères.


Les erreurs

Que se passe-t-il si nous tapons (char=? 1 #\a) ? Il devrait nécessairement y avoir une erreur signalée parce que la fonction char=? attend deux caractères et elle est appliquée à un entier et un caractère.

Eh bien cette écriture provoque une erreur de la forme :

Osm> (char=? 1 #\a)
  => Error : wait a character instead 
     of `1' in `(char=? 1 #\a)'.

Cette erreur conduit à l'abandon de tout ce que l'interprète était en train de faire et le retour à l'invite. Notons toutefois que la forme du message d'erreur dépend de l'interprète utilisé.


Les chaînes de caractères

Les chaînes de caractères s'écrivent entre guillemets. Il existe un certain nombre de fonctions pour les manipuler :

Osm> "une-chaîne"
  => une-chaîne

Osm> (string #\a #\b #\c) 
  => abc

Osm> (string-set! "abc" 0 #\X) 
  => Xbc

Osm> (string-ref "abc" 2) 
  => #\c

Osm> (string-set ! "ac" 2 #\Z)
  => Error: index `3' out of range 
     in `(string-set! "ac" 3 #\Z)'

Osm> (string=? "abc" "ABC")
  => #f

Osm> (string-ci=? "abc" "ABC")
  => #t

Osm> (string? (string #\a #\b #\c)) 
  => #t

Le dernier exemple montre que les appels aux fonctions peuvent être imbriqués. C'est en fait vrai pour toutes les expressions Scheme, comme nous aurons l'occasion de le voir.


Les nombres

Les nombres s'écrivent pour la plupart naturellement : l'entier 1 s`écrit 1, le nombre réel 1.23 s'écrit 1.23. Scheme propose d'autres syntaxes plus ésotériques comme par exemple #b1001 qui représente l'entier binaire 9.

Comme nous le verrons plus loin, Scheme définit une tour des types numériques. Cette spécification lui permet de toujours donner le ``meilleur'' résultat possible. Ainsi, l'entier 4 divisé par l'entier 3 ne donne pas l'entier 1, comme c'est le cas avec la plupart des autres langages, mais le nombre rationnel 4/3. Ainsi, en Scheme, (4/3)*3 ne s'évalue pas en 3, mais en 4, ce qui est le résultat exact. Vérifions :

Osm> (* (/ 4 3) 3) 
  => 4

Osm> (/ 4 3)
  => 4/3

Lorsque l'on dit que Scheme donne toujours un résultat le plus exact possible, vérifions-le encore avec :

Osm> (* 9999999 9999999 9999999 9999999)
  => 9999996000000599999960000001

Cette fois-ci, le résultat a été converti en entier long. Peu de langages sont si soucieux de l'exactitude des résultats, n'est-ce pas ?


L'alternative

L'alternative permet d'effectuer un choix en fonction d'un prédicat. En Scheme, l'alternative s'écrit (if condition alors sinon), où condition, alors, sinon sont des expressions Scheme. La clause sinon peut être omise. Nous pourrions avoir :

Osm> (if #t 1 2) 
  => 1

Osm> (if #f 1 2)
  => 2

Osm> (if #f 1)
  => #unspecified

La première écriture peut être lue comme ``si vrai alors retourner 1 sinon, retourner 2''.

Notons qu'en Scheme, toutes les valeurs sont supposées vraies, à l'exception de #f. Ceci est une entorse à l'orthogonalité du langage, mais rend, en pratique, l'écriture bien plus lisible. Nous pouvons donc écrire :

Osm> (if 1 3 4)
  => 3

Osm> (if (not "e") #\a #\b)
  => #\b


Affichage

Il existe en Scheme une fonction bien pratique : display permet d'afficher tous les objets existants. C'est une fonction dont le paramètre est l'objet à afficher. Par exemple :

Osm> (display 123)
  => 123#unspecified

123 est l'affichage produit par display. #unspecified est sa valeur de retour.

Pour aller à la ligne après avoir affiché une valeur, on pourra utiliser la fonction newline, comme dans :

Osm> (display #t) (newline)
  => #t
     #unspecified

ou bien :

Osm> (display "1234") (newline)
  => 1234
     #unspecified

Remarquons qu'un entier et une chaîne ne contenant que des chiffres s'affichent de la même manière.

De plus, display est une fonction qui peut prendre des valeurs de n'importe quel type. Nous l'avons illustré avec un entier, un booléen et une chaîne de caractères. Elle sera donc appelée fonction polymorphe.


Abstraire en nommant

Tout langage de programmation permet de donner des noms à des résultats. Sans ce procédé, il serait fastidieux de programmer, car on serait sans cesse obligé de répéter les expressions. C'est la première possibilité d'abstraction d'un langage.

Nommer en Scheme se fait à l'aide de la fonction define :

Osm> (define un-nom 123) 
  => #unspecified

Ici, un-nom est le nom que l'on souhaite définir ; c'est un symbole. 123 est la valeur de définition.

En Scheme, tous les caractères imprimables mis à par les ``blancs'' peuvent être utilisés dans un nom, pour peu qu'il commence par une lettre. Scheme ne fait pas la différence entre les majuscules et les minuscules dans les symboles : il est insensible à la casse.

La valeur de retour de define est une valeur spéciale qui signifie qu'elle n'est pas spécifiée. Comme Scheme est un langage fonctionnel, l'évaluation de toutes les expressions doit avoir un résultat. Cependant, dans certain cas dont celui-ci, la valeur de retour n'est pas spécifiée ; #unspecified est alors retourné !

Comment vérifier que un-nom ``vaut'' maintenant 123 ? Entrons :

Osm> un-nom
  => 123

Que se passe-t-il si on demande la valeur d'un symbole sans l'avoir défini :

Osm> non-définit
  => Error: symbol `non-définit' is 
     unbounded in `non-définit'.

La réponse est claire : Scheme n'accepte pas que l'on évalue des noms sans les avoir préalablement définis.


Abstraire par les fonctions

Dans cette section, nous allons apprendre à définir nos propres fonctions. Cette possibilité est très puissante car elle permet d'augmenter sans limite le nombre des fonctions offertes par le langage.

La forme define est aussi utilisée pour définir les fonctions, en utilisant la syntaxe suivante :

(define (nom param-1 param-2 ... param-m)
  expression-1
  expression-2
  expression-n)

Ici, nom, param-1, param-2, ..., param-m sont des symboles et expression-1, expression-2, ..., expression-n sont des expressions Scheme.

Cette écriture crée une nouvelle fonction dont le nom est nom. Cette nouvelle fonction a m paramètres. Elle aurait tout aussi bien pu n'avoir aucun paramètre. Lorsque l'on applique cette fonction à des arguments, les paramètres prennent la valeur des arguments, puis, les expressions sont évaluées leur ordre d'écriture. La valeur retournée par la fonction est la valeur de retour de la dernière expression. L'ensemble des expressions forme le corps de la nouvelle fonction.

Définissons une fonction qui retourne la somme de ses deux arguments en les ayant préalablement affiché à l'écran :

Osm> (define (somme a b)
       (display "argument 1 = ")
       (display a)
       (newline)
       (display "argument 2 = ")
       (display b)
       (newline)
       (+ a b))
  => #unspecified

L'affichage de #unspecified nous indique que la fonction somme a bien été enregistrée. Nous pouvons donc maintenant l'appliquer à des arguments :

Osm> (somme 3 4)
  => argument 1 = 3
     argument 2 = 4
     7

Nous obtenons bien ce que nous pressentions. Essayons un appel composé :

Osm> (somme 3 (somme 5 6))
  => argument 1 = 5
     argument 2 = 6
     argument 1 = 3
     argument 2 = 11
     14

Cet exemple est très intéressant car il montre la manière dont fonctionne l'interprète Scheme.

Nous appliquons somme à deux arguments, 3 et (somme 5 6). Pour évaluer cette application, l'interprète évalue les trois composantes de cette application, c'est-à-dire le symbole somme, l'entier 3 et l'expression (somme 4 5). L'évaluation du symbole somme retourne la fonction que nous avons définie, l'évaluation de l'entier 3 donne l'entier 3. Enfin, l'évaluation de (somme 5 6) retourne l'entier 11. Mais pour obtenir ce dernier résultat, l'interprète a du évaluer au préalable (somme 5 6) qui se décompose en l'évaluation de somme, de 5 et de 6. Cette sous-évaluation provoque l'affichage des deux premières lignes et retourne l'entier 11. Maintenant que l'interprète dispose des composantes de la première application, il l'évalue, ce qui provoque l'affichage des deux dernières lignes et retourne l'entier 14.

Les affichages effectués avec les fonctions display et newline nous permettent de connaître le fonctionnement de Scheme. Ce sont des fonctions à effets de bord, c'est-à-dire qu'elle modifient l'état du système, l'écran en l'occurrence.


next up previous contents index
Next: Ordre d'évaluation de Scheme Up: Premiers pas Previous: Les outils   Contents   Index
© 1993 to 2001 Erian Concept