OCaml

OCaml
Logo.
Dato for første versjon 1987 (CAML), 1996 (OCaml)
Paradigme Multiparadigm  : tvingende , funksjonell , objektorientert
Utvikler Inria
Siste versjon 4.12.0 (24. februar 2021)
Skrive Sterk , statisk
Dialekter JoCaml, Fresh OCaml, GCaml, MetaOCaml, OCamlDuce, OcamlP3L
Påvirket av ML
Påvirket F # , Rust , OPA , Scala
Skrevet i OCaml
Operativsystem Kryssplattform
Tillatelse LGPL 2.1
Nettsted ocaml.org
Filutvidelse ml og mli

OCaml , tidligere kjent som Objective Caml , er den mest avanserte implementeringen av Caml- programmeringsspråket , opprettet av Xavier Leroy , Jérôme Vouillon , Damien Dolurez , Didier Rémy og deres samarbeidspartnere i 1996 . Dette språket, fra ML- språkfamilien , er et open source- prosjekt ledet og vedlikeholdt hovedsakelig av Inria .

OCaml er etterfølgeren til Caml Light , som den blant annet har lagt til et objektprogrammeringslag . Forkortelsen CAML kommer fra Categorical Abstract Machine Language , en abstrakt maskinmodell som imidlertid ikke lenger brukes i nyere versjoner av OCaml.

Bærbar og kraftig, OCaml brukes i prosjekter så forskjellige som Unison -filsynkroniseringsprogramvaren , Coq formelle bevisassistent eller nettversjonen av Facebook Messenger . De symbolske prosesseringsanleggene på språket tillater utvikling av statiske verifiseringsverktøy, for eksempel SLAM-prosjektet for Windows- piloter skrevet av Microsoft , eller ASTRÉE for visse innebygde systemer på Airbus A380 .

Prinsipper

Caml er et funksjonelt språk utvidet med funksjoner som tillater tvingende programmering . OCaml utvider språkets muligheter ved å aktivere objektorientert programmering og modulær programmering . Av alle disse grunnene faller OCaml inn i kategorien multiparadigmaspråk .

Den integrerer disse forskjellige konseptene i et typesystem arvet fra ML, preget av en statisk , sterk og utledet typing .

Typesystemet tillater enkel manipulering av komplekse datastrukturer: vi kan enkelt representere algebraiske typer , det vil si hierarkiske og potensielt rekursive typer (lister, trær osv.), Og enkelt manipulere dem ved å bruke mønstertilpasningen . Dette gjør OCaml til et valgt språk i områder som krever manipulering av komplekse datastrukturer, som kompilatorer .

Den sterke typingen , samt fraværet av eksplisitt minnehåndtering (tilstedeværelse av en søppeloppsamler ) gjør OCaml til et veldig trygt språk. Det er også kjent for sin ytelse, takket være tilstedeværelsen av en innfødt kodekompilator .

Historie

Caml-språket er født fra møtet til ML-programmeringsspråket, som har fokusert teamet Formel of INRIA siden begynnelsen av 1980 - tallet , og den kategoriske abstrakte maskinen CAM Guy Cousineau , basert på arbeidet til Pierre-Louis Curien i 1984 . Den første implementeringen, skrevet av Ascander Suarez (den gang doktorgradsstudent ved Paris Diderot University ) og deretter vedlikeholdt av Pierre Weis og Michel Mauny, ble publisert i 1987 . Språket skilte seg gradvis fra ML-faren fordi Inria-teamet ønsket å tilpasse et språk til sine egne behov, og fortsette å få det til å utvikle seg, noe som kom i konflikt med den pålagte "stabiliteten" til ML. Gjennom Standard MLs standardiseringsarbeid.

Begrensningene med CAM førte til etableringen av en ny implementering, utviklet av Xavier Leroy i 1990 , under navnet Caml Light . Denne implementeringen, inkludert en nyere versjon, brukes fremdeles i utdanningen i dag , Selv om systemet ikke lenger vedlikeholdes av INRIA , fungerer det gjennom en tolk- byte-kode ( bytecode ) kodet i C , noe som gir den stor bærbarhet. Memory management system, designet av Damien Dolurez, dukket også opp i Caml Light. I 1995 publiserte Xavier Leroy en versjon av Caml kalt Caml Special Light , som introduserte en innfødt kodekompilator, og et modulsystem inspirert av Standard ML-moduler.

OCaml, som ble utgitt for første gang i 1996 , gir Caml et objektsystem designet av Didier Rémy og Jérôme Vouillon. Noen avanserte funksjoner, for eksempel polymorfe varianter eller etiketter (som gjør det mulig å skille argumentene som er gitt til en funksjon ved navn, i stedet for deres posisjon) ble introdusert i 2000 av Jacques Garrigue. OCaml har siden stabilisert seg relativt (til tross for fraværet av en spesifikasjon, og det gjeldende dokumentet er den offisielle håndboken som Inria opprettholder ). Mange dialekter av OCaml har dukket opp, og fortsetter å utforske spesifikke aspekter ved programmeringsspråk (samtidighet, parallellitet, lat evaluering, XML-integrasjon ...); se avsnitt Avledede språk .

