OCaml | |
---|---|
Sémantika | multiparadigma : funkční , objektově orientované , imperativní |
Jazyková třída | objektově orientovaný programovací jazyk , funkcionální programovací jazyk , multiparadigmatický programovací jazyk , imperativní programovací jazyk , programovací jazyk a svobodný a open source software |
Objevil se v | 1996 |
Autor | Leroy, Xavier a Damien Doligez [d] |
Vývojář | INRIA |
Přípona souboru | .ml, .mli |
Uvolnění | 4.14.0 ( 28. března 2022 ) |
Typový systém | přísný , statický |
Dialekty | F# , JoCaml , MetaOCaml, OcamlP3l |
Byl ovlivněn | Standardní ML , Caml Light |
Licence | LGPL |
webová stránka | ocaml.org |
OS | Unixový operační systém [1] |
Mediální soubory na Wikimedia Commons |
OCaml ( Objective Caml ) je obecný objektově orientovaný funkcionální programovací jazyk . Byl navržen s ohledem na bezpečnost provádění a spolehlivost programů. Podporuje funkční, imperativní a objektově orientovaná programovací paradigmata. Nejběžnější dialekt jazyka ML v praktické práci .
Objevil se v roce 1996 pod názvem Objective Caml, kdy Didier Rémy (Didier Rémy) a Jérôme Vouillon (Jérôme Vouillon) implementovali podporu pro objektově orientované programování pro jazyk Caml, původně vyvinutý ve Francouzském institutu INRIA . Oficiálně přejmenován na OCaml v roce 2011 [2] .
Sada nástrojů OCaml obsahuje tlumočník , kompilátor bajtového kódu a optimalizační kompilátor nativního kódu, jehož účinnost je srovnatelná s Javou a má jen mírně nižší výkon než C a C++ [3] .
Konkrétně vykreslování vzorců Wikipedie pomocí tagu <math>, klienta pro výměnu souborů MLDonkey , řídicího zásobníku Xen xapi hypervisor (součást platformy Xen Server / Xen Cloud Platform) a programovacího jazyka Haxe jsou napsány v OCaml. jazyk .
Jazyk OCaml je univerzální programovací jazyk, ale má své vlastní zavedené oblasti použití [4] .
Za prvé je to tvorba „bezpečných“ (nejen ve smyslu informační bezpečnosti) aplikací. Jazyk používá garbage collection a většina datových typů je referenčních ( anglicky boxed ), což znamená zamezení přetečení vyrovnávací paměti během provádění programu. Kromě toho statické typování a kontroly během kompilace znemožňují některé další třídy chyb, jako jsou chyby přetypování , kvůli nedostatku automatického přetypování. Kromě toho lze kód formálně ověřit . Existují nástroje pro automatické prokázání typové správnosti kódu, lepší než u většiny programovacích jazyků. A co je důležité, bezpečnostní opatření nemají vliv na efektivitu spustitelného kódu [4] .
Další oblastí úspěchu s OCaml jsou aplikace řízené daty . Tato oblast zahrnuje zpracování textu a také překladače psaní. OCaml má nejen nástroje pro zpracování textu (kterými jsou známé například Perl nebo AWK ), ale také nástroje pro hlubokou sémantickou analýzu a transformaci textu, díky čemuž je OCaml použitelný v úlohách dolování dat [ 4 ] .
Samozřejmě, OCaml, stejně jako ostatní dialekty ML, se používá ve výzkumných a ověřovacích úkolech, ve kterých je hlavní kód napsán v nějakém programovacím jazyce a poté formálně ověřen a analyzován programem OCaml [4] . Například interaktivní systém dokazování vět Coq je napsán v OCaml .
OCaml zaujímá zvláštní místo mezi programovacími jazyky díky kombinaci efektivity, expresivity a praktičnosti. Mezi rysy jazyka, které se vyvíjely více než 40 let od vytvoření ML , jsou [5] :
OCaml má svůj původ v ML ( angl. meta language ), který do dialektu Lisp implementoval Robin Milner v roce 1972 jako softwarový nástroj pro dokazování teorémů, jako metajazyk pro logiku vyčíslitelných funkcí (LCF, angl. logic for computable funkce ). Později byl vytvořen kompilátor a do roku 1980 se ML stal plnohodnotným programovacím systémem [6] .
Guy Cousineau přidal do jazyka algebraické datové typy a porovnávání vzorů a definoval ML jako kategorický abstraktní stroj (CAM). CAM-ML tak bylo možné popsat, ověřit a optimalizovat, což byl pro ML krok vpřed [7] .
Dalším vývojem byl jazyk Caml (přehraný CAM-ML) [6] [7] vytvořený v roce 1987 Ascánderem Suárezem a pokračující Pierrem Weisem a Michelem Maunym .
V roce 1990 vydali Xavier Leroy a Damien Doligez novou implementaci nazvanou Caml Light . Tato implementace v jazyce C používala interpret bajtového kódu a rychlý sběrač odpadu. Se psaním knihoven se jazyk začal používat ve vzdělávacích a výzkumných ústavech [6] [7] .
Caml Special Light , vyvinuté C. Leroyem , spatřilo světlo v roce 1995 . Programovací systém obdržel kompilátor do strojových kódů, který dal efektivitu spustitelného kódu na úroveň jiných kompilovaných jazyků. Současně byl vyvinut modulový systém , jehož myšlenka byla vypůjčena ze Standard ML [6] .
Moderní podoba OCaml sahá až do roku 1996 , kdy Didier Rémy a Jérôme Vouillon implementovali úhlednou a efektivní podporu objektů pro jazyk . Tento objektový systém vám umožňuje používat objektově orientované programovací idiomy v době kompilace typově bezpečným způsobem , bez inherentních C++ a Java run- time kontrol [6] .
V roce 2000 se jazyk vyvíjel hladce a získal větší uznání v komerčních projektech a vzdělávání. Mezi vyvinuté v současné době patří polymorfní metody a varianty typů, pojmenované a volitelné parametry, moduly první třídy , zobecněné algebraické datové typy (GADT). Jazyk začal podporovat několik hardwarových platforem ( X86 , ARM , SPARC , PowerPC ) [6] [7] .
Výpočtový model OCaml jako funkcionálního programovacího jazyka je založen na třech hlavních konstrukcích lambda kalkulu : proměnné , definice funkcí a aplikace funkce na argumenty [8] .
Proměnná je identifikátor, jehož hodnota je spojena s určitou hodnotou. Názvy proměnných začínají malým písmenem nebo podtržítkem. Vazba se obvykle provádí pomocí klíčového slova let, jako v následujícím příkladu v interaktivním shellu [9] :
nechť v = 1 ;Proměnné mají rozsah . Například v interaktivním shellu lze proměnnou použít v příkazech následujících po jejím navázání. Podobně proměnná definovaná v modulu může být použita poté, co byla definována v tomto modulu [9] .
Variabilní vazbu lze také provést v rozsahu specifikovaném konstruktem let-in, jako v následujícím příkladu pro výpočet plochy kruhu z poloměru:
# nech rádius plochy = nech pi = 3 . 14 v poloměru *. poloměr *. pí ; val area : float -> float = < fun > # area 2 . 0 ; - : plovoucí = 12 . 56V OCaml jsou vazby proměnných neměnné (jako v matematických rovnicích), to znamená, že hodnota proměnné je „přiřazena“ pouze jednou (jednotné přiřazení). Jiná věc je, že uvnitř vpuštění může být další vpuštění, ve kterém je zavedena další proměnná, která může „zastínit“ tu první [9] .
Existuje několik syntaktických konstrukcí pro definování funkcí v OCaml.
Funkce lze definovat pomocí function. Výraz pro funkci vypadá takto [10] :
funkce x -> x + 1V tomto případě je funkce anonymní a lze ji použít jako parametry pro jiné funkce nebo použít na nějaký argument, například:
( funkce x -> x + 1 ) 5Typ této funkce je int -> int, to znamená, že funkce vezme celé číslo a vrátí celé číslo.
Funkce může mít více argumentů [11] :
funkce ( x , y ) -> x - yV tomto příkladu je její typ: int * int -> int, to znamená, že vstupem funkce je pár a výstupem je celé číslo.
Existuje další přístup k reprezentaci funkcí několika argumentů - převod N-ární funkce na N funkcí jednoho argumentu - currying . Následující dva zápisy pro funkci, která počítá součin celočíselných argumentů, jsou ekvivalentní [11] :
funkce x -> funkce y -> x * y fun x y -> x * yPojmenované funkce lze získat přidružením proměnné k funkci [10] . Definice pojmenované funkce je tak běžnou operací, že má samostatnou syntaktickou podporu. Následující tři položky jsou ekvivalentní způsoby, jak definovat funkci (v interaktivním prostředí):
# let prod = funkce x -> funkce y -> x * y ;; val prod : int -> int -> int = < fun > # let prod x y = x * y ;; val prod : int -> int -> int = < fun > # let prod = fun x y -> x * y ;; val prod : int -> int -> int = < zábava >Funkce dvou argumentů lze definovat pro použití infixové notace [10] :
# let (^^) x y = x ** 2 . 0+ . y ** 2 . 0 ; val ( ^^ ) : float -> float -> float = < zábava > # 2 . 0 ^^ 3 . 0 ; - : plovoucí = 13 . # (^^) 2 . 0 3 . 0 ; - : plovoucí = 13 .Tento příklad definuje funkci (^^), která vypočítá součet druhých mocnin dvou čísel s plovoucí desetinnou čárkou . Poslední dva typy zápisu jsou ekvivalentní.
Rekurzivní funkce , tedy funkce, které odkazují na svou vlastní definici, lze zadat pomocí let rec[10] :
# let rec fac n = shodu n s | 0 -> 1 | x -> x * fac ( x - 1 ) ;;Ve stejném příkladu výpočtu faktoriálu je použito porovnávání vzorů (construct match-with).
Argumenty funkce lze definovat jako pojmenované. Pojmenované argumenty lze zadat v libovolném pořadí [10] :
# let divmod ~ x ~ y = ( x / y , x mod y ) ;; val divmod : x : int -> y : int -> int * int = < fun > # divmod ~ x : 4 ~ y : 3 ;; - : int * int = ( 1 , 1 ) # divmod ~ y : 3 ~ x : 4 ;; - : int * int = ( 1 , 1 )V OCaml můžete vynechat hodnoty pomocí štítků , pokud jsou název parametru a název proměnné stejné [ 10] :
# let x = 4 v let y = 3 v divmod ~ x ~ y ;; - : int * int = ( 1 , 1 )
Asociativita operací ve výrazech OCaml je určena prefixem, čímž se rozšiřuje na uživatelem definované operace. Znak -funguje jako prefix i jako operace infix, a pokud je to nutné, pro použití jako prefix společně s funkcí musí být parametr uzavřen v závorce [12] .
Předpona operace | Asociativnost |
---|---|
! ? ~ | Předpona |
. .( .[ .{ | |
použití funkce, konstruktoru, štítku assert,lazy | Vlevo, odjet |
- -. | Předpona |
** lsl lsr asr | Že jo |
* / % mod land lor lxor | Vlevo, odjet |
+ - | Vlevo, odjet |
:: | Že jo |
@ ^ | Že jo |
& $ != | Vlevo, odjet |
& && | Že jo |
or || | Že jo |
, | |
<- := | Že jo |
if | |
; | Že jo |
let match fun function try |
Jazyk OCaml má několik primitivních typů : číselné typy ( celé číslo a s plovoucí desetinnou čárkou), znak , znakové řetězce , boolean [13] .
Typ integer představuje celá čísla z rozsahu [−2 30 , 2 30 − 1] a [−2 62 , 2 62 − 1] pro 32bitové a 64bitové architektury. S celými čísly můžete provádět obvyklé operace sčítání, odčítání, násobení, dělení a zbytek dělení :+,-,*,/,mod. Pokud výsledek překročí povolený interval, nedojde k žádné chybě a výsledek se vypočítá modulo the interval boundary [14] .
Čísla s plovoucí desetinnou čárkou jsou reprezentována 53bitovou mantisou a exponentem v intervalu [−1022, 1023] podle standardu IEEE 754 pro dvojky. V operacích nelze tato čísla míchat s celými čísly. Operace s čísly s pohyblivou řádovou čárkou se navíc syntakticky liší od operací s celými čísly:+.,-.,*.,/.. Existuje také operace umocňování:**. Pro převod celých čísel na čísla s pohyblivou řádovou čárkou a naopak jsou k dispozici následující funkce: float_of_int a int_of_float [14] .
Pro čísla s plovoucí desetinnou čárkou existují další matematické funkce: trigonometrické (sin, cos, tan, asin, acos, atan), zaokrouhlování (strop, podlaha), exponenciální (exp), logaritmické (log, log10), stejně jako převzetí odmocnina (sqrt) [14] . Existují také polymorfní porovnávací operace pro číselné typy [14] .
Typ znaku - char - odpovídá reprezentaci znaku s kódem od 0 do 255 (prvních 128 znaků je stejných jako ASCII ). Typ řetězce - řetězec - sekvence znaků (maximální délka: 2 24 - 6) [15] . Příklad použití funkce převodu celého čísla na řetězec a operace zřetězení :
# "Příklad" ^ string_of_int ( 2 ) ;; - : string = "Příklad 2"Booleovský typ má dvě hodnoty:true(true) afalse(false). Operace s booleovskými hodnotami: unární not (negace), binární:&&(and),||(nebo). Binární operace nejprve vyhodnocují levý argument a pravý argument pouze v případě potřeby [16] .
Booleovské hodnoty jsou získány jako výsledek srovnání: =(strukturální rovnost), ==(identita), <>(popírání strukturální rovnosti), !=(popírání identity), <, >, <=, >=. U primitivních typů, s výjimkou řetězců a čísel s plovoucí desetinnou čárkou, se strukturální rovnost a identita shodují, u ostatních typů jsou hodnoty umístěné v paměti na stejné adrese považovány za identické a ve strukturálním srovnání jsou hodnoty kontrolovány komponent po komponentě. [16] .
OCaml má navíc jednotku speciálního typu, která má pouze jednu hodnotu - ()[16] .
SeznamyV OCaml je seznam konečná, neměnná sekvence prvků stejného typu, implementovaná jako jednoduše propojený seznam. Následující příklad ukazuje syntaxi seznamu [17] :
# [ 'a' ; 'b' ; 'c' ] ;; - : seznam znaků = [ 'a' ; 'b' ; 'c' ] # 'a' :: ( 'b' :: ( 'c' :: [] )) ;; - : seznam znaků = [ 'a' ; 'b' ; 'c' ] # 'a' :: 'b' :: 'c' :: [] ;; - : seznam znaků = [ 'a' ; 'b' ; 'c' ] # [] ;; - : ' seznam = [ ]Operace ::vám umožňuje sestavit seznam na základě nového prvku a konce starého seznamu. V tomto případě se "starý" seznam nezmění:
# let lst = [ 1 ; 2 ] ; val lst : int seznam = [ 1 ; 2 ] # let lst1 = 0 :: lst ;; val lst1 : int seznam = [ 0 ; 1 ; 2 ] # lst ;; - : int seznam = [ 1 ; 2 ] # lst1 ;; - : int seznam = [ 0 ; 1 ; 2 ] Příklad: výpočet součtu prvků seznamuSeznam je jedním z hlavních datových typů v OCaml. Následující příklad kódu definuje rekurzivní funkci (všimněte si klíčového slova rec), která iteruje prvky daného seznamu a vrací jejich součet:
nechť rec sum xs = odpovídá xs s | [] -> 0 | x :: xs' -> x + součet xs' #součet[1;2;3;4;5];; - : int = 15Dalším způsobem, jak vypočítat součet, je použít funkci rollup:
nechť součet xs = Seznam . fold_left (+ ) 0xs # součet [ 1 ; 2 ; 3 ; 4 ; 5 ]; - : int = 15 ZáznamyZáznamy jsou důležitým prvkem v systému typu OCaml. Záznam je sada hodnot uložených společně, kde každý prvek hodnotového záznamu je přístupný svým názvem, názvem pole záznamu. Příklad deklarace typu, vazba záznamu na proměnnou a přístup k poli záznamu [18] :
# type user = { login : string ; heslo : string_ _ přezdívka : řetězec _ };; # let usr = { login = "myuser" ; heslo = "tajné" ; nick = "aka" ; } ;; val usr : user = { login = "myuser" ; heslo = "tajné" ; nick = "aka" } # usr . Nick ;; - : string = "aka"Je třeba poznamenat, že typ proměnné usr nastavil automaticky kompilátor.
Stejně jako u jiných typů lze typ parametrizovat. Další možnosti nahrávání [18] :
Typ varianty představuje data, která mohou mít různé formy definované explicitními štítky. Následující příklad definuje typ pro základní barvy [19] :
# typ main_color = Červená | zelená | modrá ;; # modrá ;; - : main_color = Modrá # ( Červená , Modrá ) ;; - : main_color * main_color = ( červená , modrá )Ve výše uvedeném příkladu je typ varianty použit jako výčtový typ . V OCaml je ale typ varianty bohatší, protože kromě popisků umožňuje specifikovat i data, například:
# typ color_scheme = RGB int * int * int | _ CMYK float * float * float * float ;; _ typ color_scheme = RGB of int * int * int | CMYK float * float * float * float _Při definování funkcí se typ varianty přirozeně spáruje s párováním se vzorem.
ObjektyV OCaml jsou objekty a jejich typy zcela odděleny od systému tříd . Třídy se používají ke konstrukci objektů a podporují dědičnost , ale nejsou typy objektů. Objekty mají své vlastní typy objektů a pro práci s objekty nemusíte používat třídy. Objekty se v OCaml nepoužívají tak často (například systém modulů je výraznější než objekty, protože moduly mohou obsahovat typy, ale třídy a objekty nikoli). Hlavní výhodou objektů oproti záznamům je to, že nevyžadují deklarace typu a jsou flexibilnější díky polymorfismu řádků . Na druhou stranu výhody objektů vstupují do hry při použití systému tříd. Na rozdíl od modulů třídy podporují pozdní vazbu, která umožňuje odkazovat na objektové metody bez staticky definované implementace a používat otevřenou rekurzi (v případě modulů lze použít funkce a funktory, ale syntakticky takové popisy vyžadují psaní více kódu) [20 ] .
Typový závěrPřestože je OCaml silně typovaný programovací jazyk , systém odvození typu ( anglicky type inference ) vám umožňuje určit typ výrazu na základě dostupných informací o jeho komponentách. V následujícím příkladu paritní funkce není specifikována žádná deklarace typu, a přesto má jazykový kompilátor kompletní informace o typu funkce [21] :
# let liché x = x mod 2 <> 0 ;; val odd : int -> bool = < zábava >Kromě funkčních obsahuje jazyk imperativní programovací nástroje : funkce s vedlejšími efekty , proměnlivá data, imperativní syntaktické konstrukce, zejména explicitní smyčky while , a for[22] .
Následující příklad vytiskne 11 řádků na standardní výstup (toto je vedlejší efekt funkce printf):
pro i = 0 až 10 do Printf . printf "i =%d \ n " hotovo ;;V následujícím (spíše umělém) příkladu jsou prvky pole inkrementovány na místě ve smyčce předpokladů. Pro index pole se používá odkaz (ref), který se zvyšuje v těle smyčky:
# let incr_ar ar = let i = ref 0 in while ! i < pole . délka ar do ar .(! i ) <- ar .(! i ) + 1 ; včetně hotovo ;; _ val incr_ar : int pole -> unit = < fun > # let nums = [| 1 ; 2 ; 3 ; 4 ; 5 |];; val nums : int pole = [| 1 ; 2 ; 3 ; 4 ; 5 |] # incr_ar nums ;; - : jednotka = () # nums ;; - : pole int = [| 2 ; 3 ; 4 ; 5 ; 6 |]Vedlejší efekty umožňují optimalizovat výpočty, zejména pokud jde o významné transformace na velkých souborech dat. Používají se také k implementaci líného hodnocení a zapamatování [22] .
OCaml lze považovat za sestávající ze dvou jazyků: základní jazyk s hodnotami a typy a jazyk modulů a jejich podpisů . Tyto jazyky tvoří dvě vrstvy v tom smyslu, že moduly mohou obsahovat typy a hodnoty, zatímco běžné hodnoty nemohou obsahovat moduly a moduly typů. OCaml však nabízí mechanismus pro prvotřídní moduly , které mohou být hodnotami a podle potřeby převádět do a z normálních modulů [23] .
Systém modulů OCaml není omezen na modulární organizaci kódu a rozhraní. Jedním z důležitých nástrojů generického programování jsou funktory . Jednoduše řečeno, funktory jsou funkce od modulu k modulům, což umožňuje implementovat následující mechanismy [24] :
Chcete-li spustit překladač jazyka OCaml, zadejte do konzoly následující příkaz:
$ ocaml OCaml verze 4.08.1 #Výpočty lze provádět interaktivně, například:
# 1 + 2 * 3 ; - : int = 7Následující program "hello.ml":
print_endline "Ahoj světe!" ;;lze zkompilovat buď do bytecode :
$ ocamlc ahoj.ml -o ahojnebo do optimalizovaného strojového kódu :
$ ocamlopt hello.ml -o helloa spuštěno:
$ ./ahoj Ahoj světe! $Následující příklad je algoritmus rychlého řazení, který seřadí seznam ve vzestupném pořadí:
nechť rec qsort = funkce | [] -> [] | pivot :: rest -> let is_less x = x < pivot in let left , right = List . oddíl je_less zbytek v qsort vlevo @ [ pivot ] @ qsort vpravoPoznámka - Kniha používá překlad termínu " first-class function " jako " first-order function ". Je však třeba mít na paměti, že v mnoha zdrojích v anglickém jazyce (o sémantice jazyků obecně a konkrétně o ML a Hindley-Milnerovi ) se koncepčně rozlišují čtyři pojmy:
navíc „ první třída “ je „ lepší “ než „ druhá třída “ (širší ve schopnostech, blíže teorii a vyšší z hlediska vstupního prahu ( C. Strachey — Fundamental Concepts in Programming Languages )), ale „ prvního řádu “ "primitivnější než " vysokého řádu ". Zejména rozšíření jazyka modulu ML na úroveň „ první třídy vysokého řádu “ představuje pro výzkumníky mnohem větší problém než jeho rozšíření pouze na „ první třídu “ nebo pouze na „ vysoký řád “ ( Rossberg A. Functors and runtime vs kompilace (downlink) Získáno 25. června 2015. Archivováno z originálu 26. června 2015 ).
Programovací jazyky | |
---|---|
|