Programmation réticulaire

Transcription

Programmation réticulaire
Programmation réticulaire
le langage Javascript
Christian Queinnec
Professeur émérite de l’UPMC
Paracamplus
by C.Queinnec
Programmation réticulaire
1/71
I
Cours dispensé à l’INSTA
I
en octobre 2015
by C.Queinnec
Programmation réticulaire
2/71
Plan
I
Le langage Javascript
I
Prototypes
I
Mise en œuvre
I
Concurrence en Javascript
I
Techniques fonctionnelles
by C.Queinnec
Programmation réticulaire
3/71
Partie 1
Le langage Javascript
by C.Queinnec
Programmation réticulaire
4/71
Avertissement
I
Ce cours n’est pas un cours d’initiation à la
programmation
I
Il passe en revue les éléments spécifiques, voire
atypiques, de Javascript
I
Cette séquence ne s’intéresse qu’au langage
indépendamment des mises en œuvre au sein des
navigateurs.
by C.Queinnec
Programmation réticulaire
5/71
Historique
I
Créé par Brendan Eich en mai 1995 pour Netscape
I
puis re-nommé Javascript !
I
quelques implantations divergentes: javascript,
JScript, EcmaScript
I
et maintenant ES5, ES6 en cours et ES7 en préparation
I
implantations d’évaluateurs Javascript autonomes ou
embarqués en navigateurs (Chrome, Explorer, Firefox,
Opera) : Rhino, Node.js, V8, Nashorn, etc.
by C.Queinnec
Programmation réticulaire
6/71
Caractéristiques
I
langage typé dynamiquement
I
sûr(sic) mais assez permissif
I
procurant des prototypes (plutôt que des objets)
I
gestion mémoire automatique
I
pas nécessairement bien compilable
I
pas nécessairement efficace
Il est utile de regarder les tests associés à ces transparents:
Code pour Jasmine
by C.Queinnec
Programmation réticulaire
7/71
Syntaxe
I
Attention aux fins de lignes implicites!
Écrivez plutôt
v = 3 +
4;
// et non
v = 3
+ 4;
I
commentaires à la C++
I
identificateurs à la C et mots-clés réservés
by C.Queinnec
Programmation réticulaire
8/71
Données
I
nombres (représentés par des flottants 64bits)
I
donc entiers sur 54 bits
I
écritures hexadécimales (préfixe 0x)
ou octales (préfixe 0)
I
isNan, isFinite
I
chaînes de caractères immuables mono-lignes
encadrées par ’ ou "
I
échappement à la C avec \
I
mais attention aux graphies \n ou \uXXXX
I
vrais booléens true et false mais toute valeur a une
valeur de vérité
by C.Queinnec
Programmation réticulaire
9/71
Expressions rationnelles
I
aussi connues comme regexp
I
syntaxe dédiée à la Perl /^f(.*)?[^n-q]+$/
I
Exemples:
i f ( "foobar".search(/^fo+/) >= 0 ) {
var r2 = new RegExp("f(o+)(.*)a");
r e t u r n r2.exec("foobar", "g");
// [ ’fooba’, ’oo’, ’b’, index: 0, input: ’foobar’
}
by C.Queinnec
Programmation réticulaire
10/71
Langage fonctionnel
En Javascript, les fonctions sont des valeurs de première
classe.
functio n add (x, y) {
r e t u r n x + y;
}
var plus = add;
var suivant = f u n c t i o n (a) {
r e t u r n plus(a, 1);
}
suivant("3");
// -> "31"
by C.Queinnec
Programmation réticulaire
11/71
Tables associatives
I
Syntaxe dédiée avec accolades
I
ou création via new Object() (rare!)
I
accès avec notation pointée (champ quelquefois
non modifiable)
I
suppression de clé avec delete
var point = { x: 11, y: 22 };
point.x == point.y / 2;
point.x === point[’x’];
point.z = ’wtf’;
’z’ i n point;
delete point.x;
! ( ’x’ i n point );
by C.Queinnec
// pas de virgule finale
Programmation réticulaire
12/71
Tableaux
I
Syntaxe dédiée avec crochets
I
ou création via new Array(taille)
I
accès avec index (débutant à 0)
I
Tables associatives à clé numérique positive ou nulle.
I
Méthodes prédéfinies pour tableaux, piles, files
var a = [ ’a’, ’b’, [5+6, ’b’] ]; // pas de virgule fi
a[1] == a[2][1];
a.length == 3;
a[44] = ’d’;
a.length == 45;
a[’c’] = 33;
a[’c’] == a.c;
a[’1’] == a[1];
// [ ’a’, ’b’, [ 11, ’b’ ], , , , , , ’d’, c: 33 ]
by C.Queinnec
Programmation réticulaire
13/71
Valeurs spéciales
I
null
I
undefined n’est pas un mot-clé
var truc;
truc === undefined;
truc !== n u l l ;
truc = [];
truc[9] === undefined;
truc.neuf === undefined;
by C.Queinnec
Programmation réticulaire
14/71
Classes prédéfinies
var msg = "Coucou";
typeof(msg) === ’string’;
! ( msg instanceof String);
msg.length === 6;
var cmsg = new String(msg);
typeof(cmsg) === ’object’;
cmsg instanceof String;
cmsg.length === 6;
msg !== cmsg;
msg == cmsg;
Distinguer valeur immédiate et instance d’une classe.
Même chose pour Number et Boolean
by C.Queinnec
Programmation réticulaire
15/71
Conversions
I
Javascript est assez laxiste
I
Notions étranges de Vrai/Faux
I
Conversion nombre vers chaîne et inversement
[4] * [4]
//
[] * []
//
[] * {}
//
[4, 4] * [4, 4] //
{} * {}
//
16
0
NaN
NaN
NaN
Mais aussi (sic):
new Boolean(false) == false
new Boolean(false)?true:false == true
by C.Queinnec
Programmation réticulaire
16/71
Instructions
I
Comme en C pour if, else, while, do, break,
continue, for, switch, case, break, default,
return
I
Comme en Java pour try, catch, throw, finally
I
Et, en Javascript, énumération des clés propres d’un
objet (cf. Object.hasOwnProperty()) et notion de
clé énumérable.
f o r ( var key i n object ) {
var value = object[key];
}
by C.Queinnec
Programmation réticulaire
17/71
Portée
I
I
portée globale
portée fonction
var g0;
// g0 a une portée globale
g1 = 3;
// g1 a une portée globale (non use strict)
functio n f1 (x) {
g2 = x;
// g2 a une portée globale
var l4 = 1; // l4 visibles en f1
i f ( false ) {
var l5 = l3; // l5, l3 visible dans f1
}
f o r ( var l3 i n g0 ) {
// l6 visible dans f1
}
var f2 = f u n c t i o n (y) { // y visible en f2
var ll7 = l6;
// ll7 visible en f2
}
}
ES6 introduit la véritable portée lexicale avec let
by C.Queinnec
Programmation réticulaire
18/71
Environnement global
I
Il existe un objet global
I
Dans un navigateur, il se nomme window
I
En Node.js, c’est global
I
Dans un module Node.js, c’est module
gg = 44;
global.gg === global[’gg’];
var ggg;
global.ggg === global[’ggg’];
ggg = 55;
global.ggg === global[’ggg’];
by C.Queinnec
Programmation réticulaire
19/71
Comparaisons
I
Distinguer l’égalité ==
I
et l’identité ===
I
Toujours préférer l’identité.
NaN !== NaN
NaN != NaN
3 !== 1/0
3 != 1/0
undefined === undefined
n u l l !== undefined
n u l l == undefined
[1] != [1]
{a: 1} != {a: 1}
"chaine" == "cha" + "ine"
"chaine" === "cha" + "ine"
11 == ’11’
by C.Queinnec
11 > 3
"11" < "3"
"11" > 3
1 < 1 + 1e-15
1 >= 1 + 1e-16
[] == false
Programmation réticulaire
20/71
Opérateurs
I
Comme en C
I
+ concatène des chaînes si l’un au moins de ses
arguments est une chaîne
I
Nouveaux mots-clé in, instanceof, typeof, new,
delete
i f ( "key" i n object ) ...
f o r ( var key i n object ) ...
i f ( s instanceof String || typeof(s) == ’string’ ) ...
by C.Queinnec
Programmation réticulaire
21/71
Fonctions
functio n bar (x) { ... }
var foo = f u n c t i on (x) {
r e t u r n x === arguments[0];
}
var hux = f u n c t i on () {
r e t u r n arguments.length;
}
var fns = [ bar, foo, hux ];
fns[1](fns[0]);
hux(11, 22, 33);
hux.call( n u l l , 11, 22, 33);
hux.apply( n u l l , [11, 22, 33]);
// À suivre
// À suivre
Attention, si ES6 impose que les récursions terminales
soient correctement implantées, peu d’évaluateurs le
proposent.
by C.Queinnec
Programmation réticulaire
22/71
Partie 2
Prototypes
by C.Queinnec
Programmation réticulaire
23/71
Objets
I
Javascript n’est pas un langage à objets mais un
langage à prototypes.
I
Les “méthodes” sont des champs dont les valeurs
sont des fonctions.
var o = {
a: 2,
m1: f u n c t i o n (b) {
r e t u r n t h i s .a + b;
}
};
o.m1(5);
o.m2 = f u n c t i o n (c) {
r e t u r n t h i s .a * c;
};
o.m2(5);
by C.Queinnec
Programmation réticulaire
24/71
Constructeurs
functio n Person (firstname, lastname) { // constructeur
t h i s .firstname = firstname;
// en fait initialiseur
t h i s .lastname = lastname;
t h i s .old = 0;
t h i s .setOld = fu n c t i o n (old) { t h i s .old = old}
}
var p1 = new Person("Jean", "Dupont");
p1.lastname == p1[’lastname’];
p1.setOld(42); p1.old == 42;
functio n fullname (msg) {
r e t u r n msg + t h i s .firstname + ’ ’ + t h i s .lastname;
}
fullname.call(p1, ’Hello ’);
p1.fullname = fullname;
p1.fullname(’Hello ’);
by C.Queinnec
Programmation réticulaire
25/71
Blocs d’activation
les blocs d’activation (ou frames) correspondent à la pile
de contrôle.
var g = 100;
var f = f u n c t i o n (x) {
var y = 33;
var g = f u n c t i o n (y) {
r e t u r n x * y; // x libre dans g
}
r e t u r n [ g(x+y), g];
}
f(g+1)[1](44);
by C.Queinnec
Programmation réticulaire
26/71
Blocs d’activation
Avant appel à f:
this=window
window:
g: 100
f:
f
env
by C.Queinnec
Programmation réticulaire
27/71
Blocs d’activation
Pendant l’appel à f(g+1):
this
window
window:
parent
arguments:
g: 100
f:
f
env
x:
101
y:
33
g:
by C.Queinnec
callee:
[0]:
101
g
env
Programmation réticulaire
28/71
Blocs d’activation
Pendant l’appel à f(g+1)[1](44):
window
parent
window:
arguments:
g: 100
f:
f
env
x:
101
y:
g:
33
callee:
[0]:
101
g
env
this
parent
arguments:
y:
by C.Queinnec
44
callee:
[0]:
44
Programmation réticulaire
29/71
Environnements
Toute fonction est une fermeture (closure). Lorsque l’on
souhaite accéder à une variable, on la cherche dans
l’environnement puis dans l’environnement de définition
de la fonction.
var mkcounter = f u n c t i o n (start) {
var count = start;
var counter = fu n c t i o n (increment) {
r e t u r n (count += (increment || 1));
}
r e t u r n counter;
}
var counter1 = mkcounter(10);
var counter2 = mkcounter(100);
counter1();
counter2(1);
counter1();
by C.Queinnec
Programmation réticulaire
30/71
Environnements
Avant l’appel à mkcounter(10):
this=window
window:
mkcounter:
env
by C.Queinnec
Programmation réticulaire
31/71
Environnements
Pendant l’appel à mkcounter(10):
this=window
parent
window:
mkcounter:
env
arguments:
callee:
start:
10
[0]:
count:
10
counter:
by C.Queinnec
10
env
Programmation réticulaire
32/71
Environnements
Après var counter1 = mkcounter(10):
this=window
parent
window:
mkcounter:
arguments:
callee:
counter1:
start:
10
[0]:
count:
10
env
counter:
by C.Queinnec
10
env
Programmation réticulaire
33/71
Environnements
Pendant l’appel à counter2(1) (après counter1()):
this=window
parent
window:
mkcounter:
arguments:
callee:
counter1:
start:
10
[0]:
count:
11
env
counter2:
counter:
10
env
parent
parent
arguments:
callee:
increment:1
[0]:
arguments:
callee:
start: 100
[0]:
100
10
count: 100
by C.Queinnec
1
counter:
env
Programmation réticulaire
34/71
Fermetures
Les fermetures capturent des liaisons pas des valeurs!
var foo = ( f u n c t i o n (shared) {
r e t u r n { get: f u n c t i o n () {
r e t u r n shared;
},
set: f u n c t i o n (v) {
shared = v;
} };
})(10);
foo.get() == 10;
foo.set(22);
foo.get() == 22;
by C.Queinnec
Programmation réticulaire
35/71
Captures d’index
var fnsA = [];
f o r ( var i=0 ; i<5 ; i++ ) {
fnsA.push( f u n c t i o n () { r e t u r n i });
}
fnsA[0]() == fnsA[1]();
fnsA[2]() == 5;
var fnsB = [];
f o r ( var i=0 ; i<5 ; i++ ) {
( fun c t i o n (ii) {
fnsB.push( f u n c t i o n () { r e t u r n ii });
})(i);
}
expect(fnsB[2]()).toBe(2);
expect(fnsB[3]()).toBe(3);
by C.Queinnec
Programmation réticulaire
36/71
Modules de node.js
( functi o n (require, exports, module) {
// {{{ Contenu de module.js:
// module est l’objet global courant
// lister les dependances:
var autre = require(’...’);
// exporter
module.exports.f = f u n c t i o n () { ... };
// }}} fin de module.js
})();
// Dans un autre module:
var m = require(’module.js’); // ie module.exports
// utiliser m.f()
by C.Queinnec
Programmation réticulaire
37/71
Méthode d’instance
Comment partager des méthodes ?
functio n Truc (chose) {
t h i s .chose = chose;
// méthode d’instance:
t h i s .setChose = f u n c t i o n (chose) {
t h i s .chose = chose;
}
}
var t1 = new Truc(1);
var t2 = new Truc(2);
t1.setChose != t2.setChose;
t1.machin = f u n c t i o n () { r e t u r n ’binz’ };
t1.machin();
t2.machin(); // TypeError: undefined is not a function
by C.Queinnec
Programmation réticulaire
38/71
Prototypes
I
Tout objet est lié à son constructeur
I
Toute fonction (donc tout constructeur) est associé à
un prototype.
functio n Truc (chose) {
t h i s .myKind = ’truc’;
t h i s .chose = chose;
}
var t1 = new Truc(1);
var t2 = new Truc(2);
t1.constructor == Truc;
t1.machin = 111;
Truc.prototype.binz = f u n c t i o n (d) {
r e t u r n ( t h i s .chose += d);
}
t1.binz(10);
t2.binz(10);
by C.Queinnec
Programmation réticulaire
39/71
Prototypes
I
En fait, tout objet a un prototype accessible par
Object.getPrototypeOf()
I
Dans o.f, on cherche d’abord f dans o puis dans le
prototype de o puis dans le prototype du prototype
de o, etc.
window:
Truc:
Truc
t1:
prototype:
t2:
prototype
prototype
constructor:
constructor:
chose:
chose:
prototype
2
myKind: truc
1
constructor:
binz:
myKind: truc
machin: 111
by C.Queinnec
Programmation réticulaire
40/71
this
I
À tout moment, il y a un this
I
Au début, this est l’objet global
functio n incr (dx) {
t h i s .x += dx;
t h i s .incr = incr;
return t h i s
}
var c = {x: 10};
incr.call(c, 2).incr(3);
c.x == 15;
by C.Queinnec
// cascade
Programmation réticulaire
41/71
Capture de this
this est un mot-clé pas une variable donc non
capturable dans une fermeture.
functio n Foo () {
t h i s .count = 0;
t h i s .mkincr = fu n c t i o n (d) {
r e t u r n f u n c t i o n () {
r e t u r n ( t h i s .count += d);
}
}
}
var vf = new Foo();
var f = vf.mkincr(2);
f();
vf.count == 0;
f.call(vf);
vf.count == 2;
by C.Queinnec
f u n c t i o n Foo () {
var self = t h i s ;
t h i s .count = 0;
t h i s .mkincr = f u n c t i o
r e t u r n f u n c t i o n ()
r e t u r n (self.cou
}
}
}
var vf = new Foo();
var f = vf.mkincr(2);
f();
vf.count == 2;
Programmation réticulaire
42/71
Création d’instance
new Chose(’a’, 2);
new Chose(’a’, 2)
this
prototype
constructor:
x:
’a’
y:
2
by C.Queinnec
Chose
prototype
Programmation réticulaire
43/71
Héritage
I
Chainer les prototypes.
var p = Object.create(o); // clonage
Object.getPrototypeOf(p) === o;
Object.getPrototypeOf(Object) === n u l l ;
// existe aussi Object.setPrototypeOf(obj, proto)
by C.Queinnec
Programmation réticulaire
44/71
Héritage de classes
functio n Worker (name) {
t h i s .name = name;
};
var getSalary = fu n c t i o n () { r e t u r n 1 };
Worker.prototype.getSalary = getSalary;
functio n Plumber (nickname) {
Worker.call( t h i s , nickname); // super()
t h i s .nickname = nickname;
};
Plumber.prototype = Object.create(Worker.prototype);
Plumber.prototype.constructor = Plumber;
Plumber.prototype.getSalary = f u n c t i o n () {
r e t u r n 10 * Worker.prototype.getSalary(); // super.get
};
by C.Queinnec
Programmation réticulaire
45/71
Héritage de classes
var util = require(’util’);
util.inherits(Plumber, Worker);
Plumber
prototype:
// En Node.js
Worker
prototype:
jean
prototype
constructor:
sayWelcome:
prototype
constructor:
fixPipe:
by C.Queinnec
prototype
constructor:
getSalary:
Programmation réticulaire
46/71
Héritage de classes - remarques
I
(1) permet à un Plumber d’avoir les méthodes d’un
Worker mais
new Plumber().contructor === Worker
I
ce que corrige (2).
functio n Worker (name) {
t h i s .name = name;
};
functio n Plumber (nickname) {
Worker.call( t h i s , nickname); // super()
t h i s .nickname = nickname;
};
Plumber.prototype = Object.create(Worker.prototype); //
Plumber.prototype.constructor = Plumber;
// (2)
by C.Queinnec
Programmation réticulaire
47/71
Variable et champ (ou méthode)
I
Les variables sont recherchés dans l’environnement
puis dans le parent de l’environnement puis etc.
I
Lorsqu’une variable est modifiée, elle est modifiée
dans son environnement de définition.
I
Les champs sont cherchés dans l’objet puis dans le
prototype de l’objet puis dans le prototype du
prototype de l’objet puis etc.
I
Lorsqu’un champ est modifié, la modification est
effectuée dans this.
Javascript reference
by C.Queinnec
Programmation réticulaire
48/71
Un peu d’ordre
I
Javascript est assez complexe
I
à partir d’ES5, ajout de ’use strict’ en tête de
fonction ou de module
I
Plus de delete sur des variables
I
Plus de références sur des variables globales non
déclarées
I
etc.
Cf. notes sur "use strict"
by C.Queinnec
Programmation réticulaire
49/71
Partie 3
Mise en œuvre
by C.Queinnec
Programmation réticulaire
50/71
Mise en œuvre
Pour ce cours:
I
L’IDE à utiliser: Atom
I
L’évaluateur Javascript: Node.js
I
Le gestionnaire de module: npm
I
Les tests sont écrits en Jasmine (cf. spec/tests-spec.js)
I
Le code est vérifié avec jshint
L’installation de node.js installe aussi le gestionnaire de
modules npm. L’option -g installe pour toute la machine
plutôt que pour le seul projet.
cd /usr/local
# nodejs.org
tar --strip-components 1 -xzf node-v0.12.7-linux-x
npm install -g jshint
npm install -g jasmine
dpkg -i atom-amd64.deb # atom.io
by C.Queinnec
Programmation réticulaire
51/71
Node
I
Évaluation d’un fichier avec node
I
Documentation
I
Interagir avec l’OS: l’objet process
I
Créer un serveur HTTP
node file.js
node debug file.js
by C.Queinnec
Programmation réticulaire
52/71
Vérification
I
un linter: jshint
I
paramétré par .jshintrc
I
intégré dans Atom
by C.Queinnec
Programmation réticulaire
53/71
Test
I
TDD (Test Driven Development avec Jasmine ou
Mocha
I
ou plutôt BDD (Behavior Driven Development)
I
avec les usuels beforeAll, beforeEach, afterEach,
afterAll
I
Jasmine supporte les rappels asynchrones
I
Cf. quelques tests pour ce cours
by C.Queinnec
Programmation réticulaire
54/71
Tests
// Jasmine:
describe("my application", function () {
it("should perform concatenations", function () {
expect(3 + "5").toBe(’35’);
});
});
// Mocha:
describe("my application", function () {
it("performs concatenations", function () {
assert.equal(3 + "5", ’35’);
//(3 + "5").should.equal(’35’);
});
});
by C.Queinnec
Programmation réticulaire
55/71
Partie 4
Concurrence en Javascript
by C.Queinnec
Programmation réticulaire
56/71
Concurrence
I
Javascript est mono-tâche: à tout moment, au plus
un code utilisateur est en cours d’exécution.
I
C’est un modèle non-préemptif: un calcul infini, une
attente active de l’utilisateur bloquent donc tout.
I
La bibliothèque d’exécution est multi-tâches et
signale, par le biais de rappels (ou renvois) (ou
callback), la survenue de certains événements.
I
Node procure des bibliothèques avec rappels pour
tous les cas où il peut y avoir une attente.
by C.Queinnec
Programmation réticulaire
57/71
Messages
I
La bibliothèque d’exécution de Javascript maintient
un ensemble de tâches en attente (A) et un
ensemble de tâches pouvant être poursuivies (P).
I
Si une tâche de A n’a plus besoin d’attendre, elle est
transférée en P.
I
Lorsque Javascript a fini de traiter une tâche de P et si
P n’est pas vide, il prend et exécute une nouvelle
tâche de P.
I
En particulier, l’ordre de traitement des rappels ne
peut être prédit.
by C.Queinnec
Programmation réticulaire
58/71
Rappels
var timeout1 = setTimeout( f u n c t i o n () {
console.log(’rappel timeout1’);
}, 123456);
var timeout2 = setTimeout( f u n c t i o n () {
console.log(’rappel timeout2’);
}, 0);
var interval = setInterval( f u n c t i o n () {
console.log(’rappel interval’);
}, 987);
console.log(’on y va’);
f o r ( var i=0 ; ; i++ ) {
i = 0;
// boucle infinie
}
by C.Queinnec
Programmation réticulaire
59/71
Rappels et serveurs
var http = require(’http’);
functio n handle (request, response) {
// npm install httpdispatcher
response.end(
’URL=’ + request.url,
fu n c t i o n () {
console.log(’response sent’);
});
}
var server = http.createServer(handle);
server.listen(61234, f u n c t i o n () {
console.log("Server listening");
});
console.log(’end of file’);
by C.Queinnec
Programmation réticulaire
60/71
Rappels et clients
var util = require(’util’);
http.get(’http://www.insta.fr/’,
f u n c t i o n (response) {
response.on(’data’, f u n c t i o n (data) {
console.log(’response is ’ + data);
});
console.log(’got response’);
}).on(’error’, f u n c t i o n (e) {
console.log("Got error: " + e.message);
});
by C.Queinnec
Programmation réticulaire
61/71
Partie 5
Techniques fonctionnelles
by C.Queinnec
Programmation réticulaire
62/71
Fonctionnelle
var map = f u n c t i on (array, fn) {
var result = [];
f o r (var i=0 ; i<array.length ; i++ ) {
result[i] = fn(array[i]);
}
r e t u r n result;
}
map([1, 2, 3], f u n c t i o n (x) { r e t u r n x * x; });
by C.Queinnec
Programmation réticulaire
63/71
Fonctionnelle
var toggle = f u n c t i o n (f1, f2) {
var counter = 0;
r e t u r n f u n c t i o n () {
i f ( 0 === (counter++ % 2) ) {
r e t u r n f1.apply(toggle, arguments);
} else {
r e t u r n f2.apply(toggle, arguments);
};
};
};
var t = toggle( f u n c t i o n (a) { r e t u r n 2 * a; },
f u n c t i o n (b) { r e t u r n 5 * b; });
t(1); // 2
t(2); // 10
t(3); // 6
by C.Queinnec
Programmation réticulaire
64/71
Memoization
var memo = f u n c t i o n (fn) {
var result;
r e t u r n f u n c t i o n () {
i f ( ! result ) {
result = fn.apply( t h i s , arguments);
}
r e t u r n result;
};
};
var f = memo( f u n c t i o n () {
var limit = 1000*100
f o r ( var i=0 ; i<limit+1 ; i++ ) {
f o r ( var j=0 ; j<limit+1 ; j++ ) {
i f ( i==limit && j==limit ) {
r e t u r n 42;
}}}
});
f();
f();
by C.Queinnec
Programmation réticulaire
65/71
Bibliothèques fonctionnelles
Encore plus de techniques fonctionnelles dans:
bibliothèque Underscore ou son successeur lodash
var __ = require(’lodash);
var f = __.memoize( f u n c t i o n () {
var limit = 1000*100
f o r ( var i=0 ; i<limit+1 ; i++ ) {
f o r ( var j=0 ; j<limit+1 ; j++ ) {
i f ( i==limit && j==limit ) {
r e t u r n 42;
}}}
});
f();
f();
by C.Queinnec
Programmation réticulaire
66/71
Trampoline
Calculer en bas de pile plutôt qu’en haut!
f u nc tio n cpsfact (n, cb) {
f u n c t io n cpsfactaccum (n, r) {
i f ( n === 1 ) {
r e t u r n cb(r);
} else {
r e t u r n cpsfactaccum(n - 1, r * n);
}
}
r e t u r n cpsfactaccum(n, 1);
}
var k = f u nc t i o n (fact100) { ... };
var fact100 = cpsfact(100, k);
by C.Queinnec
Programmation réticulaire
67/71
Trampoline (suite)
f u nc tio n cpsfact (n, cb) {
f u n c t io n cpsfactaccum (n, r) {
i f ( n === 1 ) {
r e t u r n f u n c t io n () { r e t u r n cb(r); }; // <} else {
r e t u r n cpsfactaccum(n - 1, r * n);
}
}
r e t u r n cpsfactaccum(n, 1);
}
var k = f un c t i o n (fact100) { ... };
var fact100 = (cpsfact(100, k))();
// <-
by C.Queinnec
Programmation réticulaire
68/71
Continuation(callback) Passing Style
var euclide = f un c tio n (n, d, cb) {
var q = Math.floor(n / d);
r e t u r n cb(q, n - q*d);
};
// Si pgcd(n, p) = 1 alors il existe u et v
// tels que 1 = u*n + v*p
var bezout = f unc ti o n (n, p, cb) {
r e t u r n euclide(n, p, fu nc tio n (q, r) {
i f ( r === 0 ) {
i f ( p === 1 ) {
r e t u r n cb(0, 1);
} else {
throw new Error("gcd != 1");
}
} else {
r e t u r n bezout(p, r, f un c ti o n (u, v) {
r e t u r n cb(v, u - v*q); });
} }); };
bezout(53, 17, f u n cti on (u, v) { r e t u r n [u, v]; });
by C.Queinnec
Programmation réticulaire
69/71
Promesses
var Peuclide = func tio n (n, d) {
r e t u r n new Promise( fu n ction (resolve, reject) {
i f ( d !== 0 ) {
var q = Math.floor(n / d);
resolve([q, n - q*d]);
} else {
reject(new Error("diviseur nul"));
}
});
};
var pe = Peuclide(53, 17);
pe.then( fun ct i on (value) {
console.log(1, value);
}, fu n ct ion (error) {
console.log(error.toString());
});
pe.then( fu nc t i on (value) {
console.log(2, value);
});
by C.Queinnec
Programmation réticulaire
70/71
Cascades de promesses
Cf. Spécification, économise des try catch
var pe = Peuclide(53, 17);
pe.then( f u nc t i o n (value1) {
i f (...) {
r e t u r n value2;
} else {
throw new Error(...);
} })
.then( f u nc t i o n (value2) { ... })
.catch( f u n ct i o n (error) { r e t u r n value3; })
.then( f u n c t io n (value3) { ... });
by C.Queinnec
Programmation réticulaire
71/71