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

Documents pareils