OKaml | |
---|---|
Semantik | multiparadigme : funktionel , objektorienteret , imperativ |
Sprog klasse | objektorienteret programmeringssprog , funktionelt programmeringssprog , multi-paradigme programmeringssprog , imperativt programmeringssprog , programmeringssprog og gratis og open source software |
Dukkede op i | 1996 |
Forfatter | Leroy, Xavier og Damien Doligez [d] |
Udvikler | INRIA |
Filtypenavn _ | .ml, .mli |
Frigøre | 4.14.0 ( 28. marts 2022 ) |
Type system | streng , statisk |
Dialekter | F# , JoCaml , MetaOCaml, OcamlP3l |
Blev påvirket | Standard ML , Caml Light |
Licens | LGPL |
Internet side | ocaml.org |
OS | Unix-lignende operativsystem [1] |
Mediefiler på Wikimedia Commons |
OCaml ( Objective Caml ) er et objektorienteret funktionelt programmeringssprog til generelle formål . Det blev designet med sikkerheden ved udførelse og pålidelighed af programmer i tankerne. Understøtter funktionelle, imperative og objektorienterede programmeringsparadigmer. Den mest almindelige dialekt af ML -sproget i praktisk arbejde .
Dukkede op i 1996 under navnet Objective Caml, da Didier Rémy (Didier Rémy) og Jérôme Vouillon (Jérôme Vouillon) implementerede støtte til objektorienteret programmering til Caml-sproget, oprindeligt udviklet ved det franske institut INRIA . Officielt omdøbt til OKaml i 2011 [2] .
OCaml-værktøjssættet inkluderer en fortolker , en compiler til bytekode og en optimeringskompiler til native kode, sammenlignelig i effektivitet med Java og kun lidt ringere i ydeevne til C og C++ [3] .
Især er gengivelsen af Wikipedia - formler ved hjælp af <math>-tagget, MLDonkey-filudvekslingsklienten , Xen xapi hypervisor-kontrolstakken (en del af Xen Server/Xen Cloud Platform) og Haxe -programmeringssproget skrevet i OCaml sprog .
OCaml-sproget er et alment programmeringssprog, men det har sine egne etablerede anvendelsesområder [4] .
For det første er det skabelsen af "sikre" (ikke kun i betydningen informationssikkerhed) applikationer. Sproget bruger skraldopsamling, og de fleste af datatyperne er reference ( engelsk boxed ), hvilket betyder at forhindre bufferoverløb under programafvikling. Derudover gør statisk indtastning og kontrol af kompileringstid visse andre fejlklasser, såsom cast -fejl, umulige på grund af manglen på automatisk type-casting. Derudover kan koden formelt verificeres . Der er hjælpeprogrammer til automatisk at bevise kodens typekorrekthed, som er bedre end dem for de fleste programmeringssprog. Og vigtigere er det, at sikkerhedsforanstaltninger ikke påvirker effektiviteten af den eksekverbare kode [4] .
Et andet område med succes med OCaml er datadrevne applikationer . Dette område omfatter tekstbehandling samt skrivekompilatorer. OCaml har ikke kun værktøjer til tekstbehandling (som f.eks. Perl eller AWK er kendt for ), men også værktøjer til dyb semantisk analyse og teksttransformation, som gør OCaml anvendelig i data mining opgaver [ 4 ] .
Selvfølgelig bruges OCaml, ligesom andre dialekter af ML, i forsknings- og verifikationsopgaver, hvor hovedkoden er skrevet i et eller andet programmeringssprog, og derefter formelt verificeret og analyseret af et OCaml-program [4] . For eksempel er det interaktive sætningsbevissystem Coq skrevet i OCaml .
OCaml har en særlig plads blandt programmeringssprog på grund af dens kombination af effektivitet, udtryksfuldhed og praktisk. Blandt kendetegnene ved sproget, der har udviklet sig over mere end 40 år siden oprettelsen af ML , er [5] :
OCaml har sin oprindelse i ML ( eng. meta language ), som blev implementeret i Lisp- dialekten af Robin Milner i 1972 som et softwareværktøj til at bevise sætninger, som et metasprog for logikken i beregnelige funktioner (LCF, eng. logic for computable ). funktioner ). Senere blev der lavet en compiler , og i 1980 var ML blevet et fuldgyldigt programmeringssystem [6] .
Guy Cousineau tilføjede algebraiske datatyper og mønstertilpasning til sproget og definerede ML som en kategorisk abstrakt maskine (CAM). Således kunne CAM-ML beskrives, verificeres og optimeres, hvilket var et skridt fremad for ML [7] .
En yderligere udvikling var Caml -sproget (afspillet af CAM-ML) [6] [7] skabt af 1987 af Ascánder Suárez og videreført af Pierre Weis og Michel Mauny .
I 1990 udgav Xavier Leroy og Damien Doligez en ny implementering kaldet Caml Light . Denne C -implementering brugte en bytekodefortolker og en hurtig skraldopsamler . Med skrivningen af biblioteker begyndte sproget at blive brugt i uddannelses- og forskningsinstitutioner [6] [7] .
Caml Special Light , udviklet af C. Leroy , så lyset i 1995 . Programmeringssystemet modtog en compiler til maskinkoder, som satte effektiviteten af den eksekverbare kode på niveau med andre kompilerede sprog. Samtidig blev et modulsystem udviklet , hvis idé var lånt fra Standard ML [6] .
OCamls moderne form går tilbage til 1996 , hvor Didier Rémy og Jérôme Vouillon implementerede pæn og effektiv objektstøtte til sproget . Dette objektsystem giver dig mulighed for at bruge objektorienterede programmeringssprog på kompileringstidspunktet på en typesikker måde , uden de iboende C++ og Java run- time checks [6] .
I 2000'erne har sproget udviklet sig gnidningsløst, samtidig med at det har fået mere anerkendelse i kommercielle projekter og uddannelse. Blandt de udviklede på dette tidspunkt er polymorfe metoder og varianttyper, navngivne og valgfrie parametre, førsteklasses moduler , generaliserede algebraiske datatyper (GADT). Sproget begyndte at understøtte flere hardwareplatforme ( X86 , ARM , SPARC , PowerPC ) [6] [7] .
Beregningsmodellen for OCaml som et funktionelt programmeringssprog er baseret på tre hovedkonstruktioner af lambda-regningen : variabler , funktionsdefinitioner og anvendelse af en funktion på argumenter [8] .
En variabel er en identifikator, hvis værdi er forbundet med en bestemt værdi. Variablenavne starter med et lille bogstav eller understregning. Indbinding udføres normalt med nøgleordet let, som i følgende eksempel i en interaktiv skal [9] :
lad v = 1 ;;Variabler har et omfang . For eksempel, i en interaktiv shell, kan en variabel bruges i kommandoer efter dens binding. På samme måde kan en variabel defineret i et modul bruges efter at være blevet defineret i det modul [9] .
Variabel binding kan også udføres i det omfang, der er specificeret af den indlæste konstruktion, som i følgende eksempel til beregning af arealet af en cirkel fra en radius:
# lad arealradius = lad pi = 3 . _ 14 i radius *. radius *. pi ;; val area : float -> float = < sjov > # område 2 . 0 ;; - : flyder = 12 . 56I OCaml er variable bindinger uforanderlige (som i matematiske ligninger), det vil sige, at værdien af en variabel kun "tildeles" én gang (enkelt tildeling). En anden ting er, at der inde i let-in kan være endnu en let-in, hvori der indføres en anden variabel, som kan "skygge" den første [9] .
Der er flere syntakskonstruktioner til at definere funktioner i OCaml.
Funktioner kan defineres ved hjælp af function. Udtrykket for funktionen ser således ud [10] :
funktion x -> x + 1I dette tilfælde er funktionen anonym , og den kan bruges som parametre til andre funktioner eller anvendes på et eller andet argument, for eksempel:
( funktion x -> x + 1 ) 5Typen af denne funktion er int -> int, det vil sige, at funktionen tager et heltal og returnerer et heltal.
En funktion kan have flere argumenter [11] :
funktion ( x , y ) -> x - yI dette eksempel er dens type: int * int -> int, dvs. funktionens input er et par , og outputtet er et heltal.
Der er en anden tilgang til at repræsentere funktioner af flere argumenter - konvertering af en N-ær funktion til N funktioner af et argument - currying . De følgende to notationer for en funktion, der beregner produktet af heltalsargumenter, er ækvivalente [11] :
funktion x -> funktion y -> x * y sjov x y -> x * yNavngivne funktioner kan opnås ved at associere en variabel med en funktion [10] . Definitionen af en navngivet funktion er så almindelig en operation, at den har separat syntaktisk understøttelse. Følgende tre poster er tilsvarende måder at definere en funktion på (i en interaktiv skal):
# lad prod = funktion x -> funktion y -> x * y ;; val prod : int -> int -> int = < sjov > # lad prod x y = x * y ;; val prod : int -> int -> int = < sjov > # lad prod = sjov x y -> x * y ;; val prod : int -> int -> int = < sjov >Funktioner af to argumenter kan defineres for at bruge infix-notationen [10] :
# lad (^^) x y = x ** 2 . 0+ . y ** 2 . 0 ;; val ( ^^ ) : float -> float -> float = < sjov > # 2 . 0 ^^ 3 . 0 ;; - : flyder = 13 . # (^^) 2 . 0 3 . 0 ;; - : flyder = 13 .Dette eksempel definerer en funktion (^^), der beregner summen af kvadraterne af to flydende kommatal . De sidste to typer notation er ækvivalente.
Rekursive funktioner , det vil sige funktioner, der refererer til deres egen definition, kan specificeres ved hjælp af let rec[10] :
# lad rec fac n = matche n med | 0 -> 1 | x -> x * fac ( x - 1 ) ;;I det samme faktorberegningseksempel anvendes mønstertilpasning (construct match-with).
Funktionsargumenter kan defineres som navngivne. Navngivne argumenter kan angives i enhver rækkefølge [10] :
# lad divmod ~ x ~ y = ( x / y , x mod y ) ;; val divmod : x : int -> y : int -> int * int = < sjov > # divmod ~ x : 4 ~ y : 3 ;; - : int * int = ( 1 , 1 ) # divmod ~ y : 3 ~ x : 4 ;; - : int * int = ( 1 , 1 )I OCaml kan du udelade værdier ved at bruge labelpunning , hvis parameternavnet og variabelnavnet er det samme [ 10] :
# lad x = 4 i lad y = 3 i divmod ~ x ~ y ;; - : int * int = ( 1 , 1 )
Associativiteten af operationer i OCaml-udtryk bestemmes af præfikset og udvides således til brugerdefinerede operationer. Tegnet -fungerer både som præfiks og som infix-operation, og for at bruge som præfiks sammen med funktionen skal parameteren om nødvendigt stå i parentes [12] .
Driftspræfiks | Associativitet |
---|---|
! ? ~ | Præfiks |
. .( .[ .{ | |
anvende en funktion, konstruktør, etiket, assert,lazy | Venstre |
- -. | Præfiks |
** lsl lsr asr | Ret |
* / % mod land lor lxor | Venstre |
+ - | Venstre |
:: | Ret |
@ ^ | Ret |
& $ != | Venstre |
& && | Ret |
or || | Ret |
, | |
<- := | Ret |
if | |
; | Ret |
let match fun function try |
Ocaml-sproget har flere primitive typer : numeriske typer ( heltal og flydende komma), tegn , tegnstrenge , boolesk [13] .
Heltalstypen repræsenterer heltal fra området [−2 30 , 2 30 − 1] og [−2 62 , 2 62 − 1] for henholdsvis 32-bit og 64-bit arkitekturer. Med heltal kan du udføre de sædvanlige operationer med addition, subtraktion, multiplikation, division, idet du tager resten af divisionen :+,-,*,/,mod. Hvis resultatet går ud over det tilladte interval, opstår der ingen fejl, og resultatet beregnes modulo intervalgrænsen [14] .
Flydende kommatal er repræsenteret af en 53-bit mantisse og en eksponent i intervallet [−1022, 1023], efter IEEE 754 -standarden for doubler. I operationer kan disse tal ikke blandes med heltal. Derudover er operationer på flydende kommatal syntaktisk forskellige fra heltalsoperationer:+.,-.,*.,/.. Der er også en eksponentieringsoperation:**. For at konvertere heltal til flydende kommatal og omvendt er følgende funktioner tilgængelige: float_of_int og int_of_float [14] .
For flydende kommatal er der andre matematiske funktioner: trigonometriske (sin, cos, tan, asin, acos, atan), afrunding (loft, gulv), eksponentiel (exp), logaritmisk (log, log10), samt at tage kvadratrod (sqrt) [14] . Der er også polymorfe sammenligningsoperationer for numeriske typer [14] .
Tegntypen - char - svarer til repræsentationen af et tegn med en kode fra 0 til 255 (de første 128 tegn er de samme som ASCII ). Strengtype - streng - sekvens af tegn (maksimal længde: 2 24 - 6) [15] . Et eksempel, der bruger konverteringsfunktionen heltal-til-streng og sammenkædningsoperationen :
# "Eksempel" ^ string_of_int ( 2 ) ;; - : string = "Eksempel 2"Boolsk type har to værdier:true(sand) ogfalse(falsk). Operationer på booleske værdier: unær not (negation), binær:&&(og),||(eller). Binære operationer evaluerer det venstre argument først, og det højre argument kun hvis det kræves [16] .
Booleske værdier opnås som et resultat af sammenligninger: =(strukturel lighed), ==(identitet), <>(benægtelse af strukturel lighed), !=(nægtelse af identitet), <, >, <=, >=. For primitive typer, bortset fra strenge og flydende kommatal, er strukturel lighed og identitet sammenfaldende, for andre typer anses værdier placeret på samme adresse i hukommelsen som identiske, og i strukturel sammenligning kontrolleres værdier komponent for komponent [16] .
Derudover har OCaml en speciel type enhed, som kun har én værdi - ()[16] .
ListerI OCaml er en liste en endelig, uforanderlig sekvens af elementer af samme type, implementeret som en enkelt linket liste. Følgende eksempel viser listesyntaksen [17] :
# [ 'a' ; 'b' ; 'c' ] ;; - : char list = [ 'a' ; 'b' ; 'c' ] # 'a' :: ( 'b' :: ( 'c' :: [] )) ;; - : char list = [ 'a' ; 'b' ; 'c' ] # 'a' :: 'b' :: 'c' :: [] ;; - : char list = [ 'a' ; 'b' ; 'c' ] # [] ;; - : ' en liste = []Operationen ::giver dig mulighed for at bygge en liste baseret på det nye element og halen af den gamle liste. I dette tilfælde ændres den "gamle" liste ikke:
# lad lst = [ 1 ; 2 ] ;; val lst : int liste = [ 1 ; 2 ] # lad lst1 = 0 :: lst ;; val lst1 : int liste = [ 0 ; 1 ; 2 ] # lst ;; - : int liste = [ 1 ; 2 ] # lst1 ;; - : int liste = [ 0 ; 1 ; 2 ] Eksempel: beregning af summen af elementerne i en listeListe er en af hoveddatatyperne i OCaml. Følgende kodeeksempel definerer en rekursiv (bemærk rec nøgleordet) funktion, der itererer over elementerne i en given liste og returnerer deres sum:
lad rec sum xs = matche xs med | [] -> 0 | x :: xs' -> x + sum xs' #sum[1;2;3;4;5];; - : int = 15En anden måde at beregne summen på er at bruge rollup-funktionen:
lad sum xs = Liste . fold_venstre (+ ) 0xs # sum [ 1 ; 2 ; 3 ; 4 ; 5 ];; - : int = 15 IndlægOptegnelser er et vigtigt element i systemet af typen OCaml. En post er et sæt værdier, der er gemt sammen, hvor hvert element i værdiposten er tilgængelig ved sit navn, postens feltnavn. Et eksempel på en typeerklæring, binding af en post til en variabel og adgang til et postfelt [18] :
# type bruger = { login : streng ; adgangskode : streng _ nick : streng _ };; # let usr = { login = "minbruger" ; password = "hemmeligt" ; nick = "aka" ; } ;; val usr : bruger = { login = "min bruger" ; password = "hemmeligt" ; nick = "aka" } # usr . Nick ;; - : string = "aka"Det skal bemærkes, at typen af usr-variablen blev indstillet automatisk af compileren.
Som med andre typer kan en type parametreres. Andre optagelsesmuligheder [18] :
En varianttype repræsenterer data, der kan antage forskellige former, defineret af eksplicitte etiketter. Følgende eksempel definerer en type for basisfarver [19] :
# type hovedfarve = Rød | grøn | blå ;; # blå ;; - : hovedfarve = Blå # ( Rød , Blå ) ;; - : hovedfarve * hovedfarve = ( rød , blå )I eksemplet ovenfor bruges varianttypen som den opregnede type . I OKaml er varianttypen dog rigere, fordi den udover etiketter også giver dig mulighed for at angive data, f.eks.
# type farveskema = RGB af int * int * int | CMYK af float * float * float * float ;; type farveskema = RGB af int * int * int | CMYK af float * float * float * floatNår du definerer funktioner, parres varianttype naturligt med mønstermatchning.
ObjekterI OKaml er objekter og deres typer fuldstændig adskilt fra klassesystemet . Klasser bruges til at konstruere objekter og understøtte arv , men er ikke typer objekter. Objekter har deres egne objekttyper , og du behøver ikke bruge klasser for at arbejde med objekter. Objekter bruges ikke så ofte i OCaml (modulsystemet er f.eks. mere udtryksfuldt end objekter, da moduler kan indeholde typer, men klasser og objekter kan ikke). Den største fordel ved objekter i forhold til poster er, at de ikke kræver typedeklarationer og er mere fleksible på grund af rækkepolymorfi . På den anden side spiller fordelene ved objekter ind, når man bruger klassesystemet. I modsætning til moduler understøtter klasser sen binding, som giver dig mulighed for at henvise til objektmetoder uden en statisk defineret implementering og bruge åben rekursion (i tilfælde af moduler kan du bruge funktioner og funktorer, men syntaktisk kræver sådanne beskrivelser at skrive mere kode) [20 ] .
Indtast inferensSelvom OCaml er et stærkt indtastet programmeringssprog , giver typeinferenssystemet ( engelsk typeinferens ) dig mulighed for at bestemme typen af et udtryk baseret på den tilgængelige information om dets komponenter . I det følgende eksempel på en paritetsfunktion er der ikke angivet nogen typedeklaration, og alligevel har sprogkompileren fuldstændig information om typen af funktionen [21] :
# lad ulige x = x mod 2 <> 0 ;; val ulige : int -> bool = < sjov >Ud over de funktionelle indeholder sproget imperative programmeringsværktøjer : funktioner med bivirkninger , mutable data, imperative syntaktiske konstruktioner, især eksplicitte loops while , og for[22] .
Følgende eksempel vil udskrive 11 linjer til standardoutput (dette er en bivirkning af printf-funktionen):
for i = 0 til 10 gør Printf . printf "i =%d \n " jeg er færdig ;;I det følgende (temmelig kunstige) eksempel inkrementeres elementerne i et array på plads i en forudsætningsløkke. Til array-indekset bruges en reference (ref), som inkrementeres i løkkelegemet:
# lad incr_ar ar = lad i = ref 0 i mens ! i < array . længde ar do ar .(! i ) <- ar .(! i ) + 1 ; incr jeg er færdig ;; val incr_ar : int array -> unit = < sjov > # lad nums = [| 1 ; 2 ; 3 ; 4 ; 5 |];; val nums : int array = [| 1 ; 2 ; 3 ; 4 ; 5 |] # incr_ar nums ;; - : enhed = () # nums ;; - : int array = [| 2 ; 3 ; 4 ; 5 ; 6 |]Bivirkninger giver dig mulighed for at optimere beregninger, især når det kommer til væsentlige transformationer på store datasæt. De bruges også til at implementere doven evaluering og huskeseddel [22] .
OCaml kan opfattes som bestående af to sprog: et kernesprog med værdier og typer, og et sprog af moduler og deres signaturer . Disse sprog danner to lag i den forstand, at moduler kan indeholde typer og værdier, mens almindelige værdier ikke kan indeholde moduler og typemoduler. OCaml tilbyder dog en mekanisme til førsteklasses moduler , som kan være værdier og konvertere til og fra normale moduler efter behov [23] .
OCaml-modulsystemet er ikke begrænset til modulær kodeorganisation og grænseflader. Et af de vigtige værktøjer til generisk programmering er funktorer . Enkelt sagt er funktorer en funktion fra et modul til moduler, som giver dig mulighed for at implementere følgende mekanismer [24] :
For at starte OCaml-sprogfortolkeren skal du indtaste følgende kommando i konsollen:
$ ocaml Ocaml version 4.08.1 #Beregninger kan udføres interaktivt, for eksempel:
# 1 + 2 * 3 ;; - : int = 7Følgende "hello.ml" program:
print_endline "Hej verden!" ;;kan kompileres enten til bytekode :
$ ocamlc hello.ml -o hejeller ind i optimeret maskinkode :
$ ocamlopt hello.ml -o hejog lanceret:
$ ./hej Hej Verden! $Følgende eksempel er en quicksort- algoritme , der sorterer en liste i stigende rækkefølge:
lad rec qsort = funktion | [] -> [] | pivot :: hvile -> lad er_mindre x = x < pivot ind lad venstre , højre = Liste . partitionen er_mindre hvile i qsort venstre @ [ pivot ] @ qsort højreBemærk - Bogen bruger oversættelsen af udtrykket " førsteklasses funktion " som " førsteordensfunktion ". Men det skal huskes, at i talrige engelsksprogede kilder (om sprogs semantik generelt og om ML og Hindley-Milner i særdeleshed) skelnes der mellem fire begreber:
desuden er " førsteklasses " " bedre " end " anden klasse " ( bredere i kapaciteter, tættere på teorien og højere med hensyn til adgangstærskel ( C. Strachey - Fundamental Concepts in Programming Languages )), men " førsteordens "mere primitiv end " høj orden ". Især udvidelse af ML -modulsproget til " førsteklasses højordens " -niveau udgør et meget større problem for forskere end at udvide det til kun " førsteklasses " eller kun til " højordens " ( Rossberg A. Functors og runtime vs compile time (downlink) Hentet 25. juni 2015. Arkiveret fra originalen 26. juni 2015 ).
Programmeringssprog | |
---|---|
|