DEUG MIAS et MASS 2001/2002

Françoise LAMBERT

PETITE DOCUMENTATION DRSCHEME

(version 103) pour les premières années

Introduction :

les différents niveaux de scheme, les valeurs () et #<void>, execute, aides, erreurs

I . Formes spéciales

1. Définitions define, let, let*

2. Structures de contrôle if, cond

3. Connecteurs logiques and, or, not

4. Lambda

5. Evaluation eval, quote

6. Divers begin, begin0, error

 

II . Procédures prédéfinies

1. Les nombres en scheme e, pi, +inf.0, -inf.0, exact?, inexact?

exact->inexact, inexact->exact, numerator, denominator

2. Fonctions pour travailler avec les nombres

number?, integer?, even?, odd?, zero?, positive?, negative?, + ,* , / ,- , sqrt , add1, subb1, abs, log, exp , expt, sin , cos , tan , acos, asin , atan, LCM , GCD , remainder, quotient, max, min , round, truncate, floor, ceiling

3. Listes null?, list?, cons, list, append , car , cdr, length , reverse , member

4. Opérations sur les fonctions : apply , map

5. Egalités eq?, equal?, eqv?, =?

6. l'aléatoire, le temps random, random-seed, time, current-seconds

7. Chaînes string?, string-append, string-length, string=?, string-ci=?, string-ci<?, string<?, substring, symbol->string, string->number, number->string,

string->symbol, , string-ref , format

 

III. Entrées, sorties, fichiers

1. écriture display, newline, printf, fprintf

2. lecture read, read-line

3. fichier open-output-file, output-port?, open-input-file, input-port?, close-input-port, close-output-port , eof-object?

 

IV. Graphisme, fenêtres en scheme

A. Objets graphiques : graphisme et fenêtres, utilisation de la souris, exemple

B. Objets GUI : fenêtres, boutons, zones textes, évènements, actions, exemples

 

 

Introduction

1. Cette version de scheme est bâtie sur les normes du rapport 4 de scheme (que vous trouverez dans l’aide en ligne de DRScheme). Dans la version 103, il y a plusieurs niveaux de scheme. Toute la doc se rapporte au niveau " graphical full scheme " dans lequel il faut impérativement se placer pour travailler correctement (on y accède par choose language dans la barre de menus ou avec CTRL L).

 

2. la valeur () et la valeur #<void>

En DRScheme, la seule valeur "fausse" est #f, les booléens renvoient #f (ou #t bien sûr) . Attention, () n’est pas faux donc, par exemple,

(define (test liste)(if liste 1 2)) appelée avec (test ()) renverra 1.

Il existe en DRScheme une valeur #<void> qui est la valeur retournée par les expressions qui ont un résultat indéfini:

exemples: (define v1 (display "bonjour"))

(define v2 (if (> 1 2) 5))

Dans les deux cas, v1 et v2 ont la valeur #<void>.

On peut le tester grâce au booléen (void? v).

Par contre l’évaluation de v1 ou v2 ne renvoie rien.

 

3. execute , aides, erreurs et debuggage

Pour évaluer un programme écrit dans la partie éditeur, on clique sur execute. Ceci a pour effet de remettre la mémoire de l’évaluateur scheme à zéro: aucune des variables déjà définies n’existe encore (donc, il vaut mieux définir les valeurs au début du programme sous peine de devoir recommencer à chaque fois).

DRScheme met en évidence les expressions délimitées par des parenthèses, possède un bouton pour vérifier la syntaxe, les messages d’erreurs sont relativement clairs, et l’erreur est mise automatiquement en évidence (ou au moins la zone)!! Par ailleurs on dispose d'un bouton stop qui permet à tout moment d'arrêter une boucle en cours.

 

 

remarques utiles:

1. la commande "echap p" permet de ré-obtenir successivement toutes les commandes qui ont déjà été évaluées depuis le début de la session scheme.

2. drscheme est disponible (gratuitement) à l’adresse suivante : www.cs.rice.edu/cs/PLT (ou avec 3 disquettes sur les machines étudiantes).

 

I . FORMES SPECIALES

1. Définitions , affectations

a. (define var exp) définit (ou change) la valeur de var en ajoutant dans l'environnement la paire (var . valeur de exp).

Si var existait déjà , on rajoute une deuxième paire dans l'environnement et c'est cette dernière qui sera ensuite la seule lue.

exp peut être n'importe quelle expression scheme (mais une seule expression); en utilisant une forme lambda, on peut définir var comme étant une fonction.

(define (fonc arg1 arg2 ...argn ) expr1 expr2 expr3 ..) définit la fonction fonc ayant les arguments arg1 arg2 ...argn .

On peut faire des fonctions sans arguments: la syntaxe sera alors:

(define (fonc) expr1 .. exprk) expr1 .. exprk constituant le corps de la fonction

