OCaml

OCaml
Logó.
Az első változat kelte 1987 (CAML), 1996 (OCaml)
Paradigma Multiparadigma  : imperatív , funkcionális , objektum-orientált
Fejlesztő Inria
Utolsó verzió 4.12.0 ( 2021. február 24)
Gépelés Erős , statikus
Nyelvjárások JoCaml, Fresh OCaml, GCaml, MetaOCaml, OCamlDuce, OcamlP3L
Befolyásolta ML
Befolyásolt F # , Rozsda , OPA , Scala
Beírva OCaml
Operációs rendszer Cross-platform
Engedély LGPL 2.1
Weboldal ocaml.org
Fájlkiterjesztés ml és mli

OCaml , korábbi nevén célkitűzés Caml , a legfejlettebb végrehajtását a Caml programozási nyelv által létrehozott Xavier Leroy , Jérôme Vouillon , Damien Dolurez , Didier Rémy és munkatársaik a 1996 . Ez az ML nyelvcsaládból származó nyelv egy nyílt forráskódú projekt , amelyet főként az Inria irányít és tart fenn .

Az OCaml a Caml Light utódja , amelyhez többek között egy objektum programozási réteget is hozzáadott. A betűszó CAML származik kategorikus Abstract Machine Language , egy absztrakt gép modell , amely azonban már nem használják az utóbbi változat a OCaml.

Hordozható és nagy teljesítményű OCaml-t olyan sokféle projektben használnak, mint az Unison fájlszinkronizáló szoftver , a Coq hivatalos igazoló asszisztens vagy a Facebook Messenger webes verziója . A nyelv szimbolikus feldolgozási lehetőségei lehetővé teszik statikus ellenőrző eszközök kifejlesztését, például a Microsoft által írt SLAM-projektet Windows pilóták számára , vagy az ASTRÉE-t az Airbus A380- asok egyes fedélzeti rendszereihez .

Alapelvek

A Caml egy funkcionális nyelv, amelyet olyan funkciókkal egészítettek ki, amelyek lehetővé teszik az imperatív programozást . Az OCaml kiterjeszti a nyelv lehetőségeit azáltal, hogy lehetővé teszi az objektum-orientált programozást és a moduláris programozást . Mindezen okokból az OCaml a több paradigma nyelv kategóriájába tartozik .

Ezeket a különböző fogalmakat integrálja az ML-től örökölt típusrendszerbe, amelyet statikus , erős és következtetett gépelés jellemez .

A típusrendszer lehetővé teszi a bonyolult adatszerkezetek egyszerű manipulálását: könnyen ábrázolhatunk algebrai típusokat , vagyis hierarchikus és potenciálisan rekurzív típusokat (listák, fák stb.), És könnyen kezelhetjük őket a mintaillesztés segítségével . Ez az OCaml-t választott nyelvvé teszi olyan területeken, amelyek bonyolult adatstruktúrák manipulációját igénylik, például fordítók .

Az erőteljes gépelés , valamint a memória kifejezett manipulációjának hiánya ( szemétgyűjtő jelenléte ) miatt az OCaml nagyon biztonságos nyelv. A teljesítményéről is híres, köszönhetően a natív kódfordító jelenlétének .

Történelem

A Caml nyelv az ML programozási nyelv találkozásából született, amely az 1980-as évek eleje óta a Formel of INRIA csapatot , és a Pierre-Louis Curien 1984 -es munkáján alapuló kategorikus absztrakt gépet, a CAM Guy Cousineau- t összpontosítja . Az első megvalósítást Ascander Suarez (akkor a párizsi Diderot Egyetem doktorandusz hallgatója ) írta, majd Pierre Weis és Michel Mauny tartotta fenn 1987-ben . A nyelv fokozatosan megkülönböztette az ML atyjától, mert az Inria csapata egy nyelvet saját igényeihez akart igazítani és tovább fejleszteni, ami a Standard ML szabványosítási erőfeszítései révén konfliktusba került az ML kényszerített "stabilitásával".

A CAM korlátai egy új megvalósítás létrehozásához vezettek, amelyet Xavier Leroy fejlesztett ki 1990-ben , Caml Light néven . Ez végrehajtása, beleértve a legutóbbi verzió még mindig használják az oktatásban ma , Bár a rendszer már nem tartja fenn a INRIA , keresztül működik tolmács byte-kódot ( bytecode ) kódolt C , ami azt nagy hordozhatóságot. A Damien Dolurez által tervezett memóriakezelő rendszer a Caml Light-ban is megjelent. A 1995 , Xavier Leroy közzétett változata Caml nevezett Caml különleges fény , amely bevezette a natív kód fordító, és a modul rendszer által inspirált standard ML modulokat.

