TD5 : Modes d’appel, Portée
1. Portée
1)
a).
int find_max (int a[], int i, int j)
{
int maxl, maxr, k;
if (i==j)
return a[i];
k = (i+j)/2;
maxl = find_max (a, i, k);
maxr = find_max (a, k, j);
return (maxl > maxr)?maxl:maxr;
}
avec des static
:
int find_max (int a[], int i, int j)
{
static int maxl, maxr, k;
if (i==j)
return a[i];
k = (i+j)/2;
maxl = find_max (a, i, k);
maxr = find_max (a, k, j);
return (maxl > maxr)?maxl:maxr;
}
Les variables sont allouées une fois pour toutes : variables = “globales dans la fonction”.
Seule find_max
a accès à ces variables, mais une fois qu’elles ont été allouées (au premier appel), elles sont stockées au même endroit.
**Opposé des variables statiques ** : les variables auto
⟹ Le programme bogue.
b).
let maxl = ref 0 in
let maxr = ref 0 in
let rec find_max a i j =
if i=j
then a.(i)
else let k = (i+j)/2 in begin
maxl := find_max a i k;
maxr := find_max a k j;
if !maxl > !maxr then !maxl else !maxr
end;;
C’est comme si les variables maxl
, maxr
étaient statiques, au sens précédent.
2).
a).
let x=3 in
let y=x+1 in
let x=12 in
x+y
16 : CamL = langage à portée lexicale
b).
let x=3 in
let f y = x+y in
let x=4 in
f 5
8
c).
let f = (let x=3 in fun y -> x+y) in
f 4
7
d).
let f = (let x=3 in
let y=x+1 in
fun x -> x+y) in
let x=5 in
f x
9
3).
type 'a variable = 'a ref;;
let rec mkvar v = ref v;;
let rec fluid_let (x : 'a variable) (e : 'a) (body : unit -> 'b) =
let save_x = !x in
let result = (x := e; body ()) in
begin
x := save_x;
result
end;;
Attention : fluid_let
avec des ref
: un problème qui a mis 20 ans à être résolu.
let i = 7 in
let f = fun x -> x+i in
let i = 0 in
f 3
let i = mkvar 0;;
fluid_let i 7 (fun () ->
fluid_let f (fun x -> x + !i) (fun () ->
fluid_let i 0 (fun () ->
!f 3)))
retourne 3
c).
(setq i 7)
(defun f (x) (+ x i))
(let ((i 0))
(f 3))
retourne : 3
let i = 7;;
let rec f x = x+i;;
let i = 0 in
f 3;;
retourne : 10
d).
let i = mkvar 0;;
try
fluid_let i 12 (fun () -> raise Failure "arg")
with Failure _ -> !i
12, car on est sorti du fluid_let
(à cause de l’exception) avant d’avoir pu rétablir la valeur de i
.
⟶ Dans fluid_let
: on aurait dû gérer le cas des exceptions.
e).
- Threads :
-
bouts de programme qui s’exécutent en parallèle à l’intérieur d’un programme.
Processus en parallèle : travaillent dans des espaces mémoires séparés
VS
threads : à l’intérieur d’un programme, partagent des données communes.
⟹ On peut gérer des exceptions, mais les threads : c’est un mic-mac impossible à gérer, si deux threads font des fluid_let
sur la même variable : les valeurs de cette variable change selon que l’un soit plus rapide que l’autre, etc…
Exs :
- Serveur Apache : crée des threads ⟶ clients traités en parallèle
- Node.js : paradigme de programmation non bloquante.
4).
(setq i 1)
(defun g (y) (+ x y))
(defun f (x) (+ (g x) i))
retourne 67
NB : Remarquons qu’en C : stdin, stdout sont à liaisn dynamique : pratique dans ce contexte.
5).
$𝛼$-renommage : plus possible facilement en Lisp :
(setq i 1)
(defun g (y) (+ x y))
(defun f (z) (+ (g z) i))
ne retourne plus du tout la même chose !
6).
Scheme : C’est un Lisp à portée statique = portée lexicale
ComonLisp : est statique par défaut, mais toute variable peut être rendue dynamique avec une fonction.
II.
7.
ne fait rien : les variables échangées sont locales.
8.
- En C : avec des pointeurs
- En C++, Pascal : appel par référence
- Java :
- avec des accesseurs
- OU : avec des objets
9.
Fortran : en appel par référence.
SUBROUTINE SWAP (I, J)
INTEGER K
K=I
I=J
J=K
END
NB:
GOD is REAL unless declared INTEGER
: les variables commençant par G
, en Fortran, sont des réels.
a) CALL SWAP(I,J)
: échange I
et J
(appel par référence)
b) CALL SWAP(I+0,J)
:
- selon la norme : met la valeur de `J` dans `I` (appel par référence)
- mais en vrai : échange `I` et `J`
c) CALL SWAP(I+0,J*1)
10.
int abs (int i) {
if (i < 0)
return -i;
else return i;
}
#define ABS(i) ((i)<0)?(-(i)):(i)
a) vu en cours
b) Par valeur le i++
est calculé qu’une fois
VS : Paresseuse : i++
est fait deux fois
c)
fputc
VS putc
:
#define putc(c,f) do { \
int __i = (f)->buflen; \
if (__i->buflen>=MAXBUFLEN) { fflush(f); __i=0; } \
(f)->buf[__i++] = c; \
(f)->buflen = __i; \
} while (0)
void fputc (int c, FILE *f) {
putc (c, f);
}
putc
:- évalue plusieurs fois ses argument, à cause de l’appel paresseux
- mais plus rapide
fputc
:- appel par valeur : argument évalué qu’une fois
- plus lent
inline
: mot clé qui permet de recopier le code tel quel plutôt que de rappeler une fonction :
- permet optimisation, car le compilateur “apprend” le comportement de la fonction appelée
c).
#define
: la définition = sur toute la ligne : pour écrire plusieurs lignes : \
\
⟶ C bouffe le caractère suivant (si c’est un retour chariot, bouffe le passage à la ligne)
__i
: pour être quasi sûr que l’utilisateur n’ait pas utilisé ce nom de variable (même si c’est dans un scope local : attention à c
, pourrait être un f(i)
)
while (e){
// code
}
do {
// code
} while(e)
Pourquoi
do {
// code
} while(0)
?
- Protège le scope
- En fait :
if(e)
putc(c,f);
else
//
Si putc(c,f)
= { // code };
seulement :
alors appel par nom :
if(e)
{ // code };;
else
//
: le else
est ignoré !
11.
real procedure Sum(k, l, u, ak)
value l, u;
integer k, l, u;
real ak;
begin
real s;
s := 0;
for k := l step 1 until u do
s := s + ak;
Sum := s
end;
⟶ somme des valeurs $V[i]$
int hd(stream *s){
return s -> head;
}
stream *tl(stream *s){
return force(s-> next);
}
thunk *cons(int i, thunk *next){
}
hamming :: [Integer]
hamming = 1 : mergeUnique (map (2*) hamming)
(mergeUnique (map (3*) hamming)
(map (5*) hamming))
Ensemble des entiers qui s’écrivent $2^a 3^b 5^c$ triée !
Leave a comment