TP5 Prog 2 : Continuations et générateurs à la Python Exercice 1

publicité
L3 ENS Cachan
Programmation 2
13 mars 2017
TP5 Prog 2 : Continuations et générateurs à la
Python
Dans ce TP nous allons utiliser les continuations pour imiter un code python
à base de générateurs.
Votre but est de programmer en OCaml un équivalent du générateur de
permutations suivant :
def generate ( n ) :
if n > 1:
for i in range (n -1) :
for s in generate ( n - 1) :
yield s
if ( n % 2 == 0) :
yield (i , n - 1)
else :
yield (0 , n - 1)
for s in generate (n -1) :
yield s
def print_perms ( n ) :
a = range ( n )
for (i , j ) in generate ( n ) :
print a
a[i] , a[j] = a[j] , a[i]
print_perms(n) va afficher toutes les permutations de {0, . . . , n − 1}. Intuitivement, generate génère une suite de transpositions qui engendre toutes les
permutations. Plus précisément, cette suite de transpositions décrit un chemin hamiltonien dans le graphe dont les sommets sont les permutations de
{0, . . . , n − 1} et les arètes les paires de permutations égales à une transposition
près.
L’algorithme utilisé ci-dessus est dû à Heap (cf wikipedia). Pour ce TP, peu
importe de comprendre comment fonctionne cet algorithme.
Exercice 1 : premiers générateurs
On se fixe les types suivants
type
type
type
type
cont = unit -> unit
’a success = ’a - > cont - > unit
failure = cont
’a gen = ’a success -> failure -> unit
Intuitivement, un ’a gen est un générateur de valeurs de type ’a. Le premier
argument qu’il prend est une continuation de succès sk qui fait quelque chose
avec un a généré et continue avec la continuation qu’on lui a spécifié. Le deuxième
argument d’un générateur est une continuation d’échec fk qui est appellée quand
il n’y a plus rien à générer. Les générateurs qui génèrent respectivement un et
aucun élément peuvent être définis ainsi :
1
L3 ENS Cachan
Programmation 2
13 mars 2017
let yield ( a : ’ a ) : ’a gen = fun sk fk -> sk a fk
let stop : ’a gen = fun sk fk -> fk ()
Question 1
Écrivez une fonction qui itère une fonction donnée sur les éléments générés
par un générateur.
val iter : ( ’ a -> unit ) -> ’a gen -> unit
Testez votre fonction.
let () = iter print_int ( yield 1) (* affiche 1 *)
let () = iter print_int stop (* n ’ affiche rien *)
Question 2
Écrivez une fonction yield2 qui crée un générateur à deux éléments.
val yield2 : ’a -> ’a -> ’a gen
Testez votre fonction.
let print_gen printer g =
g ( fun x k -> printer x ; k () ) ( fun () -> print_string " . " )
let () = print_gen print_int ( yield2 1 2)
(* affiche : 12. *)
Question 3
Écrivez une fonction qui crée un générateur range m n qui génère en ordre
croissant les entiers entre m et n-1.
val range : int -> int -> int gen
Testez votre code.
let () = print_gen print_int ( range 2 7)
Exercice 2 : combinaisons de générateurs
Question 4
Écrivez les fonctions qui font la composition séquentielle et le produit de
deux générateurs.
2
L3 ENS Cachan
Programmation 2
13 mars 2017
val seq : ’a gen -> ’a gen -> ’a gen
val prod : ’a gen -> ’b gen -> ’(a ’* ’ b ) gen
Testez votre code.
let () = print_gen print_int ( seq ( range 0 5) ( range 5 10) )
(* affiche 0123456789. *)
let print_pair (x , y ) = Format . printf " (% d ,% d ) " x y
let () = print_gen print_pair ( prod ( range 0 2) ( range 0 2) )
(* affiche (0 ,0) (0 ,1) (1 ,0) (1 ,1) . *)
Question 5
On veut maintenant créér un générateur de ’b à partir d’un autre générateur
de ’a et d’une fonction qui pour un ’a crée un générateur de ’b.
Écrivez la fonction foreach correspondante.
val foreach : ’a gen -> ( ’ a -> ’b gen ) -> ’b gen
Testez votre code.
let prod2 g1 g2 =
foreach g1 ( fun x - >
foreach g2 ( fun y - >
yield (x , y )
)
)
let () = print_gen print_pair ( prod2 ( range 0 2) ( range 0 2) )
(* affiche (0 ,0) (0 ,1) (1 ,0) (1 ,1) . *)
Exercice 3 : mise en oeuvre
Question 6
En vous inspirant du code Python de départ, programmez un générateur de
permutations basé sur l’algorithme de Heap.
3
Téléchargement