Az 1996-ban először megjelent OCaml egy Didier Rémy és Jérôme Vouillon által tervezett objektorrendet hoz Caml-be. Néhány fejlett funkciót, például a polimorf változatokat vagy a címkéket (amelyek lehetővé teszik a függvénynek a nevük, nem pedig a pozíciója alapján adott érvek megkülönböztetését) 2000- ben Jacques Garrigue vezette be . Az OCaml azóta viszonylag stabilizálódott (a specifikáció hiánya ellenére, a hatályos dokumentum az Inria által vezetett hivatalos kézikönyv ). Az OCaml számos dialektusa jelent meg, és továbbra is vizsgálják a programozási nyelvek sajátos vonatkozásait (egyidejűség, párhuzamosság, lusta értékelés, XML integráció ...); lásd a Származtatott nyelvek részt .

Fő jellemzők

Funkcionális nyelv

Az OCaml rendelkezik a funkcionális nyelvek legtöbb közös jellemzőjével, különösen a magasabb rendű funkciókkal és bezárásokkal ( bezárásokkal ), valamint a farok rekurziójának jó támogatásával .

Gépelés

Az OCaml statikus gépelése nagyszámú programozási hibát észlel fordítási időben, amelyek problémákat okozhatnak a futás idején. A legtöbb más nyelvtől eltérően azonban nem szükséges megadni a használt változók típusát. Valójában a Caml rendelkezik egy következtetési algoritmussal, amely lehetővé teszi a változók típusának meghatározását abból a kontextusból, amelyben felhasználják őket.

Az ML tipizáló rendszer támogatja a paraméteres polimorfizmust , vagyis olyan típusokat, amelyek részei nem lesznek meghatározva, amikor az érték meghatározódik. Ez a funkció, automata, lehetővé teszi, hogy a generikus hasonló generikumok a Java vagy C # vagy sablonok a C ++ .

A fejlett funkciók, például az objektum-orientált programozás integrálásához szükséges ML tipizálás kiterjesztései azonban bizonyos esetekben bonyolultabbá teszik a típusrendszert: ezeknek a funkcionalitásoknak a felhasználása akkor tanulási időt igényelhet a programozó számára, ami nem nem feltétlenül ismeri a kifinomult típusú rendszereket.

Szűrő

A mintaillesztés ( angolul  : pattern matching ) a Caml nyelv alapvető eleme. Lehetővé teszi a kód megkönnyítését a hagyományos feltételeknél rugalmasabb írásnak köszönhetően, és a kimerültség egy ellenőrzés tárgya: a fordító ellenpéldát javasol, ha hiányos szűrést észlelnek. Például a következő kód kerül összeállításra, de figyelmeztetést okoz:

# 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

Így a program akkor működik, ha az is_active függvényt aktív vagy inaktív állapotban hívják meg , de ha ismeretlen , akkor a függvény dobja a Match_failure kivételt .

Modulok

A modulok lehetővé teszik a program struktúrák hierarchiájába bontását, amelyek típusokat és logikailag kapcsolódó értékeket tartalmaznak (például az összes lista manipulációs funkció a List modulban található). Az ML család leszármazottai azok a nyelvek, amelyek jelenleg a legfejlettebb modulrendszerekkel rendelkeznek, amelyek a névterek mellett lehetővé teszik az absztrakció (elérhető értékek, amelyek megvalósítása rejtve van) és az összetehetőség (olyan értékek, amelyek képesek különböző modulok tetejére kell építeni, amennyiben azok reagálnak egy adott interfészre).

