1 Rappels de OCaml
Transcription
1 Rappels de OCaml
Magistère STIC Module Programmation 2008–2009 Feuille de TP n◦ 1 TP1 1 Rappels de OCaml 1.1 1.1.1 Compilation À la main il y a deux compilateurs : ocamlc et ocamlopt. On n’utilisera que ocamlc. # ocamlc --help Usage: ocamlc <options> <files> Options are: -a Build a library -c Compile only (do not link) -cc <command> Use <command> as the C compiler and linker -cclib <opt> Pass option <opt> to the C linker -ccopt <opt> Pass option <opt> to the C compiler and linker -config print configuration values and exit -custom Link in custom mode -dllib <lib> Use the dynamically-loaded library <lib> -dllpath <dir> Add <dir> to the run-time search path for shared libraries -dtypes Save type information in <filename>.annot -for-pack <ident> Ignored (for compatibility with ocamlopt) -g Save debugging information -i Print inferred interface -I <dir> Add <dir> to the list of include directories -impl <file> Compile <file> as a .ml file -intf <file> Compile <file> as a .mli file -intf-suffix <string> Suffix for interface files (default: .mli) -intf_suffix <string> (deprecated) same as -intf-suffix -labels Use commuting label mode -linkall Link all modules, even unused ones -make-runtime Build a runtime system with given C objects and libraries -make_runtime (deprecated) same as -make-runtime -modern (deprecated) same as -labels -noassert Don’t compile assertion checks -noautolink Don’t automatically link C libraries specified in .cma files -nolabels Ignore non-optional labels in types -nostdlib do not add default directory to the list of include directories -o <file> Set output file name to <file> -output-obj Output a C object file instead of an executable -pack Package the given .cmo files into one .cmo 1 -pp <command> Pipe sources through preprocessor <command> -principal Check principality of type inference -rectypes Allow arbitrary recursive types -thread Generate code that supports the system threads library -unsafe No bounds checking on array and string access -use-runtime <file> Generate bytecode for the given runtime system -use_runtime <file> (deprecated) same as -use-runtime -v Print compiler version and location of standard library and exit -version Print compiler version and exit -verbose Print calls to external commands -vmthread Generate code that supports the threads library with VM-level scheduling -w <flags> Enable or disable warnings according to <flags>: C/c enable/disable suspicious comment D/d enable/disable deprecated features E/e enable/disable fragile match F/f enable/disable partially applied function L/l enable/disable labels omitted in application M/m enable/disable overriden method P/p enable/disable partial match S/s enable/disable non-unit statement U/u enable/disable unused match case V/v enable/disable hidden instance variable Y/y enable/disable suspicious unused variables Z/z enable/disable all other unused variables X/x enable/disable all other warnings A/a enable/disable all warnings default setting is "Aelz" -warn-error <flags> Treat the warnings of <flags> as errors, if they are enabled. See option -w for the list of flags. Default setting is "a" (warnings are not errors) -where Print location of standard library and exit -nopervasives (undocumented) -dparsetree (undocumented) -drawlambda (undocumented) -dlambda (undocumented) -dinstr (undocumented) -use-prims <file> (undocumented) - <file> Treat <file> as a file name (even if it starts with ‘-’) -help Display this list of options --help Display this list of options C’est beaucoup trop d’informations : les options interessantes pour le moment sont : # ocamlc --help Usage: ocamlc <options> <files> Options are: -c Compile only (do not link) -o <file> Set output file name to <file> --help Display this list of options 2 premier exemple : # cat test.ml print_endline "Salut" La compilation du caml ( comme pour beaucoup d’autres langages ) se passe en 2 étapes : compilation puis édition de liens. Au début, ocamlc va creer un fichier .cmi et un .cmo # ocamlc -c test.ml # ls test.cmi test.cmo test.ml Le .cmi est le fichier d’interface ( on vera plus tard à quoi ća sert ), le .cmo est le fichier objet. Le fichier objet contient le code compil’e. # file * test.cmi: Objective caml interface file (.cmi) (Version 010). test.cmo: Objective caml object file (.cmo) (Version 006). test.ml: ASCII text ensuite il faut faire l’édition de lien ( linker ). Dans le .cmo les appels aux bibliotheques ne sont pas compilés ( l’appel a print endline pointe dans le vide ), l’édition de lien va rassembler tous les bouts de code ensemble et faire les liens comme il faut. # ocamlc test.cmo # ls -l a.out* test.cmi test.cmo test.ml Ça nous a crée un bel executable a.out # file a.out a.out: a /usr/bin/ocamlrun script text executable # ./a.out Salut Pour choisir le nom du fichier de sortie : # ocamlc -o test test.cmo # ./test Salut En fait, on aurait pu aller plus vite : 3 # ocamlc -o test test.ml L’option -c sert quand l’on doit compiler plusieurs fichiers # cat a.ml let f () = print_endline "Salut" # cat b.ml A.f () Forcement si on compile directement ça ne marche pas... # ocamlc b.ml Error while linking b.cmo: Reference to undefined global ‘A’ Pour compiler : # ocamlc -c a.ml # ocamlc -c b.ml # ocamlc -o test a.cmo b.cmo Attention, l’ordre compte : # ocamlc -o test b.cmo a.cmo Error while linking b.cmo: Reference to undefined global ‘A’ 1.1.2 interface Pour programmer proprement, il faut séparer les bouts de code. Et pour faire cela proprement, il faut faire des interfaces. # cat c.ml let n = 1 let m = 2 # cat c.mli val n : int # cat d.ml print_int C.n;; print_int C.m;; Et ça ne marche pas car m n’est pas dans l’interface du module C # ocamlc -c a.ml # ocamlc -c b.ml File "b.ml", line 2, characters 10-11: Unbound value A.m 4 il faut ajouter val n : int au mli # ocamlc -c a.ml # ocamlc -c b.ml # ocamlc -o machin a.cmo b.cmo 1.1.3 Makefiles Comme vous êtes fainéants, vous ne voulez pas tapper tout ça à chaque fois. Les makefiles sont faits pour vous TADAAAA. Un makefile simple c’est ça : # cat Makefile test: d.cmo c.cmo ocamlc -o test c.cmo d.cmo d.cmo: d.ml c.cmi ocamlc -c d.ml c.cmo: c.ml ocamlc -c c.ml c.cmi: c.mli ocamlc -c c.mli Pour l’utiliser : # make ocamlc ocamlc ocamlc ocamlc -c -c -c -o c.mli d.ml c.ml test c.cmo d.cmo Ouuuaahhh ! Bon maintenant, que veulent dire ces lignes ? cible: dépendances commande Et comme on est vraiment fainéant, on va se débrouiller pour passer moins de temps à écrire le Makefile test: d.cmo c.cmo ocamlc -o $@ $^ 5 Le $@ est remplacé par la cible et $ˆ par les dépendances et $¡ par la première. Pour l’instant, on est d’acord, ça ne sert à rien. Mais attendez de voir la suite ! La où ça deviens utile, c’est quand on sort les wilcards : %.cmi: %.mli ocamlc -c $< %.cmo: %.ml ocamlc -c $< le %.cmi veux dire “tous les fichiers .cmi”. Un truc bien avec make, c’est qu’il ne recompile pas toujours tout quand c’est inutile. # make test make: « test » est à jour. C’est particulièrement utile quand on commence a avoir beaucoup de code. Après, quand vous voudrez rendre vos projets à votre gentil prof, vous voudrez être sympa avec lui et lui rendre une archive propre ( sans tous les fichiers de compilation intermédiaires ). Pour le faire sans se fatiguer, on fait une règle “clean” : clean: rm -f *.cmi *.cmo *~ test Comme c’est une règle qui ne génere pas de fichier, si il traine un fichier clean dans le dossier, make ne va pas executer la règle # touch clean # make make: « clean » est à jour. Pour corriger ça, on ajoute ça au début du Makefile : .PHONY: clean # make clean rm -f *.cmo *.cmi test Tout ça, c’est bien beau, mais il faut encore donner les dépendances entre les modules pour compiler. Pour nous aider, il y a ocamldep 6 # ocamldep *.ml c.cmo: c.cmi c.cmx: c.cmi d.cmo: c.cmi d.cmx: c.cmx On oublie les cmx, et il nous reste : # ocamldep *.ml c.cmo: c.cmi d.cmo: c.cmi Et ça ressemble vraiment à des bouts de Makefile. .depend: %.ml %.mli ocamldep $@ > .depend include $(.depend) Pour finir voila à quoi ressemble le Makefile du projet : %.cmx: %.ml ocamlopt -c $< %.cmo: %.ml ocamlc -c -g $< %.cmi: %.mli ocamlc -c $< .PHONY: all projet.tgz # Compilation parameters: CAMLOBJS=error.cmo cparse.cmo \ ctab.cmo clex.cmo verbose.cmo genlab.cmo compile.cmo \ main.cmo CAMLSRC=$(addsuffix .ml,$(basename $(CAMLOBJS))) PJ=ProjetMiniC FILES=clex.ml clex.mll compile.mli cparse.ml ctab.ml ctab.mli ctab.mly\ depend error.ml genlab.ml main.ml Makefile\ verbose.ml all: mcc projet: projet.tgz mcc: $(CAMLOBJS) ocamlc -custom -o mcc unix.cma -cclib -lunix $(CAMLOBJS) 7 clean: -rm mcc *.cmi *.cmo cleanall: clean -rm ctab.ml ctab.mli clex.ml projet.tgz -rm -rf Test/ -rm -rf ProjetMiniC/ test: projet.tgz -mkdir Test -rm -rf Test/* cp projet.tgz Test/ (cd Test/; tar -xvzf projet.tgz; cd ProjetMiniC/; cp ~/Papers/compile.ml .; make; cp mcc ~/ projet.tgz: -mkdir $(PJ) cp $(FILES) $(PJ) -mkdir $(PJ)/Exemples cp Exemples/*.c Exemples/*.s $(PJ)/Exemples tar -cvzf $@ $(PJ) ctab.ml: ctab.mly ocamlyacc -v ctab.mly clex.ml: clex.mll ocamllex clex.mll compile.cmi: compile.mli compile.cmo: compile.ml compile.cmi depend: Makefile ocamldep *.mli *.ml >depend include depend Pour plus d’infos sur make : http://www.gnu.org/software/make/manual/make.html 1.1.4 Truc Quand vous avez la flemme d’écrire completement vos .mli, il y a une solution : ocamlc -i Par contre, il faut quand même éditer le mli pour ne garder que ce qui doit réellement être exporté. 1.2 Interpreteur Le toplevel c’est pratique pour tester le code 8 1.2.1 Installation Normalement, c’est installé sur les machines, sinon vous pouvez aller le chercher la : http: //www-rocq.inria.fr/∼acohen/tuareg/ 1.3 Aller on code enfin Exercice 1 Écrire une fonction de fusion de deux listes triées Écrire une fonction fold left Écrire une fonction somme en utilisant fold left val fusion : ’a list -> ’a list -> ’a list val fold_left : (’a -> ’b -> ’a) -> ’a -> ’b list -> ’a val somme : int list -> int # somme [3;4];; - : int = 7 Exercice 2 Définir un type arbre déstiné à faire un arbre binaire de recherche. Écrire une fonction d’insertion. Écrire une fonction map. Écrire une fonction ajoute en utilisant map. ( ajoute n, augmente la valeur des noeuds de l’arbre de n) type ’a arbre = ... val insertion : ’a arbre -> ’a -> ’a arbre val map : (’a -> ’b) -> ’a arbre -> ’b arbre val ajoute : int -> int arbre -> int arbre Exercice 3 Écrire une fonction factorielle de maniere fonctionnelle calculer factorielle 200000 Écrire une fonction fold right récursive terminale val fold\_right : (’a -> ’b -> ’b) -> ’a list -> ’b -> ’b Exercice 4 Écrire une fonction compteur val val : unit -> int 9 # # # - compteur ();; : int = 0 compteur ();; : int = 1 compteur ();; : int = 2 Exercice 5 Définir un module Int pour utiliser avec le module Set L’utiliser avec le module Set... Écrire une fonction unique qui ne garde que le premier élément de chaque valeur d’une liste. ( l’écrire avec le module Set et la fonction List.filter ) val unique : int list -> int list # unique [1;3;3;2;1];; - : int list = [1;3;2] Exercice 6 Écrire des fonctions pour manipuler les entiers qui testent si ils sont toujours positifs. Faire une interface s’assurant qu’on ne peut pas creer de valeurs de ce types négatives. 2 doc – un bon poly : http://cristal.inria.fr/∼remy/poly/ocaml/ – la doc de l’inria : http://caml.inria.fr/pub/docs/manual-ocaml/index.html – le bouquin qui déchire (mais un peu vieux) http://caml.inria.fr/pub/docs/ oreilly-book/html/index.html – le même, en plus vieux et en français (date de ocaml 2.99, avant les variants polymorphes) http://www.pps.jussieu.fr/Livres/ora/DA-OCAML/ 10