Attention : la forme spéciale define ne peut pas être placée n'importe où dans le corps d'une fonction: les différentes définitions doivent être placées au début (avant les exécutions ).

b. Les formes spéciales let et let* permettent de faire des définitions locales provisoires

(let ( (var1 expr1)

(var2 expr2)

.....

(varn exprn))

<corps du let , les valeurs des vari pouvant être utilisées>

) <---- lors de la fermeture de cette parenthèse, les variables vari reprennent leurs valeurs antérieures (donc éventuellement "n'existent plus").

Lors de l'évaluation d'un let, les valeurs vari reçoivent leur valeurs toutes parallèlement (c'est à dire en même temps).

Avec la forme spéciale let*, qui a par ailleurs exactement la même syntaxe, les valeurs vari reçoivent leurs valeurs les unes après les autres, et donc peuvent dépendre les unes des autres.

ex: (define toto 5), (define titi 3)

(define res1 (let ((toto titi) (titi toto)) (- titi toto))) res1 vaut 2

(define res2 (let * ((toto titi) (titi toto)) (- titi toto))) res2 vaut 0

 

 

2. Structures de contrôle

a. La forme spéciale if permet d'effectuer l'une ou l'autre de deux actions selon la valeur d'une expression avec la syntaxe:

(if <expr> <expr1> <expr2> )

Si la valeur de <expr> n'est pas #f c'est <expr1> qui sera évaluée, sinon c'est <expr2> qui le sera.

Attention il ne doit y avoir pas plus de trois expressions dans le if ; par contre il peut n'y en avoir que deux: (if <expr> <expr1> ) ; dans ce cas si la valeur de <expr> est #f, il sera retourné la valeur #<void>

b. La forme spéciale cond permet d'éviter une succession de if:

(cond ( <cond1> <expr11> <expr12> ... <expr 1n> )

( <cond2> <expr21> <expr22> ... <expr 2k> )

......

( <condp> <exprp1> <exprp2> ... <expr pr> )

(else <expr1> <expr2> ... <expr l> )

)

Si cond1 n'a pas pour valeur #f, alors les expressions <expr11> <expr12> ... .... <expr 1n> seront successivement évaluées (il peut y en avoir un nombre quelconque) et on sort du cond, sinon on recommence avec cond2 etc.. .

A chaque utilisation d'un cond une seule des lignes d'expressions est exécutée.

On peut mettre else au début de la dernière clause: else est évaluée à #t et donc si aucune condition n'était vraie jusque là c'est <expr1> <expr2> ... <expr l> qui seront évaluées.

Si lors d'un appel aucune des conditions n’est réalisée alors la valeur renvoyée est #<void> , ce qui n’a pas beaucoup d’intérêt, mieux vaut généralement mettre un (else #f) .

exemple: pour définir la valeur absolue:

(define (valabs x) (if (>= x 0) x (- x)) ou (define (valabs x)

(cond ((>= x 0) x)

(else (- x))))

 

3. Connecteurs logiques

Rappelons que tout ce qui n’est pas #f en scheme est "vrai" .

Pour relier des conditions on dispose des connecteurs logiques et, ou , non :

(and <expr1> <expr2> ... <expr l> ) évalue successivement les expri de gauche à droite et s'arrête en prenant la valeur #f dès que l'une d'entre elles a cette valeur #f. Si aucune n'a été évaluée à #f, AND retourne la valeur de la dernière expression évaluée .

exemples: (and (integer? 'oui) (even? 'oui)) s'arrête dès la première évaluation (et donc ne se plante pas avec message d'erreur pour la deuxième).

(and (< 2 3) (member 2 '(1 2 3))(+ 1 2)) renvoie 3

attention : and ne peut pas servir de " relieur " d’expression (pour indiquer à la machine de faire ça " et " ça) ; pour ceci voir begin.

(or <expr1> <expr2> ... <expr l>) évalue successivement les expri de gauche à droite et s'arrête dès que l'une d'entre elles a une valeur différente de #f et renvoie cette valeur. Si toutes les expressions sont évaluées à #f alors #f est retourné .

(not <expr>) renvoie #t si expr est pas évaluée à #f et #f sinon. Il y a évidemment une seule expression en argument.

 

4. Lambda

La forme lambda est la forme spéciale qui permet de définir toutes les procédures en scheme (que vous l'utilisiez explicitement ou non).

Sa syntaxe est : (lambda ( arg1 arg2 .. argn) <corps de la procédure >)

Il peut y avoir un nombre quelconque d'arguments y compris aucun : dans ce cas, on mettra () comme liste des arguments.

ex : (define add5 (lambda (x) (+ x 5))) est équivalent à (define (add5 x) (+ x 5))

Cette forme spéciale permet de définir des fonctions n'importe où y compris dans l'appel d'une fonction (voir par exemple dans la fonction map) et permet de retourner une procédure.

ex: (define (rond f g) (lambda (x) (f (g x))))

 

5. Evaluation

(eval expr env) évalue l'expression expr dans l'environnement env (qui est par défaut l'environnement global).

exemple: (eval '(+ (* 5 3) 45) ) retourne 60 , (eval (cons + '( 4 5 6))) retourne 15

(quote obj ) ou 'obj retourne son argument non évalué

exemple: dans l'appel de (cons 2 '(1 2 3)) il ne faut pas évaluer la liste (1 2 3).

 

6. Egalités

(eq? obj1 obj2) teste si les objets sont les mêmes (même pointeur)

(equal? obj1 obj2 ) teste si les objets 1 et 2 sont, soit les mêmes (c'est à dire eq?) soit ont les mêmes valeurs (le test se fait récursivement s'il s'agit d'objets composés).

(eqv? obj1 obj2) teste si deux objets sont, soit les mêmes soit sont le même nombre ou le même caractère.

(= x y) ou (=? x y ) testent si les nombres x et y sont les mêmes

Exemples: si on a : (define a (list 1 2)) , (define b (list 1 2)), (define c a)

(equal? a b) --> #t , (eq? a b ) --> #f , (eq? a c ) --> #t

(eqv? a b) --> #f , (eqv? 'a 'a) --> #t , (equal? "scheme" "scheme") --> #t

 

7. Divers

(begin <expr1> <expr2> ... <expr l>) évalue successivement les <expri> de gauche à droite et renvoie la dernière valeur évaluée. Il permet donc de relier des expressions pour n'en faire qu'une ( par exemple pour mettre dans un if ou dans les définitions d’un let).

(begin0 <expr1> <expr2> ... <expr l>) fait la même chose que begin mais après avoir évalué toutes les expressions il retourne la valeur de la première.

(error ch) cette fonction sert à la gestion d’erreurs : lors de son évaluation, la chaîne de caractères ch est affichée et le programme s’arrête (il n’y a plus aucune évaluation quelque soit la position de cette fonction) et on retourne à scheme.

 

 

 

 

II . QUELQUES PROCEDURES PREDEFINIES

1. Nombres

En DRScheme, les nombres peuvent être "exacts" (entiers ou rationnels) ou "inexacts" (marqués par #i dans la version débutant) et doivent être compris entre -231 et 231 -1 . On dispose des prédicats exact? et inexact?.

On peut utiliser les rationnels (et les calculs sont faits sans valeurs approchées) et les nombres complexes (voir doc pour les fonctions prédéfinies).

+inf.0 et -inf.0 représentent les infinis. exemple: (<5648 +inf.0) renvoie #t

En version débutant pi et e existent (mais plus dans la version full scheme).

Pour les rationnels: on écrit 2/9 par exemple (sans espaces). Les opérateurs +,*, /, - permettent de calculer avec les rationnels.

(exact->inexact rat) permet d'obtenir une valeur approchée de rat.

(inexact->exact nb) permet d'obtenir une fraction dont la valeur est proche de nb

numerator et denominator permettent de récupérer respectivement le numérateur et dénominateur d'un rationnel.

remarque: le calcul "exact" avec des rationnels est plus lent que le calcul approché; dans le cas du calcul d'un terme d'une suite de rationnels, on peut toujours accélérer le calcul (mais en perdant en précision) en prenant comme premier terme un nombre inexact au lieu de exact ( 3.0 à la place de 3) .

 

2. Fonctions pour travailler avec les nombres

(number? x) teste si l'élément x est un nombre (x peut être un objet scheme quelconque)

(integer? x) teste si x est un entier (x peut être un objet scheme quelconque)

(even? x ) teste si l'entier x est pair (x doit être un entier)

(odd? x ) teste si l'entier x est impair (x doit être un entier)

(zero? x ) teste si x est le nombre 0 (x doit être un nombre)

(positive? x) et (negative? x) testent respectivement si x est positif ou négatif.

On dispose de fonctions pour les calculs: pour tous ces calculs il faut évidemment que les arguments soient des nombres, (satisfaisant éventuellement à des conditions supplémentaires pour que les fonctions soient mathématiquement définies ).

+ ,* , / ,- admettent un nombre quelconque d'arguments

(avec par exemple (- 5 2 1 6 4) = 5 - 2 - 1 - 6 - 4 = -8 )

add1 et subb1 ajoute ou soustraie 1 à un nombre: (add1 5)--> 6 (subb1 5) --> 4

sqrt : racine carrée

abs : valeur absolue (abs x) renvoie la valeur absolue de x

remarque : pour prendre l'opposé d'un nombre on utilisera (- x) quand il s'agit d'une variable ; on peut simplifier (- 23) en -23 (il n’y a pas d’espace entre le — et le nombre quand il s’agit d’un nombre et non d’une variable représentant un nombre.

sin , cos , tan , acos, asin , atan fonctions trigonométriques

(log a) rend le logarithme népérien de a (a nombre positif)

(log a b ) rend log a dans la base b (c'est à dire logb a )

(exp a) renvoie ea.

(expt x n ) renvoie la valeur de xn

(LCM n1 n2 .....np ) renvoie le plus petit comm un multiple des entiers n1 n2 .....np (c'est le PPCM)

(GCD n1 n2 .....np ) renvoie le plus grand diviseur commun des entiers n1 n2 .....np (c'est le PGCD)

(max x1 x2 .....xn) renvoie le plus grand des nombres x1 x2 .....xn

(min x1 x2 .....xn) renvoie le plus petit des nombres x1 x2 .....xn

(remainder a b) renvoie le reste de la division euclidienne de a par b (a et b entiers)

(quotient a b ) renvoie le quotient de a par b dans la division euclidienne de a par b (a et b doivent être des entiers).

(round x) arrondit le nombre x à l'entier le plus proche

(truncate x ) enlève les chiffres après la virgule

(floor x) renvoie l'entier inférieur le plus proche de x

(ceiling x ) renvoie l'entier supérieur le plus proche de x

ex: (round 3.8) et (ceiling 3.8) renvoient 4 alors que (truncate 3.8) et (floor 3.8) renvoient 3 (round -3.8) et (floor -3.8) renvoient -4, alors que (truncate -3.8) et (ceiling -3.8) renvoient -3.

3. Listes

(null? x ) teste si l'objet scheme x est la liste vide ().

(list? x) teste si l'objet scheme x est une liste .

(cons x l) ajoute x comme premier élément de la liste l.

ex: (cons 3 ‘( 1 2)) renvoie (3 1 2) , (cons ‘(1 2) ‘( 3 6 )) renvoie ((1 2 ) 3 6)

(list x1 x2 .....xn ) crée la liste (x1 x2 .....xn ): les xi sont des objets scheme quelconques qui sont évaluées: exemple (list 1 2 ‘(+ 2 3) (+ 2 3)) ---> (1 2 (+ 2 3) 5)

(car l) renvoie le premier élément de la liste l

(cdr l ) renvoie la liste l privée de son premier élément

ex : (car '(1 2)) rend 1 (car '((4) 8 6 9)) rend (4)

(cdr '(1 5)) rend (5) (cdr '(1 2 3 4 5)) rend (2 3 4 5)

Les appels de (car ()) ou (cdr ()) ou ceux qui s’y ramènent comme par exemple

(cadr ‘(1))… provoquent un message d’erreur.

Pour composer ces fonctions on peut utiliser des abréviations en mettant un c au début, un r à la fin et a pour car ou d pour cdr au milieu :

ex: (caddr l ) est une abréviation de (car (cdr (cdr l)))

Le nombre de lettres significatives est limité à 4 (en dehors du c au début et r à la fin).

(append l1 l2 .. lp ) concatène les listes l1 l2 .. lp (met bout à bout)

ex : (append '(1 2 3 ) '(4 (5)) '(6 7)) renvoie '(1 2 3 4 ( 5 ) 6 7)

(length l) renvoie le nombre d'éléments de la liste l

ex : (length '(1 2 (3 4 ))) renvoie 3

(reverse l) inverse l'ordre des éléments de la liste l

ex : (reverse '(1 2 (3 4 5))) renvoie ((3 4 5) 2 1)

(member x l) renvoie #f si x n'est pas dans la liste l et sinon la liste l à partir de la première occurence de x dans l

ex : (member 'a '(1 b2 s f a g z a e)) renvoie (a g z a e)

(member '( 1 2) '((1 3) (2 1) (5 6)(1 2) (8 5))) renvoie ((1 2) (8 5))

remarque: il n’est pas gênant que member ne renvoie pas un booléen puisque si x appartient à la liste il sera renvoyé une liste donc quelque chose qui n’est pas #f. Par exemple si on veut rajouter un élément à une liste si il n’y est pas déjà, on pourra utiliser member: (define (ajout x l) (if (member x l) l (cons x l)))

 

4. Opérations sur les fonctions :

(apply op l ) applique l'opérateur op à la liste l (il faut évidemment que l'opérateur puisse "agir" ). ex: (apply + '(1 2 5 6) ) renvoie 14

(apply max '(8 5 10 2 63) ) renvoie 63

(map fonction liste ) applique fonction à chaque élément de liste et retourne la liste des résultats.

ex: (map sqrt '(1 4 36 81)) retourne (1 2 6 9)

(map cdr '((1 2 3) (4 5 6) (7 8 9))) retourne ((2 3) (5 6 )(8 9))

(map (lambda(x) (* x x x)) '(1 2 3)) retourne (1 8 27)

 

5. L'aléatoire et le temps

a. le générateur aléatoire

(random n ) retourne un nombre entier aléatoire situé entre 0 et n - 1

Pour réinitialiser le générateur aléatoire, la commande est (random-seed p) .

Il faut savoir que l’aléatoire est répétable : après la même ré-initialisation, et la même suite de commandes (random machin) on obtiendra les mêmes résultats.

Donc pour ne pas risquer à chaque fois qu'on lance le scheme puis le même jeu d'avoir les mêmes tirages, il est bon d'insérer dans le programme au lancement une initialisation "aléatoire" avec un (random-seed (current-seconds))

b. le temps

(time expr ) affiche le temps d’exécution de l’évaluation de expression expr et renvoie le résultat de celle-ci.

(current-seconds ) renvoie le temps courant en secondes

 

7. Les chaînes

Une chaîne est délimitée par des guillemets : ex "  vive le scheme en 2002  "

(string? x) teste que l'objet scheme x est une chaîne.

(string-length c) renvoie la longueur de la chaîne c ; les blancs sont des éléments comme les autres d’une chaîne.

ex: (string-length "scheme") --> 6, (string-length "sche me") --> 8

(string-ref c i) renvoie l’élément numéro i de la chaîne (compté à partir de zéro).

ex: (string-ref "bonjour" 2) --> n

(substring c n1 n2) renvoie la sous-chaîne de caractères de c dont le premier caractère est situé à la position n1 et le dernier à la position n2-1 (le tout compté à partir de zéro). ex: (substring "scheme" 2 5) --> "hem"

(string-ci=? c1 c2) et (string=? c1 c2) testent l'égalité des chaînes c1 et c2; string=? tient compte des majuscules alors que string-ci? n'en tient pas compte.

exemples: (string=? "scheme" "Scheme") renvoie #f

(string-ci=? "scheme" "Scheme") renvoie #t

(string<? c1 c2) (string<=? c1 c2) (string-ci<? c1 c2) (string-ci<=? c1 c2)

(string>? c1 c2) (string>=? c1 c2) (string-ci>? c1 c2)(string-ci>=? c1 c2)

testent l'infériorité dans l'ordre alphabétique des chaînes c1 et c2;

string=? tient compte des majuscules alors que string-ci? n'en tient pas compte.

ex: (string<? "dupont" "durant") --> #t , (string-ci<? "dupont" "durant") --> #t

(string<=? "dupont" "dupont") --> #t, (string-ci<? "Dupont" "dupont") --> #t

(string<? "Dupont" "dupont") --> #f

(string-append c1 c2) permet de concatèner deux chaînes

exemple: (string-append "bonjour" "pierre ") ---> "bonjourpierre"

(symbol->string s) transforme un symbole en une chaîne de caractères.

(string->symbol ch) transforme une chaîne en un symbole (si c’est possible).

(string->number ch) transforme une chaîne en nombre (si c’est possible).

(number->string n) transforme un nombre en chaîne.

(format ch args .......) fonctionne comme la fonction printf, (voir plus loin), sauf qu’elle n’affiche rien mais renvoie la chaîne de caractère obtenue en remplaçant dans ch (chaîne de caractères spéciale comprenant des symboles ~a) les symboles ~a par les valeurs des variables données par args. Cette fonction est indispensable pour écrire dans les fenêtres graphiques.

ex: (format " le resultat du calcul avec ~a et ~a comme paramètres est ~a" 1 8 56) renvoie la chaîne : "le resultat du calcul avec 1 et 8 comme paramètres est 56 "

 

 

 

 

 

III. ENTREES/SORTIES/FICHIERS

a. écriture

Lors de l'exécution d'un programme écrit en scheme, seule est affiché à l'écran la valeur de la dernière expression évaluée par la machine. Il existe donc des fonctions d'écriture permettant d'avoir d'autres affichages ou d'écrire "ailleurs".

(display <expr> port) port est un argument optionnel qui indique où écrire, (si il n'y a qu'un argument l'affichage se fera à l'écran), permet l'affichage dans port de expr: <expr> est une expression scheme qui sera évaluée. La valeur renvoyée par display est #<void> .

ex: (display (+ 2 3)) affichera 5.

Pour écrire du texte, on le mettra entre guillemets (il sera alors recopié de façon identique) et donc (display "(+ 2 3)") affichera (+ 2 3) .

remarque: display ne va pas à la ligne entre deux appels de display,

exemple: (begin (display 1) (display 2)(display 3)) affichera 123.

 

(newline port) permet d'aller à la ligne dans le port d'écriture.

(fprintf port ch args ....) écrit dans le port de sortie port la chaîne obtenue à partir de ch qui est une chaine de caractère particulière et des arguments (en nombre quelconque ) args. Dans la chaine ch on écrira

~n pour obtenir un saut de ligne à cet endroit,

~a sera remplacé à l’affichage (avec un display) par la prochaine variable dans args

~s idem avec write

~c idem avec write-char

ex: (fprintf port "la somme de ~a et ~a est ~a, ~n la différence est ~a "

2 3 (+ 2 3) ( - 2 3) )

affichera dans port : la somme de 2 et 3 est 5

la différence est -1

 

printf fait la même chose sauf que l’on ne doit pas préciser de port et que l’affichage se fera dans le port courant. On utilisera donc printf pour écrire à l’écran.

 

 

b. lecture

La procédure (read port ) permet d'aller lire des données dans le port d'entrée port (s'il n'y a pas d'argument port, la lecture se fera à l'écran).

remarque: read n'évalue pas l'expression qu'on lui fourni mais la "retransmet" telle quelle (on pourra utiliser la fonction eval pour évaluer ce qui a été lu):

ex : (define (demande)

(let ((op (begin (display "entrez une opération")(read)))

(x (begin (display "entrez le premier nombre")(read)))

(y (begin (display "entrez le deuxième nombre")(read))))

(printf   "~a ~a ~a = ~a " x op y ((eval op) x y))))

(read-line port) lit la prochaine ligne dans port et la transforme en chaîne.

 

 

c. Les fichiers : Pour pouvoir être utilisé comme port d’écriture, un fichier sur disque ou disquette doit être "ouvert en écriture".

(open-output-file nom-de-fichier arg-optionnel) renvoie le port de sortie crée par l’ouverture du fichier nom-de-fichier (nom-de-fichier est une chaîne de caractères comme par exemple "a:tp1" ). arg-optionnel est là pour le cas ou nom-de-fichier correspond à un fichier déjà existant et peut prendre les valeurs:

- ‘error : on signalera qu’un tel fichier existe déjà (c’est la valeur prise par défaut)

- ‘replace : on remplacera le fichier existant par celui crée

- ‘truncate : on écrira par -dessus les précédentes données

- ‘extend : on écrira à partir de la fin du fichier déjà existant

          &nbs p;                           &n bsp;            &nbs p;                           &n bsp;            &nbs p;                           &n bsp; 

(output-port? symbole) renvoie true si le symbole représente un port de sortie.

(open-input-file nom-de-fichier) renvoie le port d’entrée créé par l’ouverture du fichier "nom-de-fichier", le fichier devant déjà exister impérativement.

On peut alors lire dans le fichier grâce aux commandes classiques de lecture (read, read-line) . La lecture est effectuée séquentiellement: à chaque lecture, on avance dans le fichier d’une expression scheme avec read, d’une ligne avec read-line .

(input-port? symbole) renvoie #t si le symbole représente un port d’entrée.

(close-input-port symbole) ferme le port d’entrée qui est dans symbole.

(close-output-port symbole) ferme le port de sortie qui est dans symbole.

Lorsqu’on a fini d’utiliser un fichier, il est impératif de le fermer. Attention, le nombre de fichiers pouvant être ouverts en même temps est limité: le message d’erreur en cas de dépassement est du style "too many open files".

(eof-object? expression) renvoie true si l’expression est une fin de fichier. La fin de fichier est un objet scheme qui peut être lu toutes les fonctions de lecture.

 

 

 

IV. GRAPHISME ET FENETRES EN DRSCHEME

A. DESSINS ET FENETRES GRAPHIQUES

1. Dessiner dans des fenêtres

Pour pouvoir utiliser les fonctions graphiques (toutes celles qui suivent et celles pour la souris) il faut d’abord " charger la librairie des fonctions graphiques " en évaluant la commande : (require-library " graphics.ss " " graphics ")

puis ouvrir le mode graphique en évaluant la commande : (open-graphics) .

Quand le graphisme n’est plus nécessaire, il faut évaluer la commande

(close-graphics).

En drscheme, l'écran a une taille de 640 sur 400 en mode graphique.

a . Les fenêtres

(open-viewport nom largeur hauteur) crée une fenêtre portant nom sur le bandeau comme titre (nom étant une chaîne de caractères ), positionnée en haut à gauche de l’écran, et dont les dimensions sont largeur et hauteur. Cette fenêtre peut être déplacée grâce à la souris.

Pour pouvoir utiliser cette fenêtre ultérieurement, il faut lui donner un nom lors de la création à l’aide de define ou de let, le titre donné par nom ne servant qu’à l’affichage. Cette fonction retourne un objet de type "viewport".

remarque: la souris permet d’’agrandir la taille apparente de la fenêtre, mais l’affichage dans la fenêtre ne se fera que dans les dimensions de la création.

(close-viewport fenetre) ferme la fenêtre (fenetre créée par un open-viewport).

 

b. les points ou pixels

Il existe un type position (posn en abrégé) prédéfini possédant deux champs : x et y correspondants respectivement à l’abscisse et l’ordonnée. Pour créer un point on dispose du constructeur (make-posn a b ) qui crée le point de coordonnées a et b (et c’est la seule façon d’avoir un point utilisable en mode graphique). Pour récupérer abscisse et ordonnée d’un point, on dispose des sélecteurs (posn-x pt) et (posn-y pt) .

c. les couleurs

Par défaut, la couleur est le noir. Pour créer une autre couleur, on dispose du type rgb (correspondant à red green blue).

(make-rgb r g b) où r g b sont des nombres compris entre 0 et 1 (correspondant aux proportions des différentes couleurs) renvoie une couleur.

(rgb-r coul) , (rgb-g coul) , (rgb-b coul ) permettent de récupérer les valeurs de r g et b pour la couleur coul (forcément obtenu par un make-rgb).

On peut aussi, pour simplifier, utiliser une couleur prédéfinie en tapant son nom entre guillemets (chaînes de caractères) en anglais. Si vous tapez une couleur inexistante, le message d’erreur sera un peu bizarre, il correspondra en fait que n’ayant pas trouvé un équivalent de ce que vous avez tapé, le programme aura mis () comme couleur et que les fonctions graphique "profondes" ne peuvent pas fonctionner.

Quelques couleurs pouvant être utilisées: "yellow", "orange", "red", "pink", magenta", "purple", "cyan", "blue", "gree", "brown", "grey", "black", "white".

Remarque : dans la version 103, il y a un BUG : certaines fonctions graphiques connaissent les couleurs style " blue " (comme draw-string) d’autres ne les connaissent pas (comme draw-rectangle). Il est donc plus prudent d’utiliser les RGB. Il existe aussi une vingtaine d’index de couleur (nombres compris entre 0 et 20) mais les couleurs sont bizarres et peu variées.

 

d. écrire, dessiner dans une fenêtre

L’argument couleur est toujours facultatif. Par défaut la couleur est le noir.

!! attention la syntaxe est particulière (mais logique !)

!! attention: tous les points doivent être obtenus avec un make-posn

((draw-viewport fenetre)) colorie la fenêtre en noir

((draw-pixel fenetre) pixel coul ) colorie le point pixel dans la couleur coul

((draw- line fenetre) point1 point2 coul ) trace le segment point1 point2

((draw-rectangle fenetre) point largeur hauteur coull) trace les bords du rectangle dont point est le coin supérieur gauche et dont la largeur et la hauteur sont donnés en paramètres.

((draw-solid-rectangle fenetre) point largeur hauteur coul ) trace le rectangle plein dont point est le coin supérieur gauche et dont la largeur et la hauteur sont donnés en paramètres.

((draw-ellipse fenetre) point larg haut coul ) dessine l’ellipse inscrite dans le rectangle défini par point haut et larg ; en prenant haut=larg, on obtient un cercle.

((draw-solid-ellipse fenetre) point larg hautcoul ) dessine l’ellipse pleine inscrite dans le rectangle défini par point haut et larg ; en prenant haut= larg, on obtient un disque.

((draw-string fenetre) point chaine coul) écrit chaine (qui est une chaine de caractère) dans la fenêtre à partir de point ; pour obtenir une chaîne à partir de plusieurs paramètres on peut utiliser format.

Dans toutes ces fonction, en mettant le mot clear à la place de draw, on "efface " le dessin correspondant. En particulier ((clear-viewport fenêtre)) nettoie la fenêtre

 

 

2. LA SOURIS

Certaines des fonctions utiles pour la gestion de la souris, retournent un "mouse descriptor" c’est à dire la représentation interne du type d’évènement souris qui a eu lieu (appuyer sur un des boutons, relâcher ....), d’autres fonctions servent à analyser ce "mouse descriptor".

Les évènements souris sont stockés dans un buffer et retournés dans l’ordre où ils ont été stockés. On peut réinitialiser ce buffer à vide.

(get-mouse-click fenetre) attend éventuellement qu’un évènement souris se produise et retourne un "mouse descriptor ".

(ready-mouse-click fenetre) retourne un "mouse descriptor " si un évènement souris est disponible dans le buffer, #f sinon. Ceci permet de récupérer des "clicks" anciens.

(query-mouse-posn fenetre) retourne la position du curseur de la souris si celui-ci est dans la fenêtre ou #f si celui-ci est en dehors de la fenêtre (il s’agit de la position effective de la souris sans que l’on ait besoin de cliquer).

(mouse-click-posn mouse-click) retourne qu’elle était la position de la souris quand l’évènement décrit par mouse-click a eu lieu.

(left-mouse-click? mouse-click) teste si l’évènement décrit par mouse-click est le click du bouton gauche de la souris.

(middle-mouse-click? mouse-click) teste si l’évènement décrit par mouse-click est le click du bouton du milieu de la souris.

(right-mouse-click? mouse-click) teste si l’évènement décrit par mouse-click est le click du bouton droit de la souris.

(viewport-flush-input fenetre) vide le buffer d’évènements souris.

 

 

3. Un exemple d'utilisation du graphisme et de la souris:

Le programme suivant, ouvre le graphisme, dessine une fenêtre avec dessiné dedans deux rectangles, l'un marqué oui l'autre marqué non, attend un click souris et renvoie "oui" ou "non" selon la zone où on a cliqué. Si le click s'est produit en-dehors des zones oui et non , un message s'affiche et l'utilisateur doit re-cliquer.

(require-library " graphics.ss " " graphics ")

(open-graphics)

(define (essai)

(define fe (open-viewport "reponse" 200 100))

((draw-rectangle fe) (make-posn 20 20) 37 20)

((draw-rectangle fe) (make-posn 85 20) 37 20)

((draw-string fe) (make-posn 24 35) "OUI")

((draw-string fe)(make-posn 87 35) "NON")

(let aux ()

(let* ((truc (get-mouse-click fe))(pos (mouse-click-posn truc))

(x (posn-x pos)) (y (posn-y pos)))

(cond ((and (> x 20)(< x 57)(> y 20)(< y 40)) 'oui)

((and (> x 85)(< x 142)(> y 20)(< y 40)) 'non)

(else (display "veuillez cliquer dans la case!") (aux))))))

Il est compliqué de récupérer une réponse tapée au clavier dans ce type de fenêtre (impossible de faire un (read fenêtre) car la fenêtre n'est pas un port) c’est cependant tout à fait faisable.

En fait ce type de programme se fera beaucoup mieux avec les fonctions GUI, qui par contre ne seront pas les plus pratiques forcément pour dessiner . On peut mélanger les deux types de graphisme.

 

 

B. LES OBJETS GUI

Grâce aux fonctions de ce chapitre , on peut faire des fenêtres de dialogue avec l’utilisateur, " style windows " avec zones texte, boutons etc….. Pour avoir une vision complète des fonctions existantes, voir la doc, nous ne donnerons que les fonctions principales " pour débuter ". Il faut savoir qu’on travaille nécessairement en " programmation objet ".

1. Les fonctions les plus importantes :

a. fenêtres :

(define w (make-object frame% " toto ") ) crée une fenêtre dont le titre est "toto.

(send w show #t) ou (send w show #f) permettent d’afficher ou de cacher la fenêtre.

b. boutons :

(define b (make-object button% " texte1 " w action)) crée un, bouton sur lequel sera marqué texte11 dans la fenêtre w avec une action w (c’est une fonction) qui se déclenchera si on clique dessus.

(send b get-label) renverra le titre du bouton (ici texte1)

 

c. zone texte

(define t (make-object text-field% " texte " w action)) crée une zone texte (c’est à dire une zone où l’utilisateur peut écrire) placée dans la fenêtre w , zone précédée du texte texte et avec une action w (c’est une fonction) qui se déclenchera….

(send t get-label) renverra le titre (c’est à dire ici texte)

(send t get-value) renverra ce qui aura été tapé dans la zone texte

(send t set-value texto) modifiera le texte tapé en y mettant texto

(send t focus) positionnera le curseur dans la zone d’écriture

d. événement

(send e get-event-type) renverra le type d’événement (exemple un click, une entrée…)

e. les actions

Elles ont toujours deux paramètres l’objet et l’événement et doivent toujours figurer dans les fonctions qui nécessitent une action. Par contre, l’action peut être void s’il n’y a rien à faire.

Remarque : par défaut, les différents objets d’une fenêtre se placent verticalement les uns en dessous des autres. Pour les positionner " comme on veux ", il faudra définir des " panels " …..

2. des exemples :

à on veut afficher une fenêtre avec deux boutons, un marqué OUI l’autre NON ; quand l’utilisateur a cliqué sur l’une des zones, on fait une test sur ce qui était marqué sur le bouton …...

(define w (make-object frame% " essai "))

(define (act o e)

(let ((rep (send o get-label)))

(if (equal ? rep " OUI " ) (display " vous aviez cliqué sur oui ")

(display " vous aviez cliqué sur non ")))

(define b1 (make-object button% " OUI " w act))

(define b2 (make-object button% " NON " w act))

(send w show #t)

à une version du jeu du nombre mystérieux :

(define (jouer)

(define jeu (make-object frame% "jeu du nombre secret"))

(send jeu show #t) (boucle jeu 1 (random 1000)))

 

(define (boucle f k x)

(define (act o e)

(if (eq? (send e get-event-type) 'text-field-enter)

(let ((n (string->number (send nb get-value))))

(cond ((= n x)(gagne f res k))

((< n x)(send res set-value "trop petit") (boucle f (+ k 1) x))

(else (send res set-value "trop grand") (boucle f (+ k 1) x))))))

(define nb (make-object text-field% "votre proposition" f act))

(define res (make-object text-field% "resultat" f void))

(send nb focus))

 

(define (gagne jeu res n)

(define (action o e) (send jeu show #f))

(define bok (make-object button% "ok" jeu action))

(send res set-value (format " vous avez gagné en ~a coups" n)))