Így a szintaktikai felépítés három szintaktikai egysége struktúra, interfész és modul. A struktúrák tartalmazzák a modulok megvalósítását, az interfészek leírják a belőlük elérhető értékeket (azok az értékek, amelyek megvalósítása nincs kitéve, absztrakt értékek, és azok, amelyek egyáltalán nem jelennek meg a modul megvalósításakor. Nem érhetők el, mint pl. privát módszerek az objektum-orientált programozásban). Egy modulnak több interfésze lehet (amennyiben mind kompatibilisek a megvalósítás típusaival), és több modul képes egyetlen interfészt ellenőrizni. A funktorok olyan struktúrák, amelyeket más struktúrák paramétereznek; Például a standard OCaml könyvtár hash táblái (Hashtbl modul) használhatók functorként, amely paraméterként figyelembe vesz minden olyan struktúrát, amely megvalósítja az interfészt, amely egy típusból, a billentyűk közötti egyenlőségfüggvényből és egy hash függvényből áll. .

Objektum orientált

Az OCaml-t különösen megkülönbözteti az ML tipizálás kiterjesztése egy objektumrendszerre, amely összehasonlítható a klasszikus tárgynyelvek által használt rendszerekkel . Ez lehetővé teszi a szerkezeti altípust , amelyben az objektumok kompatibilis típusok, ha a módszereik típusai kompatibilisek, függetlenül az öröklődési fáiktól . Ez a funkcionalitás, amely a dinamikus nyelvek kacsa tipizálásának egyenértékűnek tekinthető , lehetővé teszi az objektumfogalmak természetes integrálását egy általában funkcionális nyelvbe.

Így, ellentétben az olyan objektumorientált nyelvekkel, mint a C ++ vagy a Java , amelyekhez minden osztály meghatároz egy típust, az OCaml osztályok inkább típusrövidítéseket definiálnak. Mindaddig, amíg a módszerek típusa kompatibilis, két különböző osztály két objektuma használható közömbösen ugyanabban a kontextusban. Az OCaml objektumrétegének ez a jellemzője jó néhány általánosan elfogadott alapelvet megtör: valóban lehetséges például az öröklés nélküli altípus létrehozása. A polimorf oldal megtöri a fordított elvet. Léteznek ugyan példák a kódra, bár ritkák, de az altípus nélküli öröklés esetét mutatják be. A tárgyréteg ereje homogenitásában és tökéletes integrációjában rejlik az OCaml nyelv filozófiájában és szellemében. Funkcionális objektumok is, amelyek attribútumai nem módosíthatók, és amelyek módszerei, ha vannak ilyenek, visszaadnak egy példányt az attribútumok frissítésével, vagy a közvetlen objektumok meghatározásával, vagy menet közben.

terjesztés

Az OCaml disztribúció a következőket tartalmazza:

  • egy interaktív tolmács (ocaml);
  • Egy bájtkód fordító (ocamlc) és bytecode értelmező (ocamlrun);
  • natív fordító (ocamlopt);
  • lexikai (ocamllex) és szintaktikai (ocamlyacc) elemző generátorok ;
  • egy előfeldolgozó (camlp4), amely lehetővé teszi a nyelv szintaxisának kiterjesztését vagy módosítását;
  • egy lépésről-lépésre hibakereső , Rollback (ocamldebug);
  • profilkészítő eszközök  ;
  • dokumentációs generátor (ocamldoc);
  • automatikus fordításkezelő (ocamlbuild), az OCaml 3.10 óta;
  • egy változtatható szabványos könyvtárat .

Az OCaml eszközöket rendszeresen használják Windows , GNU / Linux vagy MacOS rendszereken , de léteznek más rendszereken is, például a BSD-n .

A bájtkód fordító lehetővé teszi fájlok létrehozását, amelyeket az ocamlrun értelmez. A bájtkód platformfüggetlen, ez biztosítja a nagy hordozhatóságot (az ocamlrun eleve minden olyan platformra lefordítható, amely egy funkcionális C fordítót támogat). A natív fordító platformspecifikus összeállítási kódot állít elő, amely feláldozza az előállított futtatható fájl hordozhatóságát a jelentősen javított teljesítmény érdekében. Az IA-32 , PowerPC , AMD64 , Alpha , Sparc , Mips , IA-64 , HPPA és StrongARM platformokhoz natív fordító van jelen .

A kompatibilitási felület lehetővé teszi az OCaml kód összekapcsolását a C primitívekkel , és a lebegőpontos tömbök formátuma kompatibilis a C és a Fortran . Az OCaml lehetővé teszi az OCaml kód integrálását egy C programba, lehetővé téve az OCaml könyvtárak terjesztését C programozóknak anélkül, hogy tudnunk kellene, vagy akár telepíteniük kellene az OCaml-t.