Hovedtrekk

Funksjonelt språk

OCaml har de fleste av de vanlige funksjonene til funksjonelle språk, spesielt høyere ordensfunksjoner og lukninger ( lukkinger ), og en god støtte for halen rekursjon .

Skrive

Statisk skriving av OCaml oppdager et stort antall programmeringsfeil på kompileringstidspunktet som kan forårsake problemer på kjøretiden. I motsetning til de fleste andre språk er det imidlertid ikke nødvendig å spesifisere hvilken type variabler du bruker. Faktisk har Caml en type inferensalgoritme som gjør det mulig å bestemme typen variabler fra konteksten de brukes i.

ML-typesystemet støtter parametrisk polymorfisme , det vil si typer hvis deler ikke blir bestemt når verdien er definert. Denne funksjonen, automatisk, tillater en generikk som kan sammenlignes med generiske i Java eller C # eller maler i C ++ .

Utvidelsene av ML-typing som kreves av integrering av avanserte funksjoner, for eksempel objektorientert programmering, gjør imidlertid i visse tilfeller typesystemet mer komplekst: bruken av disse funksjonene kan da kreve en læringstid for programmereren, noe som ikke er ikke nødvendigvis kjent med sofistikerte typesystemer.

Filtrering

Den mønstergjenkjenning (i engelsk  : mønstergjenkjenning ) er et vesentlig element i Caml språket. Det gjør det mulig å lette koden takket være en mer fleksibel skriving enn tradisjonelle forhold, og uttømmingen er gjenstand for en sjekk: kompilatoren foreslår et moteksempel når en ufullstendig filtrering oppdages. For eksempel er følgende kode samlet, men det forårsaker en advarsel:

# type etat = Actif | Inactif | Inconnu;; type etat = Actif | Inactif | Inconnu # let est_actif = function # | Actif -> true # | Inactif -> false;; val est_actif : etat -> bool = <fun> Warning P: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Inconnu

Dermed fungerer programmet når funksjonen is_active kalles med statusen Aktiv eller Inaktiv , men hvis den er ukjent , kaster funksjonen Match_failure- unntaket .

Moduler

De modulene tillater å avbryte programmet inn i et hierarki av strukturer som inneholder typer og logisk relaterte verdier (for eksempel alle listene manipulasjon funksjoner er i List-modul). Etterkommerne av ML-familien er språkene som for tiden har de mest sofistikerte modulsystemene, som tillater, i tillegg til å ha navnerom, å implementere abstraksjon (tilgjengelige verdier hvis implementering er skjult) og komposisjonsevne (verdier som kan bygges på toppen av forskjellige moduler, så lenge de svarer på et gitt grensesnitt).

Dermed er de tre syntaktiske enhetene for syntaktisk konstruksjon strukturer, grensesnitt og moduler. Strukturer inneholder implementering av moduler, grensesnitt beskriver verdiene som er tilgjengelige fra dem (verdier hvis implementering ikke er eksponert er abstrakte verdier, og de som ikke vises i det hele tatt i implementeringen av modulen. Er utilgjengelige, som private metoder i objektorientert programmering). En modul kan ha flere grensesnitt (så lenge de alle er kompatible med implementeringstypene), og flere moduler kan verifisere et enkelt grensesnitt. Funktorer er strukturer som er parameterisert av andre strukturer; for eksempel kan hashtabellene (Hashtbl-modulen) i standard OCaml-biblioteket brukes som en funksjon, som tar en hvilken som helst struktur som implementerer grensesnittet som består av en type, en likestillingsfunksjon mellom tastene og en hash-funksjon .

Objekt orientert

OCaml utmerker seg spesielt ved sin utvidelse av ML-typing til et objektsystem som kan sammenlignes med de som brukes av klassiske objektspråk. Dette muliggjør strukturell undertyping , der objekter er av kompatible typer hvis typene av metodene deres er kompatible, uavhengig av deres respektive arvetrær . Denne funksjonaliteten, som kan betraktes som ekvivalent med andetyping av dynamiske språk, tillater en naturlig integrering av objektkonsepter i et generelt funksjonelt språk.

Dermed, i motsetning til objektorienterte språk som C ++ eller Java som hver klasse definerer en type for, definerer OCaml-klasser heller typeforkortelser. Så lenge typen metoder er kompatibel, kan to objekter av to forskjellige klasser brukes likegyldig i samme sammenheng. Denne egenskapen til objektlaget til OCaml bryter et stort antall allment aksepterte prinsipper: det er virkelig mulig å gjøre subtyping uten arv, for eksempel. Den polymorfe siden bryter det motsatte prinsippet. Eksempler på kode eksisterer også, selv om det er sjeldent, og som viser tilfeller av arv uten undertyping. Styrken til objektlaget ligger i dets homogenitet og perfekt integrering i filosofien og selve ånden til OCaml-språket. Funksjonelle objekter, hvis attributter ikke kan endres, og hvis metoder, hvis noen, returnerer en kopi med oppdateringen av attributtene, eller definisjonen av umiddelbare objekter, eller i farta, er også mulig.

