Tipus | llenguatge de programació orientat a objectes, llenguatge de programació funcional, llenguatge de programació multiparadigma, llenguatge de programació imperatiu, dialect (en) i programari lliure i de codi obert |
---|---|
Data de creació | 1996 |
Disseny | Xavier Leroy i Damien Doligez |
Desenvolupador | INRIA |
Epònim | Caml |
Paradigma de programació | programació funcional, llenguatge imperatiu, programació orientada a objectes i programació modular |
Darrera versió estable | 5.2.0 () |
Dialecte de | ML |
Llenguatge de programació | OCaml i C |
Influenciat per | ML Estàndard |
Sistema operatiu | Unix-like |
Extensió dels fitxers | ml i mli |
Llicència | Q Public License (en) i GNU LGPL 2.1 |
Etiqueta d'Stack Exchange | Etiqueta |
Pàgina web | ocaml.org |
OCaml, anteriorment denominat[1] Objective Caml és un llenguatge de programació de la família ML, extensió i versió actual del llenguatge de programació Caml,[2] acrònim de "Categorical Abstract Machine Language", creat per Xavier Leroy, Jérôme Vouillon, Damien Doligez, Didier Rémy i altres el 1996, amb construccions d'Orientació a Objectes, successora de l'extensió anterior del mateix llenguatge anomenada Caml Light.
OCaml és un projecte de codi obert impulsat per l'entitat estatal francesa de recerca INRIA (Institut national de recherche en informatique et en automatique).
OCaml parteix dels patrons del llenguatge funcional ML amb un lèxic i puntuació diferents, i hi afegeix construccions dels paradigmes de programació procedimental ja incorporades a Caml Light i d'Orientació a objectes, adoptant un enfocament multiparadigma.
OCaml és el nou nom oficial (abans era Objective Caml) des del Juliol del 2011,[1] i ha estat adoptat per Microsoft com a base del seu llenguatge funcional F#.[3] No hi ha cap estàndard per al llenguatge; l'única font de compiladors per al llenguatge és el mateix centre de recerca.
OCaml posa èmfasi en el rendiment. Xavier Leroy diu "El compilador OCaml proporciona pel cap baix un rendiment del 50% d'un compilador de llenguatge C"[4] i els bancs de proves mostren que generalment és així.[5]
Es disposa de dos compiladors per a OCaml,
OCaml destaca per la gestió de memòria amb recuperació incremental de curta durada.
>> .. about soft real-time performance .. GHC's stop-the-world GC will incur prohibitively long pause times for this kind of application (soft real-time), orders of magnitude longer than the pause times of OCaml's incremental GC.[8][9]
Ocaml implementa un gestor de memòria dinàmica (ang:garbage collector) de dues generacions
El caràcter incremental del recol·lector a la generació més vella, possibilita un temps reduït de les pauses. A cada passada de la generació jove (minor collection) fa una part de la feina sobre la generació vella (major collection).
Fent una prova amb el programa del tutorial de la referència[10] esmentat al paràgraf "The GC module", es comprova que les pauses per recuperació es poden comptar per poques dècimes de mil·lisegon malgrat que poden augmentar ocasionalment degut a la latència o espera de commutació de fil del planificador del nucli.
(<0.0002 segons amb ocamlc i <0.0001 amb ocamlopt mesurat sobre un Intel Atom model 330 amb Linux Ubuntu 10.04 "preempt"[11] de 64 bits i 2 GB de RAM i amb el compilador recompilat a partir dels fonts modificats per calcular, en les manques de memòria dinàmica caml_check_urgent_gc, la pausa màxima havent creat totes les estructures, és a dir, partint en haver completat la primera recol·lecció de la generació vella).
El programa següent "hola.ml":
print_string "Hola Món!\n"
;;
es compila a codi intermedi de la següent manera:
$ ocamlc hola.ml -o hola
i s'executa en entorns Unix:
$ ./hola Hola Món! $
o a l'avaluador d'expressions "ocaml"
# print_string "Hola Món!\n" ;; Hola Món! - : unit = () #
Si oblideu el fi de clàusula en doble punt-i-coma ;; el podeu afegir a la línia següent.
En-línia, a l'avaluador d'expressions ocamljava en una applet Java. (No hi oblideu el topall ;; de fi d'instrucció).
Al mòbil o tauleta Android amb l'aplic. "Ocaml toplevel" o també l'aplic. d'execució remota "IDEDroid".
A l'iPad/iPhone amb l'aplic. "CodeToGo" que executa remotament a l'IDEone igual que l'"IDEDroid"[12]
OCaml disposa de tipatge estàtic, inferència de tipus, polimorfisme paramètric, recursivitat final, encaixos de patrons, tractament d'excepcions, tancaments, mòduls paramètrics (functor) i recol·lector de memòria brossa generacional i incremental, objectes, classes d'objectes (parametritzables), tipus de classes, ...
Depèn de la codificació del fitxer font.[13] i l'entorn del sistema operatiu, degut a l'assimilació de caràcters a bytes (rang 0..255).
Les clàusules acaben en doble punt-i-coma, per distingir-lo del punt-i-coma separador de la seqüència d'instruccions en una definició (lligam).
let nom_definit = expr; expr; ..; expr ;;
(* definició de tipus *) type tipus = Constructor1 of tipus_dels_components | Constructor2 of tipus_dels_components | ... ;;
exception Excepció of tipus_dels_components ;;
(* dins una signatura *) val nom_definit : expr_de_tipus
(* dins una def. d'objecte *) val [mutable] nom_de_camp = expressió
(* comentari multilínia no existeix el comentari fins a fi de línia (* comentari niuat *) *) (** comentari d'autodocumentació *)
De manera opcional es proposa una revisió de la sintaxi per solucionar dificultats amb l'analitzador sintàctic.[14][15]
ocamlc -pp camlp4r programa.ml
ocaml # load "camlp4r.cma";;
type unit (* tipus buit, el de les expressions d'efectes col·laterals que no retornen cap valor literals: () *)
type bool (* tipus booleà literals: true, false ops: not, &&, || (''or'' i ''&'' han quedat obsolets) *)
type int (* sencers 31 o 63 bits segons la màq.<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#6_Integerarithmetic Aritmètica de sencers, 31 o 63 bits]{{en}}</ref> *), literals: 5, -5, 6-5 (* la op. menys unària es fa amb el signe '-' contràriament a SML *) ops: + - * / mod abs max_int min_int <, <=, >, >=, = (igual valor), <> (not =), == (igualtat referencial),<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#VAL(%3D%3D) Igualtat referencial (==) respecte Igualtat estructural (=)]</ref> != (not ==) land, lor, lxor, lnot, lsl, lsr, asr
No hi ha literals en bases hexa, octal, binari, però la conversió int_of_string els pot llegir d'una cadena amb el prefix "0x", "0o", "0b" respectivament.
"pseudoliterals hexa": int_of_string "0x01020304"
"pseudoliterals octal": int_of_string "0o377"
"pseudoliterals binaris": int_of_string "0b0101"
El tipus sencer int comparteix representació amb el tipus punter, reservant un bit per la distinció.
Tipus sencers de paraula completa (32 o 64 bits):
type float (* coma flotant de 64 bits [[IEEE 754]] *) literals: 0.44 1.2E2 5. (* la part sencera no pot ser buida, la decimal sí !! *) ops: +. -. *. /. ** ceil floor max_float min_float int_of_float float_of_int (* els operadors numèrics de floats que coincideixen amb els de sencers porten un puntet diferenciador *) (* no generen excepcions en cas de sobreiximent o div. per zero ''classify_float'' torna un de (FP_normal | FP_subnormal (valors IEEE754 amb repr. ''denormal'' inf. als normals per evitar div.per.zero) | FP_zero | FP_nan (0/0) | FP_infinity) Vegeu ref.<ref>[http://caml.inria.fr/pub/docs/manual-ocaml/libref/Pervasives.html#6_Floatingpointarithmetic Aritmètica de Coma flotant] {{en}}</ref>*) <, <=, >, >=, =, <>, ==, !=, classify_float
type char (* caràcters de 8 bits *) [http://caml.inria.fr/pub/docs/manual-ocaml/libref/Char.html mòdul Char] literals: 'a', '\n', '\t', '\120' (* decimal 'x' *), '\x78' (* hexadecimal 'x' *)
(* enumerats *)
type signe = Positiu | Negatiu ;;
(* tuples (tipus producte), separador en els valors la coma
sense parèntesis també val, la coma n'és el constructor *)
(2, 3.5) : int * float
fst (2, 3.5) = 2 ;;
snd (2, 3.5) = 3.5 ;;
type la_meva = int * float
(* registres, separador el punt i coma *)
{a=2; b=3.5} : {a: int; b: float}
(* registres amb camps mudables *)
type persona = {nom:string; mutable edat:int}
let p = {nom="Joan"; edat=30} ;;
let aniversari (p:persona) =
p.edat <- p.edat +1 ;;
(* actualització funcional (clonar i actualitzar) *)
let q = {p with nom="Josep"} ;;
(* unió discriminada per etiquetes (constructors) *)
type nombre = Sencer of int | Real of float ;;
let llista_de_nombres = [ Sencer 10; Real 2.5] ;;
(* tipus polimòrfics,
'a : el prefix apòstrof indica variable de tipus
cas de més d'una variable, es posen en tupla *)
(* opcionalitat de valor, per a resultats de funcions definides parcialment *)
type 'a option = None | Some of 'a ;;
(* amb dues variables *)
type ('a, 'b) a_o_b = A of 'a | B of 'b ;;
type sencerA_o_realB = (int, float) a_o_b ;;
(* tipus recursius *)
type 'a arbre = Fulla of 'a | Branca of 'a * 'a arbre * 'a arbre ;;
let a = Fulla 2 ;;
let b = Branca (5, a, a) ;;
Els caràcters ocupen un octet (valors 0..255). (Per a tires de caràcters Unicode vegeu #tipus Unicode) Vegeu[17]
type string (* text *)
(* literals: "abc"
ops: "abc" ^ "def" = "abcdef" *)
let str = "abc" ;;
String.sub str 0 2 = "ab" ;; (* subtira amb inici i llargada *)
str.[0] ;; (* obtenir caràcter a la posició x *)
str.[1] <- 'm' ;; (* estableix caràcter -- les tires són mudables *)
(* (==) igualtat referencial (indica si són la mateixa instància) *)
("abc" == "abc") = false ;;
("abc" = "abc") = true ;; (* (=) igualtat estructural *)
(* vectors / arrays *)
let arr = [| 1; 2; 3 |] ;; (* literals *)
let arr = Array.make 20 1.0 ;; (* 20 elements amb el valor *)
let arr = Array.init 20 (fun i -> 2*i) ;; (* 20 elements segons funció *)
let elem = arr.(0) ;; (* obtenir elem., el primer índex és el 0 *)
arr.(2) <- 6 ;; (* establir elem.*)
(* llistes, separador el punt i coma *)
[ 1; 2; 3 ] ;; (* literals *)
[ 1, 2; 3, 4] = [ (1, 2); (3, 4) ] ;; (* la coma fa el parell, el separador de llistes és el; *)
1 :: [ 2; 3] ;; (* op. Cons -- - : int list = [1; 2; 3] *)
[ 1; 2] @ [3] ;; (* concatena -- - : int list = [1; 2; 3] *)
List.hd [1; 2; 3] = 1 ;; (* hd (abreviació de "head" : cap) *)
List.tl [1; 2; 3] = [2; 3] ;; (* tl (abreviació de "tail" : cua) *)
El tipus és una unió discriminada oberta, sense declaració prèvia de tipus, d'elements etiquetats anomenats (variant types).[19]
let llista_d_etiquetats = [`Pomes 10; `Sucre 2.5] ;;
let totalitza (import_acum:float) variant = match variant with
| `Pomes (quant: int) -> import_acum +. (float_of_int quant) *. 0.32
| `Sucre (quant: float) -> import_acum +. quant *. 0.21
;;
let total = List.fold_left totalitza 0.0 llista_d_etiquetats
in print_float total; print_newline ()
;;
La finalitat és discriminar els elements d'una col·lecció per encaix de patrons de l'etiqueta, que ha de començar per una cometa revessa, feta amb l'accent greu seguit d'espai, i un identificador que comenci per majúscula
let llista_d_etiquetats = [`Pomes 10; `Sucre 2.5 ] ;;
(* val llista_d_etiquetats: [> `Pomes of int | `Sucre of float ] list *)
let novallista = llista_d_etiquetats @ [`Ous 2] ;; (* concatenació *)
(* les llistes etiquetades es poden comparar,
no hi ha queixa de tipus malgrat difereixin en etiquetes *)
(novallista = llista_d_etiquetats) ;; (* - : bool = false *)
Exemple:#llistes obertes d'elements etiquetats
Vegeu biblioteca Camomile[20]
UChar (* caràcter unicode de 31 bits *)
(* tipus de strings *)
UTF8
UTF16
UCS4
UText (* string com a vector de sencers *)
...
De l'anglès "Phantom types". Tipus sense definició o amb paràmetres de tipus que no intervenen a la definició.
let pi = 4. *. atan 1. ;;
(* encaix de tuples i registres *)
let (v_a, v_b) = (1, 2) ;; (* val v_a : int = 1
val v_b : int = 2
*)
let v_a, v_b = 1, 2 ;; (* sense parèntesi també val: la coma és el constructor de tupla *)
type el_meu_tipus = {a:int; b:float} ;; (* registres *)
let el_meu = { a = 2; b = 3.5 } ;;
let {a = v_a; b = v_b } = el_meu ;; (* val v_a : int = 2
val v_b : float = 3.5
*)
''expr'' : ''tipus'' (* (#) restricció de paràmetres a ''class type'' (signatures d'objectes i ''interfaces'') *) ''paràmetre'' : # ''super_classe_o_interface'' (* (:>) coerció a tipus de classe menys especialitzat (''up-cast'') (superclasses i ''class type''s) -- secció [[#herència|Herència]] i següents *) ''expr_subtipus'' :> ''super_tipus_de_classe''
if ''expr'' then ''expr'' else ''expr'' ;; match ''expr'' with ''patró'' when ''expr'' -> ''expr'' | ''patró'' | ''patró'' | ''patró'' when ''expr'' -> ''expr'' ... ;;
amb àmbits successius de visibilitat
let resultat = let ''declaració'' in (* àmbit més extern *) let ''declaracio2'' in (* àmbit intermedi *) ''expr'' ;; (* àmbit intern *)
let suma x y = x + y ;; (* val suma : int -> int -> int = <fun> *)
(* funció recursiva, ''rec'' permet fer referència al nom de la funció *)
let rec fact n = match n with
0 -> 1
| m -> m * fact (m -1)
;; (* val fact : int -> int = <fun> *)
(* lligam amb funció anònima, clàusula function definició per encaix de patrons (pattern-matching) clàusula fun definició per paràmetres[23] *)
let rec fact = function
| 0 -> 1
| n -> n * fact (n-1)
;;
(* definició simultània (''and'') amb crides circulars,
''rec'' permet fer referència als identificadors definits *)
let rec parell = function
| 0 -> true
| n -> senar (n-1)
and senar = function
| 0 -> false
| n -> parell (n-1)
;; (* val parell : int -> bool = <fun>
val senar : int -> bool = <fun>
*)
Contràriament a ML i al Haskell no n'hi ha.
(* composició de funcions, predefinit: ''compose'' *)
(* no hi ha cap operador específic per la composició *)
let compose f g = function x -> f(g(x));;
La composició de funcions en un llenguatge estricte, genera estructures intermèdies i és preferible resoldre el problema de la desforestació fusionant manualment les operacions en una funció amb un sol bucle.
La biblioteca Batteries defineix (-|) com a operador de composició.[24]
(* amb pas de paràmetres per nom (l'ordre dels param. es pot alterar)
~nomparam:argument o ~nomparam com a abrev. de ~nomparam:nomparam
~ es fa amb les tecles AltGr+4 *)
let rec fact ~num:n = match n with
0 -> 1
| m -> m * fact ~num:(m -1)
;;
L'opcio de compilació -nolabels requereix l'ordenació estricta dels paràmetres a la crida (ignora les etiquetes de paràmetres no opcionals).[25]
let salt ?incr x = (* ? prefixa el param. opcional formal *)
match incr with
| None -> x * 2 (* cas de None: el param. actual no hi era *)
| Some y -> x + y (* cas de Some: el param. actual sí que hi era *)
;; (* val salt : ?incr:int -> int -> int = <fun> *)
(* ús *)
let () = let r = salt ~incr:1 2 (* ~ prefixa el param. opcional actual *)
in print_int r ;
print_endline "" ;;
Requeriment: un paràmetre opcional ha d'ésser seguit, com a mínim, d'un paràmetre anònim (no etiquetat), el qual tanca la possible seqüència de paràmetres opcionals.[26]
let salt ?(incr = 1) x =
x + incr ;; (* val salt : ?incr:int -> int -> int = <fun> *)
Permet l'ús en prefix (entre parèntesis) o bé infix dels operadors binaris.[27]
(* definició *)
let (++) : 'a list -> 'a list -> 'a list = fun x y -> x @ y ;;
(++) [1; 2] [3; 4] (* ús en posició prefix *)
[1; 2] ++ [ 3; 4] (* ús en posició infix *)
El caràcter inicial de l'operador determina la seva precedència (per exemple els definits començant per '*' tenen la mateixa precedència dels operadors multiplicatius). Vegeu Taula de Precedències i associativitat dels operadors[28]
exception ELlistaBuida of string ;;
let obtenir_cap llista = match llista with
[] -> raise (ELlistaBuida "dins obtenir_cap")
| (cap :: _) -> cap
;;
let la_meva_llista = [] ;;
let res : 'a option =
try (
Some (obtenir_cap la_meva_llista)
)
with
| ELlistaBuida msg -> print_endline ("ELlistaBuida: tururut " ^ msg); None
| _ -> print_endline "altra excepció"; None
;;
(* funcions per llançar Excepcions típiques *)
failwith "motiu de la fallada" (* llança exc. Failure *)
invalid_arg "rutina rut, segon paràmetre" (* llança exc. Invalid_argument *)
La que modifica l'estat.
type persona = {nom:string; mutable edat : int} ;;
let joan = {nom = "Joan"; edat = 30} ;;
let aniversari (p:persona) =
p.edat <- p.edat +1 ;; (* retorna unit *)
aniversari joan ;;
print_int joan.edat ;;
let a = ref 5 ;;
let a := !a +1 ;; (* assignació -- qualsevol assignació retorna ''unit'' com a valor d'expressió*)
let resultat = a := !a + 1 (* retornaria ''unit'' *)
; !a (* ara retornarà el valor allotjat *)
;;
let _ = print_endline "abc" ;; (* el patró comodí '_' obvia el resultat *)
let () = ignore (expressió) ;; (* ignore descarta el resultat, retorna Unit *)
let a = print_string "bla bla bla\n"; "nou_valor a retornar" ;; (* efectes laterals i resultat a la mateixa clàusula *)
let imprimeix_llista_de_sencers_amb_separador llista sep =
print_int (List.hd llista); (* imprimeix el cap *)
(* imprimeix la resta d'elements prefixant-hi el separador *)
(* List.iter : ('a -> unit) -> 'a list -> unit *)
List.iter (Printf.printf "%s%d" sep) (List.tl llista) (* aplicació parcial de ''(printf "%s%d")''
deixant un dels paràmetres per al mapeig amb List.iter *)
;;
imprimeix_llista_de_sencers_amb_separador [1;2;3] ", " ;;
1, 2, 3- : unit = ()
expressió_while ::= "while" expr_booleana "do" expr_amb_efectes_laterals "done" expressió_for ::= "for" identificador "=" expr_int ("to" ∣ "downto") expr_int "do" expr_amb_efectes_laterals "done"
let sequencia i = if i <= 4 then Some i else None ;;
let corrent = Stream.from sequencia ;;
let fi = ref false ;;
while not !fi do
match (Stream.peek corrent) with (* Stream.peek consulta el primer *)
| Some v -> let _ = Printf.printf "\n%d" v in
Stream.junk corrent (* Stream.junk escapça el corrent (treu el primer) *)
| None -> fi := true
done ;;
Vegeu[29] Facilita objectes immediats d'una sola instància i classes d'objectes immutables o mudables.
let obj = object
val factor = 5 (* camp de la instància (accés privat) *)
method mult_per_factor x = x * factor
end ;;
obj#mult_per_factor 4 ;;
- : int 20
class rectangle (x:float) (y:float) = object
val area = (x *. x) +. (y *. y) (* camp de la instància (accés privat) *)
method obtenir_area = area
end ;;
let nou_rect = new rectangle 3. 4. ;;
nou_rect#obtenir_area ;;
- : float 25.0
Incorporant el qualificatiu mutable als camps
Pot incorporar una clàusula d'inicialització initializer que el generador new executa.
El desallotjament automàtic fa innecessaris els destructors. Malgrat això, es pot registrar una funció com a finaliser al recollidor de brossa.[30]
(* finalitzadors *)
let f = fun x -> ... ;; let v = ... in Gc.finalise f v
class pila_de_sencers =
object
val mutable la_llista : int list = [] (* camp de la instància, accés privat *)
method apila x =
la_llista <- x :: la_llista
method lleva =
let primer = List.hd la_llista in
la_llista <- List.tl la_llista ;
primer
method private mida = (* privat: accessible només des de la mateixa classe *)
List.length la_llista
method es_buida = (la_llista = [])
initializer (* new com a constructor l'inicialitza *)
print_endline "pila inicialitzada"
end;;
let p = new pila_de_sencers ;; (* "pila inicialitzada"
val p : pila_de_sencers = <obj> *)
(* paràmetres de funció
-- let nom_funció (param : classe_tal) -- sense prefix, el tipus del param. ha de ser exactament classe_tal
-- let nom_funció (param : #classe_tal) -- cas de prefix '#', el tipus del param. pot ésser subtipus de classe_tal
* )
let omple (s: #pila_de_sencers) =
for i = 1 to 5 do
s#apila i
done;;
let buida (s: #pila_de_sencers) =
while not s#es_buida do
Printf.printf "Hem llevat %d de la pila.\n" s#lleva
done;;
Execució a l'intèrpret ocaml
# p ;; - : pila_de_sencers = <obj> # omple p ;; - : unit = () # buida p ;; Hem llevat 5 de la pila. Hem llevat 4 de la pila. Hem llevat 3 de la pila. Hem llevat 2 de la pila. Hem llevat 1 de la pila. - : unit = () #
La construcció {< ... >}
permet l'actualització funcional en una nova instància, retornant-ne un clon actualitzat.[31]
class passa_anys (edat_ini:int) =
object
val edat = edat_ini
method aniversari = {< edat = edat +1 >} (* clonar i actualitzar *)
method imprimeix = print_string "edat: "
; print_int edat
; print_newline ()
end ;;
let joan = new passa_anys 10 ;;
let _ = joan#aniversari#imprimeix ;;
Els mètodes es poden fer abstractes (pendents d'especificar en classes derivades) amb la qualificació "virtual". Les classes amb mètodes virtuals han de ser declarades virtuals.
class virtual punt_abstracte x_init =
object (self)
val mutable virtual x : int
method virtual get_x : int
method get_dist = self#get_x - x_init
end ;;
class point x_init =
object
inherit punt_abstracte x_init
val mutable x = x_init
method get_x = x
end;;
(* tipus resultant: class point : int -> object
val mutable x : int
method get_dist : int
method get_x : int
end *)
Les classes poden tenir herència múltiple. En cas de col·lisió de noms de mètode heretats, preval l'heretat darrer.
(* provant a l'intèrpret ocaml *)
class punt (x_inicial: float) (y_inicial: float) =
object (self)
val mutable x = x_inicial
val mutable y = y_inicial
method desplassa delta_x delta_y = (* la ç de desplaça no s'admet
als ident. UTF-8 (vegeu lèxic) *)
x <- x +. delta_x ;
y <- y +. delta_y
method imprimeix =
Printf.printf "posició %.2f %.2f\n" x y
end ;;
type color = Verd | Roig | Blau ;;
let string_of_color = function
Verd -> "Verd"
| Roig -> "Roig"
| Blau -> "Blau"
;;
class acolorit (c_inicial: color) =
object (self)
val mutable color = c_inicial
method pinta nou_color =
color <- nou_color
method imprimeix =
print_endline ("color: " ^ (string_of_color color))
end ;;
class punt_acolorit (x:float) (y:float) (c:color) =
object (self)
inherit punt x y as super_punt
inherit acolorit c as super_acolorit
method imprimeix =
super_punt#imprimeix ;
super_acolorit#imprimeix
end ;;
let p_acolorit = new punt_acolorit 1.0 2.0 Verd ;;
p_acolorit#imprimeix ;; (* posició 1.00 2.00
color: Verd
- : unit = () *)
A l'estil dels Interface de Java, designen el conjunt de tipus que implementen la signatura descrita amb class type.
(* seguint l'anterior codi a l'intèrpret ocaml *)
class type imprimible =
object
method imprimeix : unit
end ;;
let imprim_test (obj : #imprimible) = obj#imprimeix ;; (* val imprim_test : #imprimible -> unit = <fun> *)
(* El tipus de punt_acolorit és subtipus de imprimible perquè n'inclou els mètodes i camps (en cas que n'hi hagi). *)
imprim_test p_acolorit ;; (* posició 1.00 2.00
color: Verd
- : unit = () *)
let p_ras = new punt 1.5 1.5 ;; (* punt ras *)
imprim_test p_ras ;; (* posició 1.50 1.50
- : unit = () *)
Per exemple, llistes amb elements d'aquells tipus que implementin una funcionalitat determinada per una interfície. Cal fer un up-cast (caracterització a tipus menys especialitzat) de cadascun dels elements al tipus de la interfície.
(* seguint les seccions anteriors *)
(* caracterització a tipus menys especialitzat (''up-cast''),
en aquest cas un interface *)
let cast_a_imprimible cp = (cp :> imprimible) ;;
(* val cast_a_imprimible : #imprimible -> imprimible = <fun> *)
(* col·leccions homogènies *)
let coleccio_d'imprimibles = [ cast_a_imprimible p_ras; (p_acolorit :> imprimible)] ;; (* - : imprimible list *)
let _ = List.iter imprim_test coleccio_d'imprimibles ;; (* iteració de imprim_test a la llista *)
Unió discriminada oberta (sense definició prèvia de tipus)
let coleccio_etiquetada = [`Punt p_ras; `Punt_acolorit p_acolorit] ;;
let rec tracta_col_etiquetada = function
| [] -> ()
| `Punt (p:punt) :: cua ->
p#desplassa 1.0 1.0 ;
p#imprimeix ;
tracta_col_etiquetada cua
| `Punt_acolorit (p:punt_acolorit) :: cua ->
p#pinta Blau ;
p#desplassa 1.0 1.0 ;
p#imprimeix ;
tracta_col_etiquetada cua
;;
Vegeu[32]
class parell_de_sencers (x: int) (y: int) =
object
method get_x = x
method get_y = y
end ;;
let parelleta_de_sencers = new parell_de_sencers 5 3 ;;
class ['a, 'b] parell_de_tipus_generic (x: 'a) (y: 'b) =
object
method get_x = x
method get_y = y
end ;;
let parelleta_de_dos = new parell_de_tipus_generic 5 3.2 ;;
(* val parelleta_de_dos : (int, float) parell_de_tipus_generic = <obj> *)
(* herència *)
class ['a, 'b] parell_de_classe_generica_derivada (x: 'a) (y: 'b) =
object
inherit ['a, 'b] parell_de_tipus_generic x y
(* ... *)
end ;;
let parelleta_del_derivat = new parell_de_classe_generica_derivada 7.5 9.5 ;;
(* funció que retorna resultat de tipus diferent
segons la instanciació de l'objecte de la classe genèrica *)
let obtenir_x (p : ('a, 'b) #parell_de_tipus_generic) = p#get_x ;;
obtenir_x parelleta_de_dos ;;
- : int 5
obtenir_x parelleta_del_derivat ;;
- : float 7.5
class type imprimible = (* interfície "imprimible" (signatura) *)
object
method imprimeix : unit
end ;;
(* als paràmetres de funcions *)
let test_impressio (obj: #imprimible) = obj#imprimeix ;;
(* als paràmetres de les classes *)
class classe_amb_component_imprimible (obj_ini: #imprimible) = object (self:'c)
constraint 'c = #imprimible (* requeriment que la classe implementi l'interface *)
val obj = obj_ini
method imprimeix = obj#imprimeix
end ;;
(* als paràmetres de tipus de classes genèriques *)
class ['a] classe_genèrica_amb_component_imprimible (obj_ini: 'a) = object
constraint 'a = #imprimible
val obj = obj_ini
method imprimeix = obj#imprimeix
end ;;
class classe_amb_copia =
object (self)
method copy = Oo.copy self
end;;
Un fitxer de codi sense clàusula module es considera un mòdul en si mateix i el seu nom de mòdul és el del fitxer amb la primera lletra majúscula.
Per fer-ne servir un identificador cal prefixar-lo: "Nom_fitxer.ident"; per incorporar tots els seus identificadors a l'àmbit actual s'utilitza la clàusula open
open Nom_fitxer (* importa l'espai de noms del mòdul Nom_fitxer *)
(* o també *)
let open Nom_fitxer in (* importació d'àmbit local, versió 3.12 *)
...
La clàusula module introdueix un submòdul que s'adreça externament com a Nom_fitxer.Nom_modul
(* fitxer nom_fitxer.ml *)
module Intern =
struct
let ident = 3
end ;;
(* des de dins del fitxer, ens hi referirem per Intern *)
print_endline ("resultat: " ^ (string_of_int Intern.ident)) ;;
(* des d'un altre fitxer : *)
open Nom_fitxer.Intern
En una compilació els fonts s'han d'especificar en ordre tal que si Mòdul_A crida Mòdul_B, Mòdul_B ha de precedir Mòdul_A.
En cas que les referències entre mòduls fossin circulars, cal generar i compilar primer els fitxers de signatura (secció següent).
(* compilació a codi intermedi "bytecode" (per executar la tasca caldrà la presència de l'intèrpret de bytecode ocamlrun *) ocamlc modul_B.ml modul_A.ml -o nom_tasca
(* En executar la tasca en bytecode, crida a l'intèrpret *) ./nom_tasca arg1..argn (* execució des de l'intèrpret *) ocamlrun nom_tasca arg1..argn .
(* compilació a codi nadiu *) ocamlopt modul_B.ml modul_A.ml -o nom_tasca ./nom_tasca arg1..argn
També es pot compilar a fitxer biblioteca, especificant -a
(* compilació a biblioteca de codi intermedi (bytecode) *) ocamlc -a modul_B.ml modul_A.ml -o nom_llib.cma
(* compilació a biblioteca de codi nadiu *) ocamlopt -a modul_B.ml modul_A.ml -o nom_llib.cmxa
per exemple la biblioteca de tractament de tires de caràcters Unicode del paquet "Camomile"[20] incorporada amb el gestor de paquets Findlib.[34] o la distribució en codi font GODI[35]
(* fitxer mon.ml (exemple de prova) *)
module CAM = CamomileLibrary.Default.Camomile ;;
let text = "Món" ;;
let _ = Printf.printf "UTF8.length '%s' : %d\n" text (CAM.UTF8.length text) ;
Printf.printf "String.length '%s' : %d\n" text (String.length text) ;;
ocamlfind ocamlc -package camomile -linkpkg mon.ml -o mon
o amb compilació i relligat separats
ocamlfind ocamlc -c -package camomile mon.ml (* compilació, genera objecte mon.cmo *) ocamlfind ocamlc -package camomile -linkpkg mon.cmo -o mon (* relligat (ang:link) *)
a Linux amb la codificació descrita a la variable d'entorn LANG = ca_ES.UTF-8
./mon
UTF8.length 'Món' : 3 String.length 'Món' : 4
"CamomileLibrary" NO és el nom del fitxer biblioteca, és un mòdul generat amb l'opció -pack del compilador (empaqueta com a submòduls els mòduls esmentats com a paràmetres).[38][39] Aquest sistema no és regla general dels paquets.
(* extracte del Makefile del paquet Camomile *) camomileLibrary.cmo camomileLibrary.cmi : $(OBJECTS) $(OCAMLC) $(BOPTIONS) -pack -o camomileLibrary.cmo $(OBJECTS) $(INCLUDES)
OcamlMkTop[40] permet crear avaluadors d'expressions OCaml (intèrprets o Toplevels en argot OCaml) amb mòduls i biblioteques precarregats.
A voltes dona millor informació d'errors en cas de manca d'adequació de crides Ocaml a biblioteques del sistema subjacent.
Incorporant l'exemple anterior
ocamlfind ocamlmktop -package camomile -linkpkg mon.cmo -o ocaml-mon
Seqüència d'especificacions de tipus dels identificadors exportats per un mòdul.
En cas que interessi declarar d'accés privat (d'ús intern) algun identif. d'un mòdul-fitxer, generarem un fitxer amb la seva signatura amb la comanda següent, redirigint la sortida:
ocamlc -i fitxer_a.ml > fitxer_a.mli
Com que només s'exporten els identificadors presents a la signatura, editarem el .mli eliminant les declaracions que volem d'accés privat, i el compilarem amb ocamlc, generant el .cmi (interfície compilada)
(* cas de compilació a codi nadiu *) ocamlc -c fitxer_a.mli (* genera interfície compilada .cmi *) ocamlopt -c fitxer_a.ml (* genera objecte de codi nadiu .cmx *) ocamlopt fitxer_a.cmx principal.ml -o nom_tasca (* genera executable nadiu *)
(* cas de compilació a codi intermedi (bytecode) *) ocamlc -c fitxer_a.mli fitxer_a.ml (* genera interfície compilada .cmi i objecte bytecode .cmo *) ocamlc fitxer_a.cmo principal.ml -o nom_tasca (* genera executable bytecode *)
module NomMòdul : Signatura
(:) equival a l'adscripció opaca del ML Estàndard quedant els tipus tan visibles com ho siguin a la signatura, i quedant d'accés privat els elements de l'estruct. no presents a la signatura.
module Intern :
sig
val obtenir_privat : unit -> int
end
=
struct
let el_meu_privat = 3
let obtenir_privat () = el_meu_privat
end ;;
o també
module type SignatIntern =
sig
val obtenir_privat : unit -> int
end ;;
module Intern : SignatIntern =
struct
let el_meu_privat = 3
let obtenir_privat () = el_meu_privat
end ;;
A partir de v3.12: herència (include) múltiple i substitució de tipus eliminant la instrucció type interna de les signatures incloses.[41]
module type SigDelMeuComponent =
sig
type u
include Imprimible with type t := u (* v. 3.12 (substitució del tipus
eliminant la instr. type interna) *)
include Ordenable with type t := u
end
Vegeu ref.[42]
module type Igualable =
sig
type t
val iguals: t -> t -> bool
end ;;
module type SigConjunt =
sig
type tip_elem
type t
val buit : t
val es_membre: tip_elem -> t -> bool
val afegeix: tip_elem -> t -> t
end ;;
module type SigConjuntLlistaDIgualables = functor (E: Igualable) ->
(SigConjunt with type tip_elem = E.t
and type t = E.t list
) ;;
module ConjuntLlistaDIgualables : SigConjuntLlistaDIgualables = functor (E: Igualable) ->
struct
type tip_elem = E.t
type t = E.t list
let buit = ([] : E.t list)
let rec es_membre = fun (elem:E.t) (conj :E.t list) -> match conj with
| [] -> false
| cap :: cua -> (E.iguals elem cap) || (es_membre elem cua)
;;
let afegeix = fun (elem :E.t) (conj :E.t list) -> if not (es_membre elem conj)
then elem :: conj
else conj
end ;;
module Sencers =
struct
type t = int
let iguals: t -> t -> bool = fun a b -> a = b
end ;;
module Reals =
struct
type t = float
let iguals: t -> t -> bool = fun a b -> compare a b = 0
end ;;
module type SigConjuntLlistaDeSencers = (SigConjunt with type tip_elem = Sencers.t
and type t = Sencers.t list
) ;;
module ConjuntLlistaDeSencers = ConjuntLlistaDIgualables (Sencers) ;;
module ConjuntLlistaDeReals = ConjuntLlistaDIgualables (Reals) ;;
Amb el mòdul Marshal.[43]
Els comentaris entre (** i *) es poden fer servir per generar un fitxer d'autodocumentació mitjançant l'eina ocamldoc[44]
Els comentaris entre (* i *) no entren a l'autodocumentació.
S'hi poden incloure camps estàndard per documentar el mòdul i els paràmetres de les definicions. (els tipus no cal posar-los-hi que ja surten automàticament) com ara
@author text @version text @param rol descripció (* no cal posar-hi tipus, ja l'obté de la def. *) @return descripció @raise Excepció descripció @see url (vegeu ..) @since text (des de versió tal) @deprecated text (obsolet s'avisa que aquesta funció ...)
Vegeu-ne l'ús a l'exemple més avall. Un exemple, amb el fitxer de signatura i sense, seria
ocamldoc -html modul.ml o bé ocamldoc -html modul.ml -intf modul.mli
Generaria els fitxers de documentació Modul.html i type_Modul.html
Vegeu[45]
(* fitxer arrenca.ml, utilitza mòduls Sys i Arg *)
(* prog. d'anàlisi de línia d'ordres *)
let nomprog = Sys.argv.(0) ;; (* argc = Array.length Sys.argv *)
let usage_msg = Printf.sprintf "Ús: %s -v -o sortida --num 10 fitxer_a fitxer_b\n" nomprog ;;
(* variables per a opcions i paràmetres *)
let ref_flag_v = ref false ;;
let ref_opcio_o = ref "" ;;
let ref_opcio_num = ref 0 ;;
let ref_fitxers = ref ([] : string list) ;;
(* especificació d'opcions: llista de (opció, comanda : Arg.spec, text_d'ajuda) *)
let spec_opcions = [ ("-v", Arg.Set ref_flag_v, "versió") ;
("-o", Arg.Set_string ref_opcio_o, "output") ;
("--num", Arg.Set_int ref_opcio_num, "numeric")
] ;;
(* paràmetres posteriors a les opcions, apilar-los a !ref_fitxers, quedaran en ordre invers *)
let en_llegir_arg arg = ref_fitxers := arg :: !ref_fitxers ;;
(* capgira la llista per recuperar-ne l'ordre *)
let obtenir_args_de_comanda () = List.rev !ref_fitxers
let processa_arg arg = Printf.printf "tracta param. %s\n" arg ;;
let run = Arg.parse spec_opcions en_llegir_arg usage_msg
; Printf.printf "flag_v: %B\n" !ref_flag_v
; Printf.printf "opcio_o: %s\n" !ref_opcio_o
; Printf.printf "opcio_num: %d\n" !ref_opcio_num
; List.iter processa_arg (obtenir_args_de_comanda ()) (* aplica funció a params. retornant unit *)
;;
(* fi de fitxer *)
compilació
ocamlc -o arrenca arrenca.ml
./arrenca -v -o sortida --num 10 fitxer_a fitxer_b
dona la següent sortida
flag_v: true opcio_o: sortida opcio_num: 10 tracta param. fitxer_a tracta param. fitxer_b
Altres sistemes:
Les operacions predefinides són al mòdul Pervasives, obert d'entrada.[46]
Els tipus predefinits s'esmenten aquí.[47]
Les biblioteques estàndards s'especifiquen aquí[48]
El gestor de paquets Findlib funciona només sobre sistemes Unix. Vegeu enllaços.[34][36]
El fitxer de projecte del paquet duu per nom META.[51]
Per instal·lar un paquet precompilat, descarregat manualment:
ocamlfind install nom_del_paquet camí_al_fitxer_META
Per especificar-lo en la compilació vegeu secció més amunt #Compilació amb biblioteques del sistema de paquets
GODI[35] és un rebost de paquets de codi font que en facilita la instal·lació i actualització compilant a destinació.
Incorpora un programa anomenat godi_console que proporciona una interfície de menús en una consola de comandes, per a la selecció, instal·lació i actualització dels paquets[52]
A l'hora d'instal·lar un paquet, cal fer primer l'operació test per comprovar si manquen paquets dels quals depèn, que haurem d'assenyalar "per instal·lar" individualment.
Per anul·lar les operacions GODI marcades, si no ens en sortim, cal cercar l'arxiu "build/wishes.txt" de la instal·lació GODI i buidar-lo o eliminar-lo.
Els paquets de distribució Godi poden tenir un nom lleugerament diferent del nom amb Findlib (per exemple godi-camomile per al paquet camomile). Per utilitzar-los en compilació cal fer servir el nom original del paquet (per exemple camomile) i el sistema ja descrit per al Findlib.
En cas d'executar més d'un programa ocaml alhora, de cara a minimitzar l'ús de memòria hi ha les següents opcions:
El codi intermedi (bytecode) pot córrer a, pràcticament, qualsevol plataforma compatible amb POSIX amb un compilador compatible amb ANSI-C.
La llista oficial de plataformes suportades aquí.[56]
JoCaml[57] és una extensió del llenguatge que implementa l'àlgebra de processos CSP anomenada "càlcul Join"[58] i facilita la concurrència per pas de missatges amb sincronització per encaix de patrons per a sistemes distribuïts amb migració de processos.
Amb el programari OCamlJava[59][60] Requereix JVM >= 1.6
export OCAMLJAVA=carpeta_distrib_ocamljava/bin
ocamlc hola.ml -o hola # compilació a codi intermedi
java -jar $OCAMLJAVA/ocamlrun.jar hola # execució a JVM de bytecode ocaml
Cadmium per a l'Android: joecaml[61] constitueix una reimplementació per a una versió més antiga de Java, la 1.5 compatible amb Android. (Cadmium de OCamlJava requereix Java 1.6)
(amb "-standalone" incorpora totes les biblios necessàries a l'arxivador .jar de sortida)
java -jar $OCAMLJAVA/ocamljava.jar hola.ml -o hola.jar -standalone
java -jar hola.jar # execució de bytecode JVM
(** fitxer volca_fitxer.ml (Autodocumentació)
especifiquem els tipus de sortida de les rutines
per assegurar-ne la comprovació a les diferents branques,
els tipus dels params. d'entrada els dedueix de les operacions involucrades
.- les operacions sense prefix de mòdul les trobareu al Pervasives
@author jo i en pitus
@version 1.0
*)
(**
llegeix una línia i l'escriu
@param inp canal d'entrada
@raise End_of_file fi de fitxer
*)
let volca_linia (inp:in_channel) : unit =
let línia = input_line inp in
print_endline linia
;;
(**
llegeix d'un canal d'entrada
@param inp canal d'entrada
*)
let volca_canal inp : unit =
try (while true do
volca_linia inp
done
)
with
End_of_file -> print_endline "\n-- fi de fitxer"
| _ -> print_endline "\n-- altra excepció"
;;
(**
obre el fitxer comprovant error
@param nom_fitxer nom del fitxer
@return possible canal d'entrada
*)
let obre_fitxer nom_fitxer : in_channel option =
try (
let inp = open_in nom_fitxer in
Some inp
)
with _ -> let _ = Printf.printf "Error en obrir fitxer %s per llegir\n" nom_fitxer in
None
;;
(**
volca el fitxer especificat si és que existeix
@param nom_fitxer nom del fitxer
*)
let volca_fitxer nom_fitxer : unit =
if not (Sys.file_exists nom_fitxer)
then Printf.printf "El fitxer de nom %s no existeix\n" nom_fitxer
else let possible_inpc = obre_fitxer nom_fitxer in
match possible_inpc with
None -> ()
| Some inpc -> volca_canal inpc ;
close_in inpc
;;
(**
comprova que la tasca té un argument i el pren de nom de fitxer a volcar
*)
let main = let nom_prog = Sys.argv.(0) in
if Array.length Sys.argv <> 2
then Printf.printf "ús: %s nom_fitxer\n" nom_prog
else volca_fitxer Sys.argv.(1)
;;
Compila, executa, genera interfície (signatura) i genera fitxers d'autodocumentació (de la implementació nom-amb-primera-lletra-majúscula.html i de la interfície type_nom-amb-primera-lletra-majúscula.html)
ocamlc volca_fitxer.ml -o volca_fitxer
./volca_fitxer nom_fitxer
ocamlc -i volca_fitxer.ml > volca_fitxer.mli
ocamldoc -html volca_fitxer.ml -intf volca_fitxer.mli
Estalvien espai en el tractament de llistes llargues o infinites.
(* tipus de llista d'avaluació tardana (ang:lazy evaluation) *)
type 'a node_t =
| Nil
| Cons of 'a * 'a zlist_t
and 'a zlist_t = 'a node_t Lazy.t
;;
(** generador tardà de m elements; ''lazy'' deixa la generació en suspens *)
let rec genera_llista (m:int) :'a zlist_t =
lazy (match m with
| 0 -> Nil
| n -> Cons (n, genera_llista (n-1))
) ;;
(** fold_left_tardà: fold_left sobre llista d'avaluació tardana;
''Lazy.force'' força l'avaluació del generador de la llista
obtenint-ne 1 elem.
*)
let rec fold_left_tardà f acum (llista:'a zlist_t) =
match (Lazy.force llista) with
| Nil -> acum
| Cons (x, xs) -> fold_left_tardà f (f acum x) xs
;;
let op_binaria = fun (a:float) (b:int) -> a +. sqrt (float_of_int b) ;; (* qualsevol que interessi *)
let _ =
Printf.printf "resultat: %.2f\n" (fold_left_tardà op_binaria 0. (genera_llista 100000))
;;
Glade és una eina de disseny d'interfícies gràfiques d'usuari per a l'entorn GTK que desa el disseny en format GladeXML que els programes poden carregar fàcilment per instanciar-ne els elements gràfics i connectar-hi els gestors d'esdeveniments.
El programari lablgtk2[70] conté els connectors a les API de GTK i Glade així com un convertidor que del format GladeXML genera un mòdul OCaml amb la definició de la interfície. Per exemple, dissenyant al Glade una finestra de nom window1 amb l'etiqueta "Hola món" desant el fitxer com a hola_mon_interficie.glade.
A la versió més recent de Glade, cal especificar a les preferències, com a format de projecte, el libGlade (GladeXML v.2.0)[71] en comptes del GtkBuilder que ve predeterminat.
lablgladecc2 hola_mon_interficie.glade > hola_mon_interficie.ml
(* fitxer hola_mon.ml *)
open Hola_mon_interficie
let en_sortir () = GMain.quit () ;;
let () =
let w1 = new window1 () in (* crea instància de la finestra importada de "Hola_mon_interficie" *)
let _ = w1#window1#connect#destroy ~callback:en_sortir in (* connecta el senyal de tancament ''on destroy'' *)
w1#toplevel#show (); (* mostra la finestra *)
GMain.Main.main () ;; (* engega el bucle de despatx de senyals *)
ocamlc -I +lablgtk2 lablgtk.cma lablglade.cma gtkInit.cmo hola_mon_interficie.ml hola_mon.ml -o hola_mon ./hola_mon
L'antic convertidor ml-glade[72] que traduïa fitxers GladeXML versió 1, no ha sigut actualitzat a les versions de Glade actuals i ha quedat obsolet.
A partir de la versió 3.12 permet la inclusió múltiple de signatures mitjançant la substitució de tipus de la segona en endavant. Vegeu ref.[41]
module type PUNT_ABSTRACTE =
sig
type t
val getX : t -> float
val setX : float -> t -> t
val modul: t -> float
val dist: t -> t -> float
end ;;
module type PUNT1D =
sig
include PUNT_ABSTRACTE
val nou: float -> t
end ;;
module type PROP_Y =
sig
type u
val getY : u -> float
val setY : float -> u -> u
end ;;
module type PUNT2D =
sig
include PUNT_ABSTRACTE
include PROP_Y with type u := t (* inclusió de signatures amb substitució de tipus v3.12 *)
val nou: float -> float -> t
val desplassaX: float -> t -> t
end ;;
module Punt1D : PUNT1D =
struct
type t = { x: float}
let nou x = {x}
let getX {x} = x (* v3.12: encaixos {x} equivalents a {x=x} *)
let setX nova_x p = {p with x = nova_x}
let modul p = p.x
let dist p q = modul { x = q.x -. p.x}
end ;;
module Punt2D : PUNT2D =
struct
type t = { x:float; y: float}
let nou x y = {x; y}
let getX {x} = x
let setX nova_x p = {p with x = nova_x}
let getY {y} = y
let setY nova_y p = {p with y = nova_y}
let modul p = sqrt (p.x ** 2.0 +. p.y ** 2.0)
let dist p q = modul { x = q.x -. p.x; y = q.y -. p.y}
let desplassaX dx ({x} as p) = {p with x = x +. dx} (* ''as pattern'' al revés del Haskell *)
end ;;
Més info al manual "Unix systems programming in Objective Caml"[73]
open Event
type info = Info of int | Plega
exception EFinal of string
let obtenir_hora : unit -> string = (* obtenir hora d'Unix.time *)
fun () -> let open Unix in (* ''open'' local, versió 3.12 *)
let tm = localtime (time ()) in
Printf.sprintf "%02d:%02d:%02d" tm.tm_hour tm.tm_min tm.tm_sec
let productor : info channel -> unit =
fun canal -> for valor = 3 downto 0 do
Thread.delay 1.0
; sync(send canal (Info valor)) (* tramesa síncrona *)
done
let consumidor : info channel * info channel -> unit =
fun (canal1, canal2) ->
try (while true do
match sync (choose [receive canal1; receive canal2]) (* recepció síncrona amb selecció *)
with
| Info valor -> Printf.printf "%s - compte: %d \n" (obtenir_hora ()) valor
; flush stdout
| Plega -> raise (EFinal "s'ha acabat") (* excepció per sortir del bucle *)
done
)
with
| EFinal msg -> Printf.printf "excep %s\n" msg
; flush stdout
;;
let main () = let canal1 = Event.new_channel () in
let canal2 = Event.new_channel () in
let consumidor_id = Thread.create consumidor (canal1, canal2) in
let productor_id = Thread.create productor canal1 in (* pel canal1 *)
Thread.join productor_id (* espera fi productor *)
; sync(send canal2 Plega) (* ordre de plegar al consumidor *) (* pel canal2 *)
; Thread.join consumidor_id (* espera fi consumidor *)
; print_string "Fi del programa\n"
;;
let _ = main ()
ocamlc -vmthread -o prova_vmt unix.cma threads.cma prova.ml ./prova_vmt
ocamlopt -thread -o prova_ost unix.cmxa threads.cmxa prova.ml ./prova_ost
dona:
11:44:15 - compte: 3 11:44:16 - compte: 2 11:44:17 - compte: 1 11:44:18 - compte: 0 excep s'ha acabat Fi del programa
Amb la biblioteca coThreads[75]
A l'Ubuntu:
sudo apt-get install libcothreads-ocaml-dev
Vegeu també:
(* fitxer mvar.ml, adaptat dels exemples de cothreads *)
module Thread=Cothread
open Stm
type 'a mvar = 'a option tvar
(* -------- primitives similars a les del Haskell -------- *)
let new_empty_mvar () = tvar None
let take_mvar mv =
read_tvar mv >>= (function
None -> retry
| Some v ->
write_tvar mv None >>= (function _ -> return v))
let put_mvar mv v =
read_tvar mv >>= (function
None -> write_tvar mv (Some v)
| Some v -> retry)
(* -------- aplicació -------- *)
let productor mv =
let c = ref 0 in
while true do
Thread.delay (Random.float 0.00005);
atom (put_mvar mv !c);
incr c
done
let consumidor mv =
while true do
Printf.printf "Rebut %d\n" (atom (take_mvar mv));
flush_all ();
done
let main () =
let mv = new_empty_mvar () in
let prod_id = Thread.create productor mv in
let consum_id = Thread.create consumidor mv in
Thread.join prod_id;
Thread.join consum_id;
()
let () = main ()
ocamlc -vmthread -o mvar-vmt cothreads.cma mvar.ml ./mvar-vmt
ocamlopt -thread -o mvar-ost unix.cmxa cothreads.cmxa mvar.ml ./mvar-ost