Az OCaml eszközöket többnyire az OCaml kódolja, kivéve néhány könyvtárat és a bytecode tolmácsot , amelyeket C kódolnak. Különösen a natív fordítót teljes egészében az OCaml kódolja.

Memóriakezelés

Az OCaml a Java-hoz hasonlóan automatizált memóriakezeléssel rendelkezik, köszönhetően a generációs növekményes szemétszállításnak . Ez kifejezetten egy funkcionális nyelvhez van adaptálva (optimalizálva a kis tárgyak gyors lefoglalásához / felszabadításához), és ezért nincs számottevő hatása a programok teljesítményére. Konfigurálható, hogy hatékony maradjon atipikus memóriahasználati helyzetekben.

Előadások

Az OCaml kiváló teljesítménytől különbözik a tudományos körökben kifejlesztett legtöbb nyelvtől . A natív kódgenerátor által végrehajtott „klasszikus” helyi optimalizálásmellett az előadások előnyösen részesülnek a nyelv funkcionális, statikus és erősen tipizált jellegéből.

Így a gépelési információk a fordítás idején teljesen meghatározódnak, és nem szükséges azokat a natív kódban reprodukálni, ami többek között lehetővé teszi a gépelési tesztek teljes eltávolítását futás közben. Másrészt a standard könyvtár egyes algoritmusai kihasználják a tiszta funkcionális adatszerkezetek érdekes tulajdonságait: így a beállított unió algoritmus aszimptotikusan gyorsabb, mint az imperatív nyelveké, mert nem mutálhatóságukat használja. készletek alkotják a kimeneti halmazt (ez az útvonali másolási technika a tartós adatstruktúrákhoz).

Történelmileg a funkcionális nyelveket lassúnak ítélték meg egyes programozók, mert természetesen olyan koncepciók megvalósítását igénylik (memória helyreállítás, részleges alkalmazás stb.), Amelyeket nem tudtunk hatékonyan összeállítani; Az összeállítási technikák fejlődése azóta lehetővé tette az imperatív nyelvek kezdeti előnyeinek felzárkóztatását. Az OCaml a nyelv ezen részeinek hatékony optimalizálásával és a funkcionális nyelvek gyakori kiosztásához igazított szemétgyűjtő alkalmazásával az elsőként funkcionális nyelvek között mutatta be a funkcionális programozás újrafelfedezettségét.