Fordeling

Distribusjonen av OCaml inneholder:

  • en interaktiv tolk (ocaml);
  • en bytecode- kompilator (ocamlc) og bytecode- tolk (ocamlrun);
  • en innfødt kompilator (ocamlopt);
  • leksikalsk (ocamllex) og syntaktisk (ocamlyacc) analysator generatorer ;
  • en preprosessor (camlp4), som tillater utvidelser eller modifikasjoner av språket syntaks;
  • en trinnvis feilsøking , med tilbakeføring (ocamldebug);
  • profilering verktøy  ;
  • en dokumentasjonsgenerator (ocamldoc);
  • en automatisk kompilasjonsleder (ocamlbuild), siden OCaml 3.10;
  • en variert standard bibliotek .

OCaml-verktøy brukes regelmessig på Windows , GNU / Linux eller MacOS , men finnes også på andre systemer som BSD .

Bytecode- kompilatoren lar deg lage filer som deretter tolkes av ocamlrun. Den bytekode blir plattformuavhengig, sikrer denne store portabilitet (ocamlrun kan a priori være sammensatt på en hvilken som helst plattform som støtter en funksjonell C kompilator). Den opprinnelige kompilatoren produserer plattformspesifikk monteringskode, som ofrer bærbarheten til den produserte kjørbare filen for forbedret ytelse. En innfødt kompilator er til stede for plattformene IA-32 , PowerPC , AMD64 , Alpha , Sparc , Mips , IA-64 , HPPA og StrongARM .

Et kompatibilitetsgrensesnitt lar deg koble OCaml-kode til C- primitiver , og formatet for flytende punktarrays er kompatibelt med C og Fortran . OCaml tillater også integrering av OCaml-kode i et C-program, noe som gjør det mulig å distribuere OCaml-biblioteker til C-programmerere uten at de trenger å vite eller til og med installere OCaml.

OCaml-verktøy er for det meste kodet i OCaml, med unntak av noen biblioteker og bytekodetolken , som er kodet i C. Spesielt er den opprinnelige kompilatoren fullstendig kodet i OCaml.

Minnehåndtering

OCaml har, i likhet med Java, en automatisk minneadministrasjon, takket være en generasjonell inkrementell søppelsamling . Dette er spesielt tilpasset et funksjonelt språk (optimalisert for rask tildeling / frigjøring av små gjenstander) og har derfor ingen merkbar innvirkning på programmets ytelse. Den kan konfigureres for å forbli effektiv i atypiske minnebrukssituasjoner.

Fremførelser

OCaml skiller seg fra de fleste språk som er utviklet i akademiske miljøer med god ytelse . I tillegg til de "klassiske" lokale optimaliseringene som er utført av den innfødte kodegeneratoren , har forestillingene fordeler av språketes funksjonelle og statiske og sterkt typede natur.

Dermed blir skriveinformasjonen fullstendig bestemt ved kompileringstidspunktet, og trenger ikke å reproduseres i den opprinnelige koden, noe som blant annet tillater å fjerne typetestene helt under kjøretid. På den annen side utnytter noen algoritmer fra standardbiblioteket de interessante egenskapene til rene funksjonelle datastrukturer: Dermed er den sammensatte foreningsalgoritmen asymptotisk raskere enn de imperative språkene, fordi den bruker deres ikke-mutabilitet. sett for å utgjøre utgangssettet (dette er banekopieringsteknikken for vedvarende datastrukturer).

Historisk sett har funksjonelle språk blitt betraktet som sakte av noen programmerere, fordi de naturlig krever implementering av konsepter (minnegjenoppretting, delvis applikasjon osv.) Som vi ikke visste hvordan vi skulle kompilere effektivt; fremskritt innen kompileringsteknikker har siden gjort det mulig å innhente den opprinnelige fordelen med tvingende språk. OCaml, ved å effektivt optimalisere disse delene av språket og implementere en søppeloppsamler tilpasset hyppige tildelinger av funksjonelle språk, var et av de første funksjonelle språkene som demonstrerte den gjenoppdagede effektiviteten av funksjonell programmering.

