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