Általánosságban elmondható, hogy a végrehajtás sebessége valamivel kisebb, mint egy ekvivalens kód C-ben . Xavier Leroy óvatosan beszél „az ésszerű C fordító teljesítményének legalább 50% -ának teljesítményéről”. Ezeket az előrejelzéseket azóta számos referenciaérték megerősítette. A gyakorlatban a programok általában ezen a tartományon belül maradnak (a C-kód 1-2-szerese), mindkét irányban szélsőségesek (néha gyorsabbak, mint a C, néha nagymértékben lelassítják a szemétgyűjtővel való szerencsétlen interakciók . Mindenesetre ez még mindig gyorsabb, mint a legutóbbi nyelvek, amelyek nincsenek natívan lefordítva, például a Python vagy a Ruby , és összehasonlíthatók az olyan menet közben összeállított statikus nyelvekkel, mint a Java vagy a C # .

használat

A kutatási körökből származó OCaml nyelv nem profitál bizonyos jelenlegi programozási nyelvek hirdetési erejéből. Ezért továbbra is viszonylag kevéssé ismert a számítógépes közönség (valamint a legtöbb funkcionális nyelv) számára, de mégis szilárdan megalapozott néhány olyan résen, amelyekben a nyelv tulajdonságai elsőbbséget élveznek a népszerűség hiányával szemben.

Oktatás

Az OCaml az a nyelv, amelyet a francia előkészítő osztályok használnak , ahol a Caml Light-ot követte, tekintettel a Grandes Écoles felvételi vizsgák számítógépes opciós tesztjeire. A tanítás keretében felhasznált dokumentumok kiemelik a funkcionális programozás és a matematika közötti kapcsolatokat, valamint azt, hogy az ML család nyelvei milyen könnyen kezelik az algoritmusok tanításához hasznos rekurzív adatszerkezeteket .

Az akadémiákban szenved a Haskell- nyelv versengésétől , amelyet néhány funkcionális programozási tanfolyamon előnyben részesítenek vele, mert többek között nem veszi fel az imperatív programozás fogalmát .

Kutatás

Az OCaml meglehetősen használt nyelv a keresési világban. Történelmileg az ML ág nyelvei mindig szorosan kapcsolódtak a formális bizonyítási rendszerek területéhez ( Robin Milner kezdeti ML-jét tehát az LCF bizonyító rendszerben használták). Az OCaml az egyik legfontosabb szoftver, a Coq proof asszisztens által használt nyelv .

Az OCaml a számítástechnika számos más területén vesz részt, beleértve a programozási nyelvek és a fordítók kutatását (lásd a Származtatott nyelvek részt ), vagy az Unison fájlszinkronizáló szoftvert .

Ipar

A viszonylag félénk kommunikáció ellenére az OCaml szilárd felhasználói bázist épített ki az ipar meghatározott területein. Így a repüléstechnika az OCaml programozási megbízhatóságát és hatékonyságát használja fel komplex algoritmusok megfogalmazásakor. Ezen a területen idézhetjük az ASTRÉE projektet , amelyet többek között az Airbus társaság is használt . A Luster szinkron, valós idejű programozási nyelv-fordítót , amelyet kritikus rendszerekhez, például Airbus repüléstechnikai rendszerekhez vagy egyes atomerőművek vezérléséhez használnak, szintén az OCaml írta.

Az OCaml-t a szoftveripar jelentős szereplői használják, például a Microsoft vagy a XenSource , mindkettő a Caml Consortium tagja. Alkalmazásokat talál a pénzügyi számítástechnikában is, amint azt a sok OCaml programozót foglalkoztató Jane Street cég , vagy a pénzügynek szentelt programnyelvek tervezésére szakosodott francia francia Lexifi cég is emellett nemzetközi díjazásban részesült.

Végül általános, ingyenes projektek is használják, mint például az MLDonkey , a GeneWeb , a Liquidsoap webrádió kliens , az FFTW könyvtár, valamint néhány szoftver a KDE asztali környezethez . Végül a MediaWiki szoftver matematikai képleteit egy OCaml-ben írt program generálja.

Nyelvi bemutató

Bonjour Monde

Vegye figyelembe a következő hello.ml programot:

print_endline "Hello world!"

Összeállítható futtatható bytecode-ba az ocamlc bytecode fordítóval:

$ ocamlc -o hello hello.ml

Optimalizált natív kódba is lefordítható a natív ocamlopt fordító segítségével:

$ ocamlopt -o hello hello.ml

Ezután a programot az ocamlrun bytecode tolmács futtathatja:

$ ./hello Hello world!

vagy

$ ocamlrun hello Hello world!

Változók

Az ocaml interaktív tolmács használható. Elindítja a "#" parancssort, amely után az OCaml utasításokat megadhatja, karakterekkel zárva ;;(ezeket az utasításvégi karaktereket csak az interaktív tolmácsban szabad használni, ezek nem részei a nyelv szintaxisának). Például xa számítás eredményét tartalmazó változó meghatározásához a következőket 1 + 2 * 3írjuk:

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

A kifejezés megadása és érvényesítése után az OCaml meghatározza a kifejezés típusát (ebben az esetben egész szám), és megjeleníti a számítás eredményét:

val x : int = 7

Kísértésbe eshet mindenféle számítás elvégzése. Vigyázzon azonban arra, hogy ne keverje össze az egész számokat és a valós értékeket, ami általában sok nyelven történik, mert az OCaml-ben ezek nem konvertálódnak automatikusan (olyan függvényt kell használnia, amely egész számot vesz fel, és valósat ad vissza, vagy fordított értéket, ha az operátor egész számokat használ). Az alábbi példában a + operátor két egész szám hozzáadását várja, ha azok két valós értékek:

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

Ez az egyszerű példa első ötletet ad a típusú következtetési algoritmus működéséről. Valóban, amikor írtunk 2.3 + 1., hozzáadtuk a valós számokat 2.3és 1.az +egész operátorral , ami problémát jelent. Valójában ennek a számításnak az elvégzéséhez meg kell győződnünk arról, hogy egyrészt minden számnak ugyanaz a típusa van (például lehetetlen hozzáadni, 2.3és 1mert 1egész szám nem, 1.vagy nem 2.3), másrészt a törvényt kell használni +valós számokra alkalmazott belső összetétel , amelyet +.az OCaml. Tehát írnunk kellett volna:

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

Funkciók

A programok gyakran eljárások és funkciók szerint vannak felépítve. Az eljárások a programban többször használt parancsokból állnak, és a kényelem érdekében ugyanazon a néven vannak csoportosítva. Egy eljárás nem ad vissza értéket, ez a szerep a funkciókhoz van rendelve. Sok nyelvnek külön kulcsszava van egy új eljárás vagy egy új funkció bevezetésére ( eljárás és függvény a Pascal-ban , al- és függvény a Visual Basic-ben ...). Az OCaml viszont csak függvényekkel rendelkezik, ezeket ugyanúgy definiálják, mint a változókat. Például az identitás meghatározásához írhatunk:

# let id x = x;;

A kifejezés megadása és érvényesítése után a típusú szintézis algoritmus meghatározza a függvény típusát. Az általunk megadott példában azonban semmi sem jósolja meg a típusát x, így a függvény polimorfként jelenik meg (a halmaz bármely eleméhez 'atársít egy képet, id xamely a halmaz eleme 'a):

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

A függvény meghívásához a következő szintaxist használjuk:

# id 5;; - : int = 5

Az OCaml lehetővé teszi az anonim függvények használatát is, vagyis olyan funkciókat, amelyek nem kapcsolódnak azonosítóhoz, a kulcsszónak functionvagy fun :

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

Az anonim függvények azonnal meghívhatók vagy felhasználhatók egy funkció definiálására:

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

Mintaszűrés

Az OCaml erőteljes jellemzője a minták illesztése . Meghatározható a kulcsszavakkal match withvagy egy névtelen függvénnyel, amelyet minden minta után egy függőleges sáv |, a minta, egy nyíl ->és a visszatérési érték követ :

# 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>

Az aláhúzás _az alapértelmezett mintát jelöli. Lehetséges egyszerre több mintának ugyanazt az értéket adni:

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

A kulcsszó whena minta feltételének kifejezésére szolgál:

# 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>

A karaktereket ..a karakterek tartományát szűrő minta kifejezésére használják:

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

A kulcsszó asa szűrt érték megnevezésére szolgál:

# 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>

Rekurzió

A rekurzió egy önmagára utaló függvény megírása a matematikai indukció mintájára. Az OCaml-ban a rekurzív függvényeket a kulcsszó segítségével vezetik be rec.

Meghatározhatjuk például a faktoriális függvényt  :

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

Meghatározhatjuk a Fibonacci szekvenciát :

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

Belső meghatározás

A kulcsszó segítségével meghatározhatók változók és függvények egy függvényen belül in.

Meghatározhatjuk például a faktoriális függvényt:

# 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>

Meghatározhatjuk a Fibonacci szekvenciát:

# 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>

Terminál rekurzió

Az OCaml fordító optimalizálja a terminálhívásokat: amikor egy funkció kiértékelése során az utolsó végrehajtandó lépés egy (másik) függvény hívása, az OCaml közvetlenül erre az új funkcióra ugrik, anélkül, hogy a hívást memóriában tartaná. most haszontalan.

Különösen az OCaml optimalizálja a terminál rekurzióját . Például a factfenti második függvény (egy kiegészítő paraméterrel a) egy terminálfüggvény, egyenértékű egy hurokkal, és ekvivalens eredményt fog produkálni, így megegyezik a megfelelő imperatív kód teljesítményével.

A terminál rekurziója ugyanolyan hatékony, mint az iteráció; ezért előnyösebb, ha lehetővé teszi, hogy világosabb vagy könnyebben kezelhető programokat írjon.

A listák manipulálása

A listákat a programozásban használják, főleg rekurzív feldolgozásra. A lista elemei mindegyike azonos típusú. A lista összeállításához két írás lehetséges, az egyik a láncolási operátorral ::, a másik pedig az operátorral ; :

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

A ::kifejezés utolsó láncolási operátorának jobb operandusának listának kell lennie:

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

Lehetőség van a listák összefűzésére az összefűzés operátorával @ :

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

A lista hosszának List.lengthaz e célra definiált funkció használata nélkül történő megismeréséhez írhatunk:

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

Ha elemezzük ezt a függvényt a következtetési algoritmus alapján, úgy tűnik, hogy a lista bármilyen típusú adatot tartalmazhat 'a.

A következő függvény két listából hozza létre a párok listáját: a lista hossza megegyezik a paraméterben megadott lista hosszával, amely a legrövidebb.

# 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>

A következő függvény beolvassa a lista első elemét:

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

A következő függvény beolvassa a lista második elemét:

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

A következő függvény beolvassa az első allista első elemét:

# 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>

Magasabb rendű funkciók

A magasabb rendű függvények olyan funkciók, amelyek egy vagy több függvényt vesznek fel a bemenetben és / vagy visszaadnak egy függvényt (ezt funkcionálisnak nevezzük). A legtöbb funkcionális nyelv magasabb rendű funkcióval rendelkezik. Ami OCaml, lehet találni példákat az előre meghatározott modulok funkcióit Array, Liststb Például a következő kifejezés:

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

A függvény mapargumentumként az anonim függvényt veszi fel, amely egészére itársítja a négyzetét, és alkalmazza a lista elemeire, így felépíti a négyzet értékeinek listáját.

Egy másik példa :

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

A függvény doubleegy függvényt fés egy értéket vesz fel paraméterként, iés kétszer érvényes frá 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]