Generelt er hastigheten på utførelsen er litt mindre enn for en tilsvarende kode i C . Xavier Leroy snakker forsiktig om "ytelse på minst 50% av en rimelig C-kompilator". Disse prognosene har siden blitt bekreftet av en rekke referanser. I praksis holder programmene seg generelt innenfor dette området (1 til 2 ganger C-koden), med ekstremer i begge retninger (noen ganger raskere enn C, noen ganger veldig bremset av en uheldig interaksjon med søppeloppsamleren. I alle fall er dette fremdeles raskere enn de siste språkene som ikke er kompilert, som Python eller Ruby , og kan sammenlignes med statiske språk som er kompilert i farta som Java eller C # .

bruk

OCaml-språket, som stammer fra forskningssirkler, drar ikke nytte av annonseringskraften til visse nåværende programmeringsspråk. Det er derfor fortsatt relativt lite kjent for allmennheten (så vel som de fleste funksjonelle språk), men er likevel godt etablert i noen få nisjer der språkets kvaliteter går foran dets manglende popularitet.

utdanning

OCaml er språket som brukes av franske forberedende klasser , der det lyktes med Caml Light, med tanke på datamaskinalternativtestene for opptaksprøver til grandes écoles. Dokumentene brukes innenfor rammen av denne undervisningen markere sammenhengen mellom funksjonell programmering og matematikk, og hvor lett språk blant ML familie håndtak rekursive datastrukturer, nyttige for å lære algoritmer .

Han lider i akademia av konkurranse fra Haskell- språket , som er foretrukket fremfor ham i noen funksjonelle programmeringskurs, fordi han blant annet ikke tar opp noe begrep med tvingende programmering .

Undersøkelser

OCaml er et ganske brukt språk i søkeverdenen. Historisk sett har språkene i ML-grenen alltid vært nært knyttet til feltet for formelle bevis systemer ( Robin Milners innledende ML så ut til å bli brukt i LCF-bevis systemet). OCaml er språket som brukes av en av de viktigste programvarene i feltet, Coq proof assistent .

OCaml er involvert i mange andre områder innen informatikkforskning, inkludert forskning i programmeringsspråk og kompilatorer (se Avledede språk ) eller Unison -filsynkroniseringsprogramvare .

Industri

Til tross for sin relativt sjenerte kommunikasjon, har OCaml bygget opp en solid brukerbase i bestemte områder av bransjen. Dermed bruker flyindustrien OCaml for sin programmeringssikkerhet og effektivitet for formulering av komplekse algoritmer. I dette området kan vi sitere ASTRÉE- prosjektet , brukt blant annet av Airbus- selskapet . Luster- synkron kompilator for sanntids programmeringsspråk , brukt til kritiske systemer som Airbus avionics-systemer eller kontroll av visse atomkraftverk, er også skrevet i OCaml.

OCaml brukes av store aktører i programvareindustrien, for eksempel Microsoft eller XenSource , begge medlemmer av Caml Consortium. Det finner også applikasjoner innen finansiell databehandling, som vist av selskapet Jane Street , som sysselsetter mange OCaml-programmerere, eller Lexifi, et fransk selskap som spesialiserer seg i design av programmeringsspråk dedikert til økonomi, og som dessuten har blitt belønnet internasjonalt.

Til slutt brukes den også av generalistfrie prosjekter, som MLDonkey , GeneWeb , Liquidsoap webradioklienten , FFTW-biblioteket, samt litt programvare for KDE- skrivebordsmiljøet . Til slutt genereres de matematiske formlene til MediaWiki- programvaren av et program skrevet i OCaml.

Språkpresentasjon

Bonjour Monde

Vurder følgende hello.ml-program:

print_endline "Hello world!"

Den kan kompileres i kjørbar bytekode med ocamlc bytekode- kompilatoren:

$ ocamlc -o hello hello.ml

Den kan også kompileres til optimalisert innfødt kode som kan kjøres med den innfødte ocamlopt-kompilatoren:

$ ocamlopt -o hello hello.ml

Programmet kan deretter kjøres av ocamlrun bytecode tolk:

$ ./hello Hello world!

eller

$ ocamlrun hello Hello world!

Variabler

Den interaktive ocaml-tolk kan brukes. Den starter en "#" kommandoprompt hvoretter OCaml-instruksjoner kan legges inn, avsluttes av tegn ;;(disse sluttbrukertegnene skal bare brukes i den interaktive tolk, de er ikke en del av språksyntaks). For eksempel, for å definere en variabel som xinneholder resultatet av beregningen 1 + 2 * 3, skriver vi:

$ ocaml # let x = 1 + 2 * 3;;

Etter å ha skrevet inn og validert dette uttrykket, bestemmer OCaml uttrykkstypen (i dette tilfellet er det et helt tall) og viser resultatet av beregningen:

val x : int = 7

Man kan bli fristet til å utføre alle slags beregninger. Vær imidlertid forsiktig så du ikke blander heltall og realer, noe som ofte gjøres på mange språk, for i OCaml blir de ikke automatisk konvertert (du må bruke en funksjon som tar et heltall og returnerer et reelt, eller omvendt hvis operatøren bruker heltall). I det følgende eksemplet forventer + -operatøren å legge til to heltall når de er to virkelige:

# 2.3 + 1.;; Error: This expression has type float but an expression was expected of type int

Dette enkle eksemplet gir en første ide om hvordan typen inferensalgoritme fungerer. Når vi skrev 2.3 + 1., la vi faktisk til de reelle tallene 2.3og 1.med +heltalloperatøren, noe som utgjør et problem. For å utføre denne beregningen må vi faktisk sørge for at alle tallene har samme type på den ene siden (for eksempel er det umulig å legge til 2.3og 1fordi 1er et heltall i motsetning til 1.eller 2.3), og på den annen side bruker loven av intern sammensetning +brukt på reelle tall, bemerket +.i OCaml. Så vi burde ha skrevet:

# 2.3 +. 1.;; - : float = 3.3

Funksjoner

Programmer er ofte strukturert i prosedyrer og funksjoner. Prosedyrene er sammensatt av et sett med kommandoer som er brukt flere ganger i programmet, og gruppert for enkelhets skyld under samme navn. En prosedyre returnerer ikke en verdi, denne rollen tildeles funksjonene. Mange språk har forskjellige nøkkelord for å introdusere en ny prosedyre eller en ny funksjon ( prosedyre og funksjon i Pascal , sub og funksjon i Visual Basic ...). OCaml har derimot bare funksjoner, og disse er definert på samme måte som variabler. For å definere identiteten kan vi for eksempel skrive:

# let id x = x;;

Etter å ha tastet inn og validert uttrykket, bestemmer typesyntese-algoritmen typen av funksjonen. Imidlertid, i eksemplet vi har gitt, forutsier ingenting typen x, så funksjonen fremstår som polymorf (til ethvert element i settet 'aknytter det et bilde id xsom er et element i settet 'a):

val id : 'a -> 'a = <fun>

For å ringe en funksjon bruker vi følgende syntaks:

# id 5;; - : int = 5

OCaml tillater også bruk av anonyme funksjoner, det vil si funksjoner som ikke er knyttet til en identifikator, takket være nøkkelordet functioneller fun :

# function x -> x;; - : 'a -> 'a = <fun>

Anonyme funksjoner kan kalles umiddelbart eller brukes til å definere en funksjon:

# (function x -> x + 1) 4;; - : int = 5 # let id = function x -> x;; val id : 'a -> 'a = <fun>

Mønsterfiltrering

En kraftig funksjon i OCaml er mønstermatching . Det kan defineres med nøkkelordene match witheller med en anonym funksjon, etterfulgt for hvert mønster av en vertikal stolpe |, mønsteret, en pil ->og returverdien:

# let est_nul x = # match x with # | 0 -> true (* la première barre verticale est facultative *) # | _ -> false;; val est_nul : int -> bool = <fun> # function # | 0 -> true (* la première barre verticale est facultative *) # | _ -> false;; - : int -> bool = <fun>

Understreken _representerer standardmønsteret. Det er mulig å gi samme verdi til flere mønstre samtidig:

# let contient_zero (x, y) = # match x, y with # | (0, _) | (_, 0) -> true # | _ -> false;; val contient_zero : int * int -> bool = <fun>

Nøkkelordet whenbrukes til å uttrykke en tilstand på mønsteret:

# let contient_zero (x, y) = # match x, y with # | (x, y) when (x = 0 || y = 0) -> true # | _ -> false;; val contient_zero : int * int -> bool = <fun>

Tegnene ..brukes til å uttrykke et mønster som filtrerer rekkevidden av tegn:

# let est_capitale c = # match c with # | 'a'..'z' -> false # | 'A'..'Z' -> true # | _ -> failwith "lettre invalide";; val est_capitale : char -> bool = <fun>

Nøkkelordet asbrukes til å navngi den filtrerte verdien:

# let mettre_en_capitale c = # match c with # | 'a'..'z' as lettre -> Char.uppercase_ascii lettre # | 'A'..'Z' as lettre -> lettre # | _ -> failwith "lettre invalide";; val mettre_en_capitale : char -> char = <fun>

Rekursjon

Den rekursjon er å skrive en funksjon som refererer seg til, etter modell av induksjon. I OCaml introduseres rekursive funksjoner ved hjelp av nøkkelordet rec.

For eksempel kan vi definere faktorfunksjonen  :

# let rec fact n = # match n with # | 0 -> 1 # | _ -> n * fact (n - 1);; val fact : int -> int = <fun>

Vi kan definere Fibonacci-sekvensen ved å:

# let rec fib n = # match n with # | 0 -> 0 # | 1 -> 1 # | _ -> fib (n - 1) + fib (n - 2);; val fib : int -> int = <fun>

Intern definisjon

Det er mulig å definere variabler og funksjoner i en funksjon ved hjelp av nøkkelordet in.

For eksempel kan vi definere faktorfunksjonen:

# let fact n = # let rec fact_aux m a = # match m with # | 0 -> a # | _ -> fact_aux (m - 1) (m * a) # in fact_aux n 1;; val fact : int -> int = <fun>

Vi kan definere Fibonacci-sekvensen ved å:

# let fib n = # let rec fib_aux m a b = # match m with # | 0 -> a # | _ -> fib_aux (m - 1) b (a + b) # in fib_aux n 0 1;; val fib : int -> int = <fun>

Terminal rekursjon

OCaml-kompilatoren optimaliserer terminalanrop: når det siste trinnet som skal utføres under evalueringen av en funksjon er anropet til en (annen) funksjon, hopper OCaml direkte til denne nye funksjonen uten å holde samtalen i minnet. Av den første, nå ubrukelig.

Spesielt optimaliserer OCaml terminalrekursjon . For eksempel er den andre funksjonen factovenfor (med en hjelpeparameter a) en terminalfunksjon, tilsvarende en sløyfe, og vil gi et ekvivalent resultat, og dermed matche ytelsen til den tilsvarende tvingende koden.

Terminalrekursjon er like effektiv som iterasjon; det er derfor å foretrekke når det lar deg skrive programmer som er tydeligere eller lettere å håndtere.

Manipulere lister

De lister brukes i programmering, spesielt for rekursiv behandling. Elementene i en liste har alle samme type. For å lage en liste er to skrifter mulige, en med kjettingoperatøren ::og den andre med operatøren ; :

# 1 :: 2 :: 3 :: [];; - : int list = [1; 2; 3] # [1; 2; 3];; - : int list = [1; 2; 3]

Den rette operanden til den siste operatøren av ::et uttrykk må være en liste:

# 1 :: 2 :: 3 :: [4; 5];; - : int list = [1; 2; 3; 4; 5]

Det er mulig å sammenkoble lister med sammenkoblingsoperatøren @ :

# [1; 2; 3] @ [4; 5];; - : int list = [1; 2; 3; 4; 5]

For å finne ut lengden på en liste uten å bruke funksjonen som er List.lengthdefinert for dette formålet, kan vi skrive:

# let rec longueur l = # match l with # | [] -> 0 # | _ :: q -> 1 + longueur q;; val longueur : 'a list -> int = <fun>

Når du analyserer denne funksjonen etter algoritmen for typeinferanse, ser det ut til at listen kan inneholde alle typer data 'a.

Følgende funksjon oppretter en liste over par fra to lister: lengden på denne listen vil være lik lengden på listen som sendes i parameteren som er den korteste.

# let rec couple l1 l2 = # match l1, l2 with # | ([], _) | (_, []) -> [] # | (t1 :: q1, t2 :: q2) -> (t1, t2) :: couple q1 q2;; val couple : 'a list -> 'b list -> ('a * 'b) list = <fun>

Følgende funksjon henter det første elementet i en liste:

# let premier_element l = # match l with # | [] -> failwith "liste vide" # | e :: _ -> e;; val premier_element : 'a list -> 'a = <fun>

Følgende funksjon henter det andre elementet i en liste:

# let deuxieme_element l = # match l with # | [] -> failwith "liste vide" # | [_] -> failwith "liste à un élément" # | _ :: e :: _ -> e;; val deuxieme_element : 'a list -> 'a = <fun>

Følgende funksjon henter det første elementet i den første underlisten:

# let sous_premier_element l = # match l with # | [] -> failwith "liste vide" # | [] :: _ -> failwith "sous liste vide" # | (e :: _) :: _ -> e;; val sous_premier_element : 'a list list -> 'a = <fun>

Funksjoner med høyere ordre

Funksjonene med høyere orden er funksjoner som tar en eller flere funksjoner i innløpet og / eller returnerer en funksjon (dette kalles funksjonelt). De fleste funksjonelle språk har høyere ordensfunksjoner. Angående Objective Caml, kan man finne eksempler i de ferdige moduler funksjoner Array, Listetc. For eksempel følgende uttrykk:

# List.map (function i -> i * i) [0; 1; 2; 3; 4; 5];; - : int list = [0; 1; 4; 9; 16; 25]

Funksjonen maptar som argument den anonyme funksjonen som i sin helhet iknytter kvadratet og bruker den på elementene i listen, og bygger dermed listen over kvadratiske verdier.

Et annet eksempel :

# let double f i = f (f i);; val double : ('a -> 'a) -> 'a -> 'a = <fun>

Funksjonen doubletar en funksjon fog en verdi som parameter iog gjelder to ganger ffor i.

# let trois = double (function i -> i + 1) 1;; val trois : int = 3 # let augmente_2 = double (function i -> i + 1);; val augmente_2 : int -> int = <fun> # let liste = # double ( # function # | [] -> [] # | e :: l -> (e + 1) :: l # ) [1; 2; 3];; val liste : int list = [3; 2; 3]

Her er et eksempel:

# let rec parcours f e l = # match l with # | [] -> e # | t :: q -> f t (parcours f e q);; val parcours : ('a -> 'b -> 'b) -> 'b -> 'a list -> 'b = <fun> # (* somme des éléments de la liste [1; 1; 2] *) # parcours (+) 0 [1; 1; 2];; - : int = 4 # (* fonction calculant la somme des éléments d'une liste *) # let somme_liste = parcours (+) 0;; val somme_liste : int list -> int = <fun> # (* fonction calculant le produit des éléments d'une liste *) # let produit_liste = parcours ( *. ) 1.;; val produit_liste : float list -> float = <fun>

Endelig et siste eksempel. Her er vi ansvarlige for å definere en ny kjent operatør $. Denne operatøren utfører kompositten av to funksjoner.

# let ( $ ) f g = function x -> f (g x) val ( $ ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun> # let f x = x * x;; val f : int -> int = <fun> # let g x = x + 3;; val g : int -> int = <fun> # let h = f $ g;; val h : int -> int = <fun> # (* affiche 36 *) # print_int (h 3);;

Rekursive trær og typer

For å definere et binært tre av hvilken som helst type, bruker vi en rekursiv type. Vi kan således benytte oss av følgende skriving:

# type 'a arbre = # | Feuille # | Branche of 'a arbre * 'a * 'a arbre;; type 'a arbre = Feuille | Branche of 'a arbre * 'a * 'a arbre

Dette treet består av grener som forgrener seg etter ønske, og ender i blader. For å vite høyden på et tre bruker vi deretter:

# let rec hauteur = # function # | Feuille -> 0 # | Branche (gauche, _, droite) -> 1 + max (hauteur gauche) (hauteur droite);; val hauteur : 'a arbre -> int = <fun>

Rotsøk etter dikotomi

# let rec dicho f min max eps = # let fmin = f min and fmax = f max in # if fmin *. fmax > 0. then failwith "Aucune racine" # else if max -. min < eps then (min, max) (* retourne un intervalle *) # else let mil = (min +. max)/.2. in # if (f mil) *. fmin < 0. then dicho f min mil eps # else dicho f mil max eps;; val dicho : (float -> float) -> float -> float -> float -> float * float = <fun> (* approximation de la racine carrée de 2 *) # dicho (function x -> x *. x -. 2.) 0. 10. 0.000000001;; - : float * float = (1.4142135618, 1.41421356238)

Memoisisering

Her er et eksempel på en funksjon som bruker memoization . Dette er en funksjon som beregner den niende termen til Fibonacci-sekvensen . I motsetning til den klassiske rekursive funksjonen husker denne resultatene og kan bringe dem ut i konstant tid takket være hasjbordet.

(* Taille de la table de hachage. *) let _HASH_TABLE_SIZE = 997 (* Retourne le n-ième terme de la suite de Fibonacci. *) let rec fibo = (* Pré-définitions. Ce code est exécuté une fois lors de la définition de la fonction, mais ne l'est pas à chaque appel. Cependant, `h` reste dans l'environnement de cette fonction pour chaque appel. *) let h = Hashtbl.create _HASH_TABLE_SIZE in (* Premiers termes. *) Hashtbl.add h 0 0; Hashtbl.add h 1 1; function | n when n < 0 -> invalid_arg "fibo" (* Pas de nombre négatif. *) | n -> try Hashtbl.find h n (* On a déjà calculé `fibo n`, on ressort donc le résultat stocké dans la table `h`. *) with Not_found -> (* Si l'élément n'est pas trouvé, … *) let r = fibo (n - 1) + fibo (n - 2) in (* … on le calcule, … *) Hashtbl.add h n r; (* … on le stocke, … *) r (* … et on renvoie la valeur. *)

Utledning av et polynom

Vi foreslår her å implementere en enkel funksjon som gjør det mulig å utlede et polynom i alle grader. Vi må først spesifisere typen som vil representere vårt polynom:

# type polyn = # | Num of float (* constante *) # | Var of string (* variable *) # | Neg of polyn (* négation *) # | Add of polyn * polyn (* addition *) # | Sub of polyn * polyn (* soustraction *) # | Mul of polyn * polyn (* multiplication *) # | Div of polyn * polyn (* division *) # | Pow of polyn * int;; (* exponentiation *) type polyn = Num of float | Var of string | Neg of polyn | Add of polyn * polyn | Sub of polyn * polyn | Mul of polyn * polyn | Div of polyn * polyn | Pow of polyn * int

Nå er her funksjonen som utleder dette polynomet med hensyn til variabelen x spesifisert i parameteren.

# let rec deriv x = function # | Num _ -> Num 0. # | Var y when y = x -> Num 1. # | Var _ -> Num 0. # | Neg p -> Neg (deriv x p) (* -p' *) # | Add (p, q) -> Add (deriv x p, deriv x q) (* p' + q' *) # | Sub (p, q) -> Sub (deriv x p, deriv x q) (* p' - q' *) # | Mul (p, q) -> Add (Mul (deriv x p, q), Mul (p, deriv x q)) (* p'q + pq' *) # | Div (p, q) -> Div (Sub (Mul (deriv x p, q), Mul (p, deriv x q)), Pow (q, 2)) (* (p'q - pq')/q^2 *) # | Pow (p, 0) -> Num 0. # | Pow (p, 1) -> deriv x p # | Pow (p, n) -> Mul (Num (float_of_int n), Mul (deriv x p, Pow (p, n - 1))) (* n * p' * p^(n - 1) *) val deriv : string -> polyn -> polyn = <fun>

Ulike kompileringsmål

OCaml-implementeringen er blitt tilpasset av andre forfattere for andre kompileringsmål enn bytecode og native code. Vi finner:

  • OCaml-Java, en distribusjon for JVM som inneholder ocamlc, ocamlrun, ocamldep, ocamldoc, ocamllex, menhir, en ocamljava kompilator til JVM;
  • OCamIL, en prototype- backend for .NET-miljøet. Den inneholder en kompilator til OCaml bytecode (kjørbar av ocamlrun), en kompilator til .NET og et verktøy som ocamlyacc som heter ocamilyacc.

Avledede språk

Mange språk utvider OCaml for å legge til funksjonalitet i den.

  • F # er et språk i .NET-plattformen utviklet av Microsoft Research, basert på OCaml (og delvis kompatibel).
  • MetaOCaml legger til en tilbuds- og kodegenereringsmekanisme i kjøretiden , noe som gir metaprogrammeringsfunksjonalitet til OCaml.
  • Fresh OCaml (basert på AlphaCaml, et annet derivat av OCaml) gjør det lettere å manipulere symbolske navn.
  • JoCaml legger til OCaml-støtte for Join Calculus, rettet mot samtidige eller distribuerte programmer.
  • OcamlP3L gir en spesiell form for parallellisme, basert på " skjelettprogrammering" .
  • GCaml legger til ad-hoc polymorfisme til OCaml, slik at operatøren kan overbelaste eller samle bevaring av skriveinformasjon .
  • OCamlDuce lar typesystemet representere XML-verdier eller relatert til regulære uttrykk. Det er et mellomledd mellom OCaml og CDuce-språket, spesialisert i manipulering av XML.
  • Opa er et språk for utvikling av webapplikasjoner og tjenester implementert i OCaml, og hvis kjerne inneholder funksjonene til OCaml-språket. I tillegg bruker Opa-kompilatoren backend av OCaml-kompilatoren til å generere native servere.
  • ReasonML utviklet av Facebook, er et språk som bruker OCaml-kompilatoren, slik at du kan samle Reason (.re) og OCaml (.ml) -kode til JavaScript-kildekode (.js), som igjen kan samles til bytekode, brukt i nettleser eller via en tolk som Node.js.

Merknader og referanser

  1. "  Messenger.com nå 50% konvertert til grunn · grunn  " , på reasonml.github.io (åpnet 27. februar 2018 )
  2. Noen suksesser med OCaml: SLAM
  3. "Funksjonelle programmer er programmer som generelt tildeler mye, og vi ser at mange verdier har en veldig kort levetid." På den annen side, så snart en verdi har overlevd flere GC-er, har den en god sjanse for å eksistere en god stund ”- Applikasjonsutvikling med Objective Caml
  4. "Å  generere effektiv maskinkode har alltid vært et viktig aspekt av OCaml, og jeg brukte ganske mye arbeid på dette i begynnelsen av OCaml-utviklingen (95-97). I dag er vi stort sett fornøyde med ytelsen til den genererte koden.  »- Xavier Leroy, på kamlens adresseliste .
  5. Garantier fra typesystemet kan også muliggjøre kraftige programoptimaliseringer.  »- Xavier Leroy, Introduksjon til typer i kompilering .
  6. (no) tråd for adresselisten  : "  Vår ytelseserklæring" OCaml leverer minst 50% av ytelsen til en anstendig C-kompilator "er ikke ugyldiggjort  :-)"
  7. (no) shootout: OCaml vs. C målestokk .
  8. ytelses sammenligning mellom ocamlopt og C # Mono .
  9. "  Memorandum ESRS1732186N of November 27, 2017  "
  10. Undervisningsprogrammering med Caml
  11. Citeseer: En liste over forskningsartikler som bruker Caml
  12. Nettstedet til ASTRÉE-prosjektet , konsultert på www.astree.ens.fr på15. januar 2011
  13. "Abstrakt tolkning: applikasjon til programvaren til A380" , Patrick Cousot, konsultert på www.di.ens.fr le15. januar 2011
  14. Caml Consortium
  15. FFTW-biblioteket, som utfører en rask Fourier-transformasjon , består av kode på C- språk . Av ytelsesgrunner genereres og optimaliseres imidlertid C-koden automatisk av en kompilator, genfft , skrevet i OCaml. Prosessen med å generere og spesialisere rutiner er beskrevet i artikkelen A Fast Fourier Transform Compiler , av Matteo Frigo (MIT) [ les online  (side konsultert 9. desember 2007)] . Vi finner i FFTW-dokumentasjonen en forståelse av bruken av OCaml: “  Genfft-pakken med kodegeneratorer ble skrevet med Objective Caml, en dialekt av ML. Objective Caml er et lite og elegant språk utviklet av Xavier Leroy. Implementeringen er tilgjengelig fra http://caml.inria.fr/ . I tidligere utgivelser av FFTW ble genfft skrevet i Caml Light, av de samme forfatterne. En enda tidligere implementering av genfft ble skrevet i Scheme , men Caml er definitivt bedre for denne typen applikasjoner.  "- FFTW-anerkjennelser
  16. EqChem komponentside for Kalzium programvare
  17. Et program som bruker terminalanrop er ofte mer lesbart enn en tilsvarende iterasjon når hoppmønsteret er komplekst. For eksempel kan vi beskrive en automat som et sett med overgangsfunksjoner som utfører terminalsamtaler (eller hopper gotopå tvingende språk) til de andre tilstandene til automaten. I dette tilfellet tillater terminalanropene mer fleksibilitet, som vist i artikkelen (i) Automatas via makroer .

Se også

Relaterte artikler

Eksterne linker