Itt van még egy példa:

# 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>

Végül egy utolsó példa. Itt mi felelünk egy új jegyzett operátor meghatározásáért $. Ez az operátor két funkció együttes elvégzésére szolgál.

# 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);;

Rekurzív fák és típusok

Bármilyen típusú bináris fa definiálásához rekurzív típust használunk. Így a következő íráshoz folyamodhatunk:

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

Ez a fa olyan ágakból áll, amelyek tetszés szerint elágaznak és levelekkel végződnek. A fa magasságának megismeréséhez ezután a következőket használjuk:

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

Gyökérkeresés dichotómiával

# 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)

Memoizálás

Itt van egy példa egy memoizálást használó függvényre . Ez egy olyan függvény, amely kiszámítja a Fibonacci-szekvencia n- edik tagját . A klasszikus rekurzív függvénytől eltérően ez megjegyzi az eredményeket, és a hash-táblának köszönhetően állandó időben elő tudja hozni azokat.

(* 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. *)

Polinom levezetése

Javasoljuk egy egyszerű függvény megvalósítását, amely lehetővé teszi bármilyen fokú polinom levezetését. Először meg kell adnunk azt a típust, amely a polinomunkat képviseli:

# 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

Itt van az a függvény, amely ezt a polinomot levezeti a paraméterben megadott x változóhoz képest.

# 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>

Különböző összeállítási célok

Az OCaml implementációt más szerzők adaptálták a bytecode és a natív kód kivételével. Meg fogjuk találni :

  • OCaml-Java, a JVM disztribúciója, amely az ocamlc, ocamlrun, ocamldep, ocamldoc, ocamllex, menhir, egy ocamljava fordítót tartalmaz a JVM számára;
  • OCamIL, a prototípus backend a .NET környezetre. Tartalmaz egy fordítót az OCaml bytecode-hoz (az ocamlrun által futtatható), egy fordítót a .NET-hez és egy olyan eszközt, mint az ocamlyacc, az ocamilyacc nevet.

Származtatott nyelvek

Sok nyelv kiterjeszti az OCaml-t, hogy funkciókat adjon hozzá.

  • Az F # a .NET platform nyelve, amelyet a Microsoft Research fejlesztett ki, az OCaml alapján (és részben kompatibilis).
  • A MetaOCaml idézési és kódgenerációs mechanizmust ad hozzá a futásidőhöz , amely metaprogramozási funkciókat hoz az OCaml-be.
  • A friss OCaml (az AlphaCaml, az OCaml másik származéka alapján) megkönnyíti a szimbolikus nevek manipulálását.
  • A JoCaml hozzáteszi az OCaml támogatását a Join Calculus számára, amely egyidejű vagy elosztott programokra irányul.
  • Az OcamlP3L a „ csontváz programozásán” alapuló párhuzamosság egy bizonyos formáját hozza .
  • A GCaml ad-hoc polimorfizmust ad az OCaml-hez, lehetővé téve a kezelő számára a túlterhelést vagy a rendezés megőrzését a tipizálási információk megőrzésében.
  • Az OCamlDuce lehetővé teszi, hogy a típusrendszer XML-értékeket képviseljen, vagy a reguláris kifejezésekhez kapcsolódjon. Ez egy közvetítő az OCaml és a CDuce nyelv között, az XML manipulálására szakosodott.
  • Az Opa az OCaml-ben megvalósított webalkalmazások és -szolgáltatások fejlesztésének nyelve, amelynek magja magában foglalja az OCaml nyelv funkcióit. Ezenkívül az Opa fordító az OCaml fordító háttérprogramját használja natív szerverek létrehozására.
  • A Facebook által kifejlesztett ReasonML az OCaml fordítót használó nyelv, amely lehetővé teszi a Reason (.re) és az OCaml (.ml) kód fordítását JavaScript forráskódba (.js), amely viszont bytecode-ba állítható össze, amelyet a webböngészővel vagy egy olyan tolmácson keresztül, mint a Node.js.

Megjegyzések és hivatkozások

  1. „  Messenger.com most 50% -ban átalakítva okra · okra  ” , a reasonml.github.io címen (elérhető : 2018. február 27. )
  2. Az OCaml néhány sikere: SLAM
  3. "A funkcionális programok olyan programok, amelyek általában sokat allokálnak, és azt látjuk, hogy sok értéknek nagyon rövid az élettartama." Másrészt, amint egy érték több GC-t is túlélt, jó esélye van egy jó ideig fennállni. ”- Alkalmazásfejlesztés az Objective Caml alkalmazással
  4. A hatékony gépi kód létrehozása mindig is fontos szempont volt az OCaml-nál, és az OCaml fejlesztés elején (95-97) elég sok munkát fordítottam erre. Manapság nagyrészt elégedettek vagyunk a generált kód teljesítményével.  »- Xavier Leroy, a caml levelezőlistán .
  5. A típusrendszer által nyújtott garanciák a program hatékony optimalizálását is lehetővé teszik.  »- Xavier Leroy, Bevezetés a típusokba az összeállításban .
  6. (en) levelezőlista szál  : "  Az általános teljesítmény-nyilatkozatunk" Az OCaml egy tisztességes C fordító teljesítményének legalább 50% -át biztosítja "nem érvénytelen  :-)"
  7. (en) lövöldözés: OCaml vs. C referenciaérték .
  8. teljesítmény összehasonlítása az ocamlopt és a C # Mono között .
  9. "  2017. november 27-i ESRS1732186N memorandum  "
  10. Oktató programozás Caml
  11. Citeseer: A Caml-t használó kutatási cikkek listája
  12. Az ASTRÉE projekt helyszíne , konzultál a www.astree.ens.fr címen 2011. január 15
  13. „absztrakt értelmezése: kérelmet a szoftver az A380” , Patrick Cousot, konzultálni www.di.ens.fr le 2011. január 15
  14. Caml Konzorcium
  15. Az FFTW könyvtár, amely gyors Fourier-transzformációt hajt végre , C nyelvű kódból áll . Teljesítményi okokból azonban a C kódot egy OCaml-ben írt fordító, genfft generálja és optimalizálja automatikusan . A rutinok létrehozásának és szakosodásának folyamatát Matteo Frigo (MIT) [ A gyors Fourier-transzformátor-fordító] cikkben [ online olvasható  (2007. december 9-én megtekintett oldal)] ismertetjük . Az FFTW dokumentációjában értékelést találunk az OCaml használatával kapcsolatban: „  A kódgenerátorok genfft csomagját az Objective Caml, az ML dialektusának felhasználásával írták. Az Objective Caml egy kicsi és elegáns nyelv, amelyet Xavier Leroy fejlesztett ki. A megvalósítás a http://caml.inria.fr/ webhelyről érhető el . Az FFTW korábbi kiadásaiban a genfft Caml Light-ban írták, ugyanazok a szerzők. A genfft még korábbi megvalósítását írták a Scheme-ben , de a Caml mindenképpen jobb az ilyen típusú alkalmazásokhoz.  "- FFTW köszönetnyilvánítás
  16. A Kalzium szoftver EqChem komponens oldala
  17. Egy program segítségével terminál hívások gyakran olvashatóbb, mint egy hasonló iteráció, ha a folytatásban minta bonyolult. Például leírhatunk egy automatát, mint átmeneti függvények halmazát, amely terminálhívásokat hajt végre (vagy gotokötelező nyelveken ugrik ) az automata többi állapotába. Ebben az esetben a terminál kéri, hogy nagyobb rugalmasságot, ahogy a cikkben (in) Géppisztolyok Via makrók .

Lásd is

Kapcsolódó cikkek

Külső linkek