Hovedtræk ved funktionelle programmeringssprog. Procedurelle programmeringssprog

Funktionel programmering kombinerer forskellige tilgange til at definere beregningsprocesser baseret på ret strenge abstrakte begreber og metoder til symbolsk databehandling.

Et træk ved funktionelle programmeringssprog er, at programtekster på funktionelle programmeringssprog beskriver "hvordan man løser et problem", men ikke foreskriver en række handlinger til at løse det. De vigtigste egenskaber ved funktionelle programmeringssprog: korthed, enkelhed, stærk skrivning, modularitet, tilstedeværelsen af ​​dovne (dovne) beregninger.

Funktionelle programmeringssprog inkluderer: Lisp, Miranda, Gofel, ML, Standard ML, Objective CAML, F#, Scala, Pythagoras osv.

Procedurelle programmeringssprog

Et proceduremæssigt programmeringssprog giver programmøren mulighed for at definere hvert trin i processen med at løse et problem. Det særlige ved sådanne programmeringssprog er, at opgaver er opdelt i trin og løses trin for trin. Ved hjælp af et proceduresprog definerer programmøren sprogkonstruktioner til at udføre en sekvens af algoritmiske trin.

Procedureprogrammeringssprog: Ada, Basic, C, COBOL, Pascal, PL/1, Rapier osv.

Stack programmeringssprog

Et stack-programmeringssprog er et programmeringssprog, der bruger maskinmodel stak. Stakbaserede programmeringssprog: Forth, PostScript, Java, C# osv. Når du bruger stakken som hovedkanal til at overføre parametre mellem ord, danner sprogelementer naturligt sætninger (sekventiel kæde). Denne egenskab bringer disse sprog tættere på naturlige sprog.

Aspektorienterede programmeringssprog 5) Deklarative programmeringssprog 6) Dynamiske programmeringssprog 7) Pædagogiske sprog programmering 8) Grænsefladebeskrivelsessprog 9) Prototypeprogrammeringssprog 10) Objektorienterede programmeringssprog 11) Logiske programmeringssprog 12) Script-programmeringssprog 13) Esoteriske programmeringssprog


Standardisering af programmeringssprog. Programmeringsparadigme

Begrebet programmeringssprog er uløseligt forbundet med dets implementering. For at sikre, at kompilering af det samme program af forskellige compilere altid giver det samme resultat, udvikles programmeringssprogsstandarder. Standardorganisationer: American National Standards Institute ANSI, Institute of Electrical and Electronics Engineers IEEE, Organisation internationale standarder ISO.



Når et sprog oprettes, frigives en privat standard, bestemt af sprogudviklerne. Hvis et sprog bliver udbredt, så opstår der over tid forskellige versioner af compilere, som ikke lige følger den bestemte standard. I de fleste tilfælde er der tale om en udvidelse af sprogets oprindeligt faste muligheder. En konsensusstandard er ved at blive udviklet for at bringe de mest populære implementeringer af sproget i overensstemmelse med hinanden. Meget vigtig faktor Standardiseringen af ​​et programmeringssprog er aktualiteten af ​​standardens udseende - før sprogets brede udbredelse og skabelsen af ​​mange inkompatible implementeringer. I processen med sprogudvikling kan der opstå nye standarder, som afspejler moderne innovationer.

Programmeringsparadigmer

Paradigme- et sæt teorier, standarder og metoder, der tilsammen repræsenterer en måde at organisere videnskabelig viden på - med andre ord en måde at se verden på. Analogt er det almindeligt accepteret, at et paradigme i programmering er en måde at begrebsliggøre på, der definerer, hvordan beregninger skal udføres, og hvordan arbejdet udført af en computer skal struktureres og organiseres.

Der er flere grundlæggende programmeringsparadigmer, hvoraf de vigtigste i øjeblikket er paradigmerne for direktiv, objektorienteret og funktionel-logisk programmering. For at understøtte programmering i overensstemmelse med et bestemt paradigme er der udviklet specielle algoritmiske sprog.

C og Pascal er eksempler på sprog designet til præskriptiv programmering, hvor programudvikleren bruger en procesorienteret model, det vil sige forsøger at skabe kode, der opererer på data på den rigtige måde. Det aktive princip i denne tilgang anses for at være et program (kode), der skal udføre alt, hvad der er nødvendigt for at opnå ønskede resultat handlinger på passive data.


Programmeringsteknologi som en proces til udvikling af softwareprodukter skabt som en uløselig helhed i form af velafprøvede programmer og undervisningsmaterialer, der beskriver deres formål og anvendelse.

Programmering- skabelsesproces computerprogrammer. I bredere forstand: rækken af ​​aktiviteter forbundet med oprettelse og vedligeholdelse af arbejde. programmers tilstand - computersoftware.

Programmeringsteknologi- et sæt metoder og værktøjer, der anvendes i softwareudviklingsprocessen.

Programmeringsteknologi er et sæt teknologiske instruktioner, herunder:

· angivelse af rækkefølgen af ​​teknologiske operationer;

· liste de betingelser, hvorunder denne eller hin operation udføres;

· beskrivelser af selve operationerne, hvor der for hver operation defineres startdata, resultater, samt instruktioner, forskrifter, standarder, kriterier mv.

Moderne programmeringsteknologi - komponent tilgang, som går ud på at bygge software fra individuelle komponenter - fysisk adskilte stykker software, der interagerer med hinanden gennem standardiserede binære grænseflader. I øjeblikket kvalitetskriterier et softwareprodukt anses for at være:− funktionalitet; − pålidelighed;− brugervenlighed;− effektivitet(forholdet mellem niveauet af tjenester leveret af softwareproduktet og brugeren hvornår givne forhold, til mængden af ​​anvendte ressourcer);− vedligeholdelse(egenskaber ved et softwareprodukt, der gør det muligt at minimere indsatsen for at foretage ændringer for at eliminere fejl i det og modificere det i overensstemmelse med brugernes skiftende behov); mobilitet(et softwaresystems evne til at blive overført fra et miljø til et andet, især fra en computer til en anden).

Et vigtigt skridt oprettelse af et softwareprodukt. test og fejlretning.

Fejlretning− dette er en aktivitet, der har til formål at opdage og rette fejl i software produkt ved at bruge deres programmers udførelsesprocesser.

Afprøvning− dette er processen med at udføre sine programmer på et bestemt datasæt, for hvilket resultatet af anvendelsen er kendt på forhånd, eller disse programmers adfærdsregler er kendte.

Følgende PS-testmetoder findes:

1) Statisk test - manuel test af programmet ved bordet.

2) Deterministisk test - med forskellige kombinationer af kildedata.

3) Stokastisk – ref. Dataene udvælges tilfældigt, og outputtet bestemmes af kvaliteten af ​​resultaterne eller et omtrentligt estimat.


Programmeringsstile.

En programmeringsstil er et sæt programmeringsteknikker eller -metoder, som programmører bruger til at producere programmer, der er korrekte, effektive, nemme at bruge og nemme at læse.

Der er flere programmeringsstile:

  1. Procedurel programmering er programmering, hvor et program er en sekvens af udsagn. Anvendes på højt niveau sprog Basic, Fortran osv.
  2. Funktionel programmering er programmering, hvor et program er en sekvens af funktionskald. Bruges på Lisp og andre sprog.
  3. Logisk programmering – dette er programmering, hvor programmet er et sæt af bestemmelse af relationer mellem objekter. Anvendes i Prolog og andre sprog.

Objektorienteret programmering– dette er programmering, hvor grundlaget for programmet er et objekt, der er en samling af data og regler for deres transformation. Anvendes i Turbo-Pascal, C++ osv.

Funktionerne er abstraktioner, hvor implementeringsdetaljerne for nogle handlinger er skjult bag et separat navn. Et velskrevet sæt funktioner gør det muligt at bruge dem mange gange. Standard bibliotek Python kommer med et væld af forudbyggede og fejlrettede funktioner, hvoraf mange er alsidige nok til at håndtere en lang række input. Selvom en bestemt sektion af kode ikke bruges flere gange, men med hensyn til input- og outputdata er den ret autonom, kan den sikkert adskilles i en separat funktion.

Denne forelæsning er mere orienteret mod praktiske overvejelser frem for funktionel programmeringsteori. Hvor det er nødvendigt, vil relevante udtryk dog blive brugt og forklaret.

Dernæst vil vi se nærmere på beskrivelsen og brugen af ​​funktioner i Python, rekursion, videregivelse og returnering af funktioner som parametre, sekvensbehandling og iteratorer samt konceptet med en generator. Det vil blive demonstreret, at i Python er funktioner objekter (og derfor kan sendes som parametre og returneres som et resultat af at udføre funktioner). Udover, vi taler om hvordan man kan implementere nogle funktionelle programmeringsmekanismer, som ikke har direkte syntaktisk understøttelse i Python, men er udbredt i funktionelle programmeringssprog.

Hvad er funktionel programmering?

Funktionel programmering er en programmeringsstil, der kun bruger sammensætninger af funktioner. Med andre ord, dette programmering i udtryk frem for imperative kommandoer.

Som David Mertz påpeger i sin artikel om funktionel programmering i Python, "funktionel programmering - programmering i funktionelle sprog (LISP, ML, OCAML, Haskell, ...)", hvis hovedattributter er:

  • "Tilstedeværelsen af ​​førsteklasses funktioner" (funktioner kan, ligesom andre objekter, overføres inde i funktioner).
  • Rekursion er grundlæggende ledelsesstruktur i et program.
  • Behandling af lister (sekvenser).
  • Forbud mod bivirkninger i funktioner, hvilket primært betyder fravær af tildeling (på "rene" funktionelle sprog)
  • Operatører er forbudt, og der lægges vægt på udtryk. I stedet for operatorer er hele programmet ideelt set ét udtryk med tilhørende definitioner.
  • Nøglespørgsmål: Hvad skal beregnes, ikke Hvordan.
  • Brug af funktioner af højere orden (funktioner over funktioner over funktioner).

Funktionelt program

I matematik er en funktion viser objekter fra samme sæt ( funktionsdefinitionssæt) til en anden ( sæt af funktionsværdier). Matematiske funktioner (de kaldes ren) "mekanisk", de beregner entydigt resultatet ud fra de givne argumenter. Rene funktioner bør ikke gemme data mellem to opkald. Du kan tænke på dem som sorte kasser, som du kun ved, hvad de gør, men slet ikke hvordan.

Funktionel stil programmer er designet som sammensætning funktioner. I dette tilfælde forstås funktioner på næsten samme måde som i matematik: de kortlægger et objekt til et andet. I programmering er "rene" funktioner et ideal, som ikke altid er opnåeligt i praksis. Praktiske nyttige funktioner har normalt bivirkning: Gem tilstand mellem opkald eller skift tilstanden for andre objekter. For eksempel er det umuligt at forestille sig I/O-funktioner uden bivirkninger. Faktisk bruges sådanne funktioner af hensyn til disse "effekter". Derudover fungerer matematiske funktioner nemt med objekter, der kræver en uendelig mængde information (f.eks. reelle tal). Generelt computer

  • Oversættelse

- PLO vil ikke længere være i stand til at redde os fra "Cloud Monsters."

Oversætterens bemærkning: Der er to begreber – parallelisme (udførelse samtidigt, uafhængigt) og samtidighed (udførelse i trin, én efter én, men flere opgaver på samme tid), og som altid var jeg nødt til at tude i hjernen for at finde de rigtige udtryk.

Jeg vil duplikere nogle ord eller udtryk i parentes i originalen for at søge med engelske termer Yderligere Information, som bliver mange gange større.

Du har måske allerede hørt et sådant udtryk som: "Clojure", "Scala", "Erlang" eller endda "Java har nu lambdas". Og du har i det mindste en vag idé om "funktionel programmering". Hvis du er medlem af et programmeringsfællesskab, er dette emne muligvis allerede blevet diskuteret af dig.

Hvis du Googler "Funktionel programmering", vil du ikke se noget nyt. Det andet sprog, der tidligere blev oprettet, dækker allerede dette emne, det blev oprettet i 50'erne og hedder Lisp. Hvorfor fanden blev dette emne så populært først nu? Bare 60 år senere?

I begyndelsen var computere meget langsomme

Tro det eller ej, computere var meget langsommere end DOM. Nej virkelig. Og samtidig var der 2 hovedtanker i aftalen om design og implementering af programmeringssprog:

De to første har lignende læseplaner, vil introducere dig til det grundlæggende i funktionel programmering og er meget velegnede til begyndere. Det tredje af linkene, dette kursus om, dækker mere end funktionel programmering. Det er vigtigt at bemærke, at disse materialer er til entry-level.

Jeg har altid ønsket at skrive en række artikler om funktionel programmering til dette blad, og jeg er meget glad for, at jeg endelig fik muligheden. Også selvom min serie om dataanalyse endnu langt fra er færdig :). Jeg vil ikke annoncere indholdet af hele serien, jeg vil kun sige, at vi i dag vil tale om forskellige programmeringssprog, der understøtter den funktionelle stil og tilsvarende programmeringsteknikker.

Programmeringssprog, som ikke alle kender til

Jeg begyndte at programmere som barn, og i en alder af femogtyve forekom det mig, at jeg vidste og forstod alt. Objekt orienteret programmering blev en del af min hjerne, blev alle tænkelige bøger om industriel programmering læst. Men jeg havde stadig følelsen af, at jeg var gået glip af noget, noget meget subtilt og utrolig vigtigt. Faktum er, at jeg, som mange i halvfemserne, i skolen blev lært at programmere i Pascal (åh ja, herlighed Turbo Pascal 5,5! - Ca. red.), så var der C og C++. På universitetet, Fortran og derefter Java som det vigtigste værktøj på arbejde. Jeg kunne Python og flere andre sprog, men det var helt forkert. Men jeg havde ikke en seriøs uddannelse inden for datalogi. En dag under en flyvning over Atlanten kunne jeg ikke sove, og jeg ville læse noget. På en eller anden måde, på magisk vis, havde jeg en bog om programmeringssproget Haskell lige ved hånden. Det forekommer mig, at det var dengang, jeg forstod den sande betydning af udtrykket "skønhed kræver ofre."

Nu, når folk spørger mig, hvordan jeg lærte Haskell, siger jeg dette: på et fly. Denne episode ændrede min holdning til programmering generelt. Selvfølgelig, efter det første bekendtskab, syntes mange ting ikke helt klart for mig. Jeg måtte anstrenge mig og studere spørgsmålet mere omhyggeligt. Og du ved, der er gået ti år, mange funktionelle elementer er blevet en del af industrisprog, lambda funktioner eksisterer allerede selv i Java, type slutning- i C++, mønstermatchning- i Scala. Mange mennesker tror, ​​at dette er en form for gennembrud. Og i denne serie af artikler vil jeg fortælle dig om funktionelle programmeringsteknikker ved hjælp af forskellige sprog og deres funktioner.

Internetbrugere sammensætter ofte alle mulige lister og toppe til underholdning for offentligheden. For eksempel "en liste over bøger, du bør læse, før du fylder tredive." Hvis jeg fik til opgave at lave en liste over bøger om programmering, som du bør læse, før du bliver ældre, så ville førstepladsen helt sikkert gå til bogen af ​​Abelson og Sussman "Struktur og fortolkning af computerprogrammer". Nogle gange forekommer det mig endda, at kompilatoren eller tolken nogen sprog bør stoppe enhver, der ikke har læst denne bog.

Derfor, hvis der er et sprog, du skal i gang med at lære funktionel programmering med, er det Lisp. Generelt er dette en hel familie af sprog, som inkluderer et nu ret populært sprog for JVM kaldet Clojure. Men det er ikke specielt velegnet som første funktionssprog. Til dette er det bedre at bruge sproget Ordning, som blev udviklet på MIT og indtil midten af ​​2000'erne fungerede som hovedsprog for undervisning i programmering. Selvom introduktionskurset med samme titel som den nævnte bog nu er erstattet af et kursus om Python, har det stadig ikke mistet sin relevans.

Jeg vil forsøge kort at tale om skemasproget og generelt om ideen bag sprogene i denne gruppe. På trods af at Lisp er meget gammel (af alle sprog på højt niveau er det kun Fortran, der er ældre), var det i den, at mange programmeringsmetoder, der bruges i dag, først blev tilgængelige. I det følgende vil jeg bruge navnet Lisp, hvilket betyder en specifik implementering - Scheme.

Syntaks på to minutter

Syntaksen i Lisp er, hmm, lidt kontroversiel. Faktum er, at ideen, der ligger til grund for syntaksen, er ekstrem enkel og er bygget på baggrund af den såkaldte S-udtryk. Dette er en præfiksnotation, hvor det velkendte udtryk 2 + 3 er skrevet som (+ 2 3) . Det kan virke underligt, men i praksis giver det nogle ekstra muligheder. Forresten virker (+ 2 10 (* 3,14 2)) også :). Således er hele programmet en samling af lister, der bruger præfiksnotation. I tilfældet med Lisp-sproget er selve programmet og det abstrakte syntakstræ - "hvis du ved, hvad jeg mener" 😉 - i det væsentlige ikke anderledes. Sådan en rekord gør parsing Lisp-programmer er meget enkle.
Da vi taler om et programmeringssprog, bør vi tale om, hvordan man definerer funktioner på dette sprog.

Her skal vi lave en lille digression. Der er én subtilitet, hvis betydning er undervurderet i moderne litteratur. Det er stadig nødvendigt at adskille en funktion i matematisk forstand og en funktion, som vi forstår den i funktionel programmering. Faktum er, at i matematik er funktioner deklarative objekter, og i programmering bruges de til at organisere beregningsprocessen, det vil sige, på en måde repræsenterer de snarere imperativ viden, viden, der besvarer spørgsmålet "hvordan?" Det er derfor, Abelson og Sussman i deres bog meget omhyggeligt adskiller dette og kalder funktioner i programmeringsprocedurer. Dette er ikke accepteret i moderne litteratur om funktionel programmering. Men jeg anbefaler stadig stærkt at adskille disse to betydninger af ordet "funktion" i det mindste i dit hoved.

Den nemmeste måde at definere en funktion på er at skrive følgende kode. Lad os starte med noget uanstændigt simpelt:

(definer (kvadratrødder a b c) (lad ((D (- (* b b) (* 4 a c)))) (hvis (< D 0) (list) (let ((sqrtD (sqrt D))) (let ((x1 (/ (- (- b) sqrtD) (* 2.0 a))) (x2 (/ (+ (- b) sqrtD) (* 2.0 a)))) (list x1 x2))))))

Ja, det er præcis, hvad du troede - at løse en andengradsligning i Scheme. Men dette er mere end nok til at se alle funktionerne i syntaksen. Her er sq-roots navnet på funktionen fra tre formelle parametre.

Ved første øjekast har let-konstruktionen, som bruges til at definere lokale variable, for mange parenteser. Men det er ikke sandt, vi definerer blot først en liste af variable og derefter et udtryk, hvori disse variabler bruges. Her er (listen). tom liste, som vi returnerer, når der ikke er nogen rødder, og (liste x1 x2) er en liste med to værdier.

Nu om udtryk. I vores sq-roots-funktion brugte vi en if-konstruktion. Det er her funktionel programmering kommer ind.

Pointen er, at i modsætning til imperative sprog som C, i funktionelle sprog, hvis er et udtryk, ikke et udsagn. I praksis betyder det, at den ikke kan have en anden filial. For et udtryk skal altid have en betydning.

Man kan ikke tale om syntaks uden at tale om syntaktisk sukker. I programmeringssprog refererer syntaktisk sukker til konstruktioner, der ikke er nødvendige, men kun gør koden nemmere at læse og genbruge. Lad os til at begynde med give et klassisk eksempel fra sproget C. Mange mennesker ved, at arrays ikke er et nødvendigt udtryksmiddel, da der er pointer. Ja, ja, arrays implementeres gennem pointere, og a[i] for C-sproget er det samme som *(a + i) . Dette eksempel er generelt ret usædvanligt, med en sjov effekt forbundet med det: da additionsoperationen forbliver kommutativ i tilfælde af pointere, er det sidste udtryk det samme som *(i + a) , og dette kan opnås ved at fjerne det syntaktiske sukker fra udtryk i[a] ! Operationen med at fjerne syntaktisk sukker i engelsk sprog kaldet et særligt ord afsukker.

For at vende tilbage til skemasproget er der et vigtigt eksempel på syntaktisk sukker. For at definere variable, som i tilfældet med funktioner, bruges et nøgleord (i Lisp og Scheme kaldes dette speciel form) Definere . For eksempel definerer (define pi 3.14159) variablen pi. Generelt kan du definere funktioner på nøjagtig samme måde:

(definer kvadrat (lambda (x) (* x x)))

dette er det samme som

(definer (kvadrat x) (* x x))

Den sidste linje ser lidt lettere at læse i forhold til den version, der bruger et lambda-udtryk. Det er dog klart, at det er nok at have den første mulighed, og den anden er valgfri. Hvorfor er den første vigtigere? Fordi en af ​​de mest grundlæggende egenskaber ved funktionelle sprog er, at funktioner i dem er førsteklasses objekter. Sidstnævnte betyder, at funktioner kan sendes som et argument og returneres som en værdi.

Hvis du ser på let fra et lambdaudtryks synspunkt, kan du nemt bemærke følgende korrespondance:

(lad ((x 5) (y 2)) (* x y)) (anvend (lambda (x y) (* x y)) (liste 5 2))

Funktionel programmering

Der er funktionelle sprog ren Og uren. Rene funktionelle sprog er relativt sjældne, de omfatter primært Haskell Og Ren. Rene sprog har ingen bivirkninger. I praksis betyder det ingen opgave eller I/O, som vi er vant til. Dette skaber en række vanskeligheder, selvom det på de allerede nævnte sprog er løst ganske smart, og på disse sprog skriver de kode med meget input/output. Sprog som Lisp, OCaml eller Scala tillader funktioner med bivirkninger, og i denne forstand er disse sprog ofte mere praktiske.

Vores opgave er at lære de grundlæggende teknikker til funktionel programmering i Scheme. Derfor vil vi skrive ren funktionel kode, uden at bruge en generator tilfældige tal, I/O og sæt! , som giver dig mulighed for at ændre værdierne af variabler. Alt dette kan du læse om i bogen SICP. Lad os nu fokusere på det vigtigste for os.

Den første ting, der forvirrer en nybegynder om funktionel programmering, er manglen på loops. Men hvad skal vi gøre? Mange af os bliver lært, at rekursion er dårligt. Dette argumenteres af det faktum, at rekursion i konventionelle programmeringssprog normalt implementeres ineffektivt. Pointen er, at man i det generelle tilfælde bør skelne mellem rekursion som en teknisk teknik, det vil sige at kalde en funktion fra sig selv, og rekursion som en proces. Funktionelle sprog understøtter optimering af halerekursion eller, som det nogle gange kaldes, akkumulatorrekursion. Dette kan illustreres med et simpelt eksempel.

Lad os sige, at vi har to funktioner - succ og prev. Den første returnerer et tal 1 større end argumentet, og den anden returnerer 1 mindre. Lad os nu prøve at definere additionsoperationen på to måder:

(definer (tilføj x y) (hvis (lign. y 0) x (tilføj (succ x) (forrige y)))) (definer (tilføj-1 x y) (hvis (lign. y 0) x (succ (tilføj-) 1 x (forrige å)))))

Hvad er forskellen mellem det første og det andet tilfælde? Faktum er, at hvis vi overvejer beregningsmetoden for den første sag trin for trin, kan vi se følgende:

(tilføj 3 4) => (tilføj 4 3) => (tilføj 5 2) => (tilføj 6 1) => (tilføj 7 0) => 7

I det andet tilfælde vil vi have noget som dette:

(tilføj-1 3 4) => (succ (tilføj-1 3 3)) => (succ (tilføj-1 3 2))) => (succ (succ (tilføj-1 3 1)) )) => (succ (succ (succ (succ (tilføj-1 3 0))))) => (succ (succ (succ (succ (succ 3))))) => (succ (succ (succ 4)))) => (succ (succ 5)) => (succ 6) => 7

På trods af at resultatet i begge tilfælde er det samme, er beregningsprocessen radikalt anderledes. I det første tilfælde ændres mængden af ​​brugt hukommelse ikke, men i det andet øges den lineært. Den første proces er iterativ, og for det andet - rekursive. Ja, til at skrive effektive programmer I funktionelle sprog skal halerekursion bruges for at undgå stakoverløb.

Lister

En af væsentlige elementer funktionel programmering, sammen med rekursion, - lister. De giver grundlag for komplekse strukturer data. Som i andre funktionelle sprog, er lister simpelthen forbundet på en hoved-til-hale måde. Cons-funktionen bruges til at oprette en liste, og bil- og cdr-funktionerne bruges til at få adgang til henholdsvis hoved og hale af listen. Så listen (liste 1 2 3) er intet andet end (cons 1 (cons 2 (cons 3 "()))). Her er "() en tom liste. Så en typisk listebehandlingsfunktion ser sådan ud:

(definer (sum lst) (hvis (null? lst) 0 (+ (bil lst) (sum (cdr lst)))))

Denne funktion summerer simpelthen elementerne i en liste. Mange listebehandlingsfunktioner ser sådan ud; i en af ​​de følgende artikler vil jeg fortælle dig hvorfor. Indtil videre vil jeg blot bemærke, at hvis vi erstatter det første argument i tillæg med 1, får vi en funktion, der beregner længden af ​​listen.

Funktioner af højere orden

Da funktioner kan sendes som argumenter og returneres som en værdi, ville det være rart at finde en brug for dette. Overvej følgende klassiske eksempel:

(definer (kort f lst) (if (null? lst) lst (cons (f (bil lst)) (kort f (cdr lst))))))

Kortfunktionen anvender f-funktionen på hvert element på listen. Hvor mærkeligt det end kan virke, kan vi nu udtrykke funktionen til at beregne længden af ​​en listelængde i form af sum og kort:

(definer (længde lst) (sum (kort (lambda (x) 1) lst)))

Hvis du pludselig besluttede nu, at alt dette på en eller anden måde er for simpelt, så lad os tænke over dette: hvordan implementerer man lister ved hjælp af funktioner af højere orden?

Det vil sige, at du skal implementere funktionerne cons , car og cdr, så de opfylder følgende relation: for enhver liste lst er det rigtigt, at værdien (cons (car lst) (cdr lst)) falder sammen med lst . Dette kan gøres på følgende måde:

(define (cons x xs) (lambda (pick) (if (eq? pick 1) x xs))) (define (car f) (f 1)) (define (cdr f) (f 2))

Hvordan det virker? Her returnerer cons-funktionen en anden funktion, som har én parameter, og afhængigt af det returnerer enten det første eller andet argument. Det er nemt at kontrollere, at det påkrævede forhold gælder for disse funktioner.

Brug af citat og metaprogrammering

En god egenskab ved Lisp-sproget er, at det er ekstremt praktisk til at skrive programmer, der konverterer andre programmer. Faktum er, at et program består af lister, og en liste er hoveddatastrukturen i sproget. Der er en måde at blot "citere" teksten i et program, så den opfattes som en liste over atomer.

Atomer er simpelthen karakterudtryk, for eksempel ("hej "verden) , som er det samme som "(hej verden) , eller i den fulde form (citat (hej verden)). Selvom de fleste Lisp-dialekter har strenge, kan du nogle gange klare dig med citat. Endnu vigtigere, ved at bruge denne tilgang kan du forenkle kodegenerering og programbehandling.

Lad os først prøve at forstå symbolske beregninger. Dette forstås normalt som computeralgebrasystemer, der er i stand til at håndtere symbolske objekter, formler, ligninger og andre komplekse matematiske objekter (der er mange sådanne systemer, de vigtigste eksempler er systemer Ahorn Og Mathematica).

Du kan prøve at implementere symbolsk differentiering. Jeg tror, ​​at alle, der er tæt på at afslutte skolen, kan forestille sig reglerne for differentiering (selvom i virkeligheden alt er lidt mere kompliceret - her vil vi beregne den partielle afledte ved blot at tælle andre variabler er konstanter, men det komplicerer ikke sagen overhovedet).

Så jeg vil bare give et eksempel på kode, der ville vise essensen af ​​sagen, og overlade detaljerne til læseren (som, jeg håber, omhyggeligt vil studere bogen "Struktur og fortolkning af computerprogrammer").

(define (afledt exp var) (cond ((tal? exp) 0) ((variabel? exp) (hvis (samme-variabel? exp var) 1 0)) ((sum? exp) (make-sum (afledt (afledt) addend exp) var) (afledt (augend exp) var))) ((produkt? exp) (make-sum (make-product (multiplikator exp) (afledt (multiplikant exp) var)) (fabrikat-produkt (afledt (multiplikator)) exp) var) (multiplicand exp)))) (ellers (fejl "ukendt udtrykstype - DERIV" exp))))

Her er udledningsfunktionen en implementering af differentieringsalgoritmen som undervist i skolen. Denne funktion kræver implementering af talfunktioner? , variabel? og så videre, som giver os mulighed for at forstå, hvilken natur dette eller hint udtrykselement har. Du skal også implementere ekstra funktioner make-product og make-sum. Her bruger vi cond-konstruktionen, som stadig er ukendt for os - dette er en analog af switch-operatøren i programmeringssprog som C og Java.

Før vi går videre til at implementere de manglende funktioner, er det værd at bemærke, at funktionel programmering ganske ofte bruger en top-down udviklingstilgang. Dette er, når de mest generelle funktioner skrives først, og derefter små funktioner, der er ansvarlige for implementeringsdetaljer.

(definer (variabel? x) (symbol? x)) (definer (samme-variabel? v1 v2) (og (variabel? v1) (variabel? v2) (eq? v1 v2))) (definer (gør-sum a1) a2) (liste "+ a1 a2)) (definer (fabrikat m1 m2) (liste "* m1 m2)) (definer (sum? x) (og (par? x) (eq? (bil x) "+ ))) (definer (tilføj s) (cadr s)) (definer (augend s) (caddr s)) (definer (produkt? x) (og (par? x) (eq? (bil x) "*)) ) (definer (multiplikator p) (cadr p)) (definer (multiplikant p) (caddr p))

Implementeringen af ​​disse funktioner kræver ikke særlige kommentarer med undtagelse af cadr- og caddr-funktionerne. Disse er intet andet end funktioner, der returnerer henholdsvis andet og tredje element i en liste.

Hvis du bruger den interaktive Scheme-fortolker, kan du nemt kontrollere, at den resulterende kode fungerer korrekt, men uden at forenkle udtrykkene:

(afledt "(+ x 3) "x) => (+ 1 0) (afledt "(* (* x y) (+ x 3)) "x) => (+ (* (* x y) (+ 1 0) )) (* (+ (* x 0) (* 1 år)) (+ x 3)))

For trivielle tilfælde (f.eks. multiplikation med 0) løses forenklingsproblemet ganske let. Dette spørgsmål overlades til læseren. De fleste af eksemplerne i denne artikel er hentet fra SICP-bogen, så hvis du har problemer, kan du blot henvise til kilden (bogen er i det offentlige domæne).

Som enhver dialekt har Lisp store metaprogrammeringsmuligheder, for det meste relateret til brugen af ​​makroer. Desværre kræver dette problem analyse i en separat artikel.

Lad os skrive en funktion, der fjerner syntaktisk sukker fra funktionsdefinitionen som diskuteret tidligere:

(define (desugar-define def) (lad ((fn-args (cadr def)) (body (caddr def))) (lad ((navn (bil fn-args)) (args (cdr fn-args)))) (liste "definer navn (liste "lambda args krop)))))

Denne funktion fungerer godt med velformede funktionsdefinitioner:

(desugar-define "(define (succ x) (+ x 1))) => (definer succ (lambda (x) (+ x 1)))

Dette virker dog ikke for almindelige definitioner som (define x 5) .
Hvis vi vil fjerne syntaktisk sukker i et stort program, der indeholder mange forskellige definitioner, så skal vi implementere yderligere kontrol:

(define (sugared? def) (og (eq? (car def) "define) (liste? (cadr def))))

Et sådant tjek kan indbygges direkte i desugar-define-funktionen, hvilket sikrer, at hvis definitionen ikke behøver at fjerne syntaktisk sukker, ændres den simpelthen ikke (denne trivielle øvelse er overladt til læseren). Så kan du pakke hele programmet ind i en liste og bruge kort:

(kort desugar-define prog)

Konklusion

I denne artikel satte jeg mig ikke til opgave at tale om Scheme i detaljer. Først og fremmest ville jeg vise flere interessante træk ved sproget og tiltrække læseren til at studere funktionel programmering. Dette vidunderlige sprog har på trods af dets enkelhed sin egen charme og funktioner, der gør programmering i det meget spændende. Hvad angår værktøjet til at arbejde med Scheme, kan de viljestærke svinge på MIT-skema, og resten - nyd et fremragende læringsmiljø Dr. Ketsjer. I en af ​​de følgende artikler vil jeg helt sikkert fortælle dig, hvordan du skriver din egen Scheme-tolk.

Dovne beregninger

I traditionelle programmeringssprog (såsom C++) får kald af en funktion alle argumenter til at blive evalueret. Denne metode til at kalde en funktion kaldes call-by-value. Hvis et argument ikke blev brugt i funktionen, så er resultatet af beregningen tabt, derfor blev beregningen gjort forgæves. I en vis forstand er det modsatte af call-by-value call-by-necessity. I dette tilfælde evalueres argumentet kun, hvis det er nødvendigt for at beregne resultatet. Et eksempel på denne adfærd er konjunktionsoperatoren fra C++ (&&), som ikke evaluerer værdien af ​​det andet argument, hvis det første argument er falsk.

Hvis et funktionelt sprog ikke understøtter doven evaluering, så kaldes det strengt. Faktisk er rækkefølgen for evaluering strengt defineret på sådanne sprog. Eksempler på strenge sprog inkluderer Scheme, Standard ML og Caml.

Sprog, der bruger doven evaluering, kaldes ikke-strenge. Haskell er et løst sprog, ligesom Gofer og Miranda for eksempel. Lamme sprog er ofte rene.

Meget ofte inkluderer strenge sprog understøttelse af nogle af de nyttige funktioner, der findes på ikke-strenge sprog, såsom uendelige lister. Standard ML leveres med et specielt modul til at understøtte udskudte beregninger. Derudover understøtter Objective Caml det ekstra reserverede ord doven og en konstruktion til lister over værdier beregnet efter behov.

Dette afsnit giver en kort beskrivelse af nogle (meget få) funktionelle programmeringssprog.

§ Lisp(Listebehandler). Det betragtes som det første funktionelle programmeringssprog. Utastet. Den indeholder en masse imperative egenskaber, men generelt opmuntrer den til den funktionelle programmeringsstil. Bruger call-by-value til beregninger. Der er en objektorienteret dialekt af sproget - CLOS.

§ JEG SVØMMER(Hvis du ser hvad jeg mener). Funktionelt sprog prototype. Udviklet af Landin i 60'erne af det 20. århundrede for at demonstrere, hvad et funktionelt programmeringssprog kunne være. Sammen med sproget udviklede Landin også en speciel virtuel maskine til at udføre programmer på ISWIM. Denne virtuelle call-by-value-maskine kaldes en SECD-maskine. Syntaksen for mange funktionelle sprog er baseret på syntaksen for ISWIM-sproget. Syntaksen for ISWIM ligner den for ML, især Caml.

§ Ordning. En Lisp-dialekt beregnet til videnskabelig forskning inden for datalogi. Ordningen blev designet med vægt på elegance og enkelhed i sproget. Dette gør sproget meget mindre end Common Lisp.


§ M.L.(Metasprog). En familie af strenge sprog med et udviklet polymorfisk typesystem og parametrerbare moduler. ML undervises på mange vestlige universiteter (nogle endda som det første programmeringssprog).

§ Standard ML. Et af de første maskinskrevne funktionelle programmeringssprog. Indeholder nogle imperative egenskaber såsom links til foranderlige værdier og er derfor ikke ren. Bruger call-by-value til beregninger. En meget interessant implementering af modularitet. Kraftfuldt system af polymorf type. Den seneste sprogstandard er Standard ML-97, som der er formelle for matematiske definitioner syntaks, samt statisk og dynamisk semantik af sproget.

§ Caml lys Og Målsætning Caml. Ligesom Standard ML tilhører den ML-familien. Formål Caml adskiller sig fra Caml Light hovedsageligt ved sin understøttelse af klassisk objektorienteret programmering. Ligesom Standard ML er streng, men har en vis indbygget støtte til doven evaluering.

§ Miranda. Udviklet af David Turner som et funktionelt standardsprog, der brugte doven evaluering. Det har et strengt polymorf type system. Ligesom ML undervises det på mange universiteter. Han havde stor indflydelse på udviklerne af Haskell-sproget.

§ Haskell. Et af de mest almindelige ikke-strenge sprog. Den har et meget udviklet skrivesystem. Modulsystemet er noget mindre veludviklet. Den seneste sprogstandard er Haskell-98.

§ Gofer(Godt til ligningsmæssigt ræsonnement). Forenklet Haskell-dialekt. Designet til undervisning i funktionel programmering.

§ Ren. Specielt designet til parallel og distribueret programmering. Syntaksen ligner Haskell. Ren. Bruger udskudte beregninger. Compileren leveres med et sæt biblioteker (I/O-biblioteker), der giver dig mulighed for at programmere en grafisk brugergrænseflade under Win32 eller MacOS.

Lad os minde dig om det den vigtigste egenskab Den funktionelle tilgang er det faktum, at ethvert program udviklet i et funktionelt programmeringssprog kan betragtes som en funktion, hvis argumenter også kan være funktioner.

Den funktionelle tilgang gav anledning til en hel familie af sprog, hvis forfader, som allerede nævnt, var programmeringssproget LISP. Senere i 70'erne udviklede man den originale version af ML-sproget, som efterfølgende udviklede sig især til SML, samt en række andre sprog. Af disse er måske den "yngste" Haskell-sproget, der blev skabt for ganske nylig i 90'erne.

En vigtig fordel implementering af funktionelle programmeringssprog er automatiseret dynamisk fordeling computerhukommelse til lagring af data. I dette tilfælde slipper programmøren af ​​med behovet for at kontrollere dataene og kan om nødvendigt køre funktionen "skraldeindsamling" - rydde hukommelse fra data, som programmet ikke længere har brug for.

Komplekse programmer i den funktionelle tilgang er bygget ved at aggregere funktioner. I dette tilfælde er programteksten en funktion, hvor nogle af argumenterne også kan betragtes som funktioner. Genbrug af kode kommer således ned på at kalde en tidligere beskrevet funktion, hvis struktur, i modsætning til en imperativ sprogprocedure, er matematisk gennemsigtig.

Fordi en funktion er en naturlig formalisme for funktionelle programmeringssprog, er implementering af forskellige aspekter af funktionsrelateret programmering meget forenklet. At skrive rekursive funktioner bliver intuitivt gennemsigtigt, dvs. funktioner, der kalder sig selv som et argument. Implementeringen af ​​behandling af rekursive datastrukturer bliver også naturlig.

På grund af implementeringen af ​​en mønstertilpasningsmekanisme er funktionelle programmeringssprog som ML og Haskell gode til symbolsk behandling.

Naturligvis er funktionelle programmeringssprog ikke uden nogle ulemper.

Disse omfatter ofte programmets ikke-lineære struktur og den relativt lave effektivitet af implementeringen. Den første ulempe er dog ret subjektiv, og den anden er med succes blevet overvundet af moderne implementeringer, især en række nyere SML-sprogoversættere, herunder en compiler til Microsoft .NET-miljøet.

For at udvikle professionel software i funktionelle programmeringssprog skal du dybt forstå funktionens karakter.

Bemærk, at udtrykket "funktion" i matematisk formalisering og softwareimplementering refererer til forskellige begreber.

Således er en matematisk funktion f med et domæne af definition A og et område af værdier B sættet af ordnede par

sådan at hvis

(a,b 1) f og (a,b 2) f,

Til gengæld er en funktion i et programmeringssprog en konstruktion af dette sprog, der beskriver reglerne for at konvertere et argument (den såkaldte faktiske parameter) til et resultat.

For at formalisere begrebet "funktion" blev en matematisk teori kendt som lambda-regning konstrueret. Mere præcist bør denne kalkulation kaldes lambda-omregningen.

Konvertering refererer til transformation af calculus-objekter (og i programmering, funktioner og data) fra en form til en anden. Den indledende opgave i matematik var ønsket om at forenkle udtryksformen. I programmering er netop denne opgave ikke så væsentlig, selvom, som vi vil se senere, brugen af ​​lambdaregning som den indledende formalisering kan være med til at forenkle programtypen, dvs. føre til programkodeoptimering.

Desuden giver konverteringer en overgang til nyindførte notationer og giver dermed mulighed for at repræsentere fagområdet i en mere kompakt eller mere detaljeret form, eller i matematisk sprog ændre abstraktionsniveauet ift. emneområde. Denne funktion er også meget brugt af objektorienterede og strukturerede modulære programmeringssprog i hierarkiet af objekter, programfragmenter og datastrukturer. Samspillet mellem applikationskomponenter i .NET er baseret på samme princip. Det er i denne forstand, at overgangen til nye notationer er et af de vigtigste elementer i programmering generelt, og det er lambdaregning (i modsætning til mange andre grene af matematikken), der repræsenterer en passende måde at formalisere renotationer på.

Lad os systematisere udviklingen af ​​de teorier, der ligger til grund for den moderne tilgang til lambdaregning.

Lad os overveje udviklingen af ​​programmeringssprog, der udvikler sig inden for rammerne af den funktionelle tilgang.

Tidlige funktionelle programmeringssprog, som stammer fra det klassiske LISP (LISt Processing) sprog, blev designet til at behandle lister, dvs. symbolsk information. I dette tilfælde var hovedtyperne et atomart element og en liste over atomare elementer, og hovedvægten var på at analysere indholdet af listen.

Udviklingen af ​​tidlige programmeringssprog blev funktionelle programmeringssprog med stærk skrivning, et typisk eksempel her er den klassiske ML og dens direkte efterkommer SML. I stærkt indtastede sprog skal hver konstruktion (eller udtryk) have en type.

I senere funktionelle programmeringssprog er der imidlertid ikke behov for eksplicit typetildeling, og typerne af oprindeligt udefinerede udtryk, som i SML, kan udledes (før programmet køres) baseret på typerne af de udtryk, der er knyttet til dem .

Næste trin i udviklingen af ​​funktionelle programmeringssprog var understøttelsen af ​​polymorfe funktioner, dvs. funktioner med parametriske argumenter (analoger matematisk funktion med parametre). Især polymorfi understøttes i SML, Miranda og Haskell.

På det nuværende udviklingstrin er "ny generation" funktionelle programmeringssprog dukket op med følgende avancerede egenskaber: mønstermatching (Scheme, SML, Miranda, Haskell), parametrisk polymorfi (SML) og såkaldt "doven" (som nødvendige) beregninger (Haskell, Miranda, S.M.L.).

Familien af ​​funktionelle programmeringssprog er ret stor. Dette bevises ikke så meget af den betydelige liste over sprog, men af ​​det faktum, at mange sprog har givet anledning til hele tendenser inden for programmering. Lad os huske på, at LISP gav anledning til en hel familie af sprog: Scheme, InterLisp, COMMON Lisp osv.

SML-programmeringssproget var ingen undtagelse, som blev skabt i form af ML-sproget af R. Milner ved MIT (Massachusetts Institute of Technology) og oprindeligt var beregnet til logiske deduktioner, især sætningsbevis. Sproget er strengt typificeret og mangler parametrisk polymorfi.

Udviklingen af ​​"klassisk" ML er blevet til tre moderne sprog med næsten identiske muligheder (parametrisk polymorfi, mønstermatchning, "dovne" beregninger). Dette er SML-sproget, udviklet i Storbritannien og USA, CaML, skabt af en gruppe franske videnskabsmænd ved INRIA Institute, SML/NJ - en dialekt af SML fra New Jersey, og også en russisk udvikling - mosml (den " Moskva" dialekt af ML).

Nærheden til matematisk formalisering og den indledende funktionelle orientering forårsagede følgende fordele ved den funktionelle tilgang:

1. let afprøvning og verifikation af programkode baseret på muligheden for at konstruere et strengt matematisk bevis for programmernes rigtighed;

2. forening af præsentationen af ​​programmet og data (data kan indkapsles i programmet som funktionsargumenter, betegnelsen eller beregningen af ​​funktionsværdien kan foretages efter behov);

3. sikker indtastning: ugyldige dataoperationer er udelukket;

4. dynamisk indtastning: tastefejl kan detekteres under kørsel (fraværet af denne egenskab i tidlige funktionelle programmeringssprog kan føre til overløb Random Access Memory computer);

5. softwareimplementeringens uafhængighed af maskinrepræsentationen af ​​data og programmets systemarkitektur (programmøren er fokuseret på detaljerne i implementeringen og ikke på funktionerne i maskinrepræsentationen af ​​data).

Bemærk, at realiseringen af ​​fordelene, som funktionelle programmeringssprog giver, afhænger væsentligt af valget af software og hardwareplatform.

Hvis .NET-teknologi vælges som softwareplatform, næsten uanset hardwareimplementeringen, programmøren eller manageren software projekt modtager desuden følgende fordele:

1. integration forskellige sprog funktionel programmering (dette udnytter fordelene ved hvert af sprogene maksimalt, især Scheme giver en mønstertilpasningsmekanisme, og SML giver mulighed for at beregne efter behov);

2. integration af forskellige tilgange til programmering baseret på den tværsprogede Common Language Infrastructure, eller CLI (det er især muligt at bruge C# til at give fordelene ved en objektorienteret tilgang og SML - funktionel, som i dette kursus) ;

3. fælles unified typing system Common Type System, CTS (uniform og sikker ledelse typer af data i programmet);

4. Flertrins, fleksibelt system til sikring af programkodens sikkerhed (især baseret på monteringsmekanismen).

Hovedtrækkene ved funktionelle programmeringssprog, der adskiller dem fra både imperative sprog og logiske programmeringssprog, er referentiel gennemsigtighed og determinisme. I funktionelle sprog er der betydelig variation i parametre som indtastning og regneregler. På mange sprog er rækkefølgen af ​​evaluering strengt defineret. Men nogle gange indeholder strenge sprog midler til at støtte nogle nyttige elementer, iboende i ikke-strenge sprog, for eksempel uendelige lister (Standard ML har et særligt modul til at understøtte dovne beregninger). I modsætning hertil tillader ikke-strenge sprog i nogle tilfælde energisk beregning.

Miranda har således doven semantik, men giver dig mulighed for at specificere strenge konstruktører ved at markere konstruktørargumenterne på en bestemt måde.

Mange moderne funktionelle programmeringssprog er stærkt indtastede sprog (stærk indtastning). Stærk skrivning giver større sikkerhed. Mange fejl kan rettes på kompileringsstadiet, så fejlfindingsfasen og den samlede programudviklingstid reduceres. Stærk indtastning gør det muligt for compileren at generere mere effektiv kode og dermed fremskynde programudførelsen. Sammen med dette er der funktionelle sprog med dynamisk skrivning. Datatypen på sådanne sprog bestemmes under programafviklingen (kapitel 3). De kaldes nogle gange "typeløse". Deres fordele inkluderer det faktum, at programmer skrevet på disse sprog har større almindelighed. En ulempe kan betragtes som tilskrivningen af ​​mange fejl til programafviklingsstadiet og det tilhørende behov for at bruge typekontrolfunktioner og en tilsvarende reduktion i programmets almindelighed. Indskrevne sprog bidrager til generering af mere "pålidelig" kode, mens maskinskrevne sprog producerer mere "generel" kode.

Det næste kriterium, som funktionelle programmeringssprog kan klassificeres efter, kan være tilstedeværelsen af ​​imperative mekanismer. Samtidig er det sædvanligt at kalde funktionelle programmeringssprog, der er blottet for imperative mekanismer, for "rene", og dem, der har dem, kaldes "urene". I oversigten over funktionelle programmeringssprog nedenfor vil programmeringssprog blive omtalt som "praktiske" og "akademiske". Med "praktiske" sprog mener vi sprog, der har en kommerciel anvendelse (de blev brugt til at udvikle rigtige applikationer eller der var kommercielle programmeringssystemer). Akademiske programmeringssprog er populære i forskningskredse og i feltet computer uddannelse, men der er praktisk talt ingen kommercielle applikationer skrevet på sådanne sprog. De forbliver kun et værktøj til at udføre teoretisk forskning inden for datalogi og er meget brugt i uddannelsesprocessen.

En liste over de mest populære funktionelle programmeringssprog er givet nedenfor ved hjælp af følgende kriterier: generel information; maskinskrivning; type beregning; renhed.

Almindelig Lisp. Versionen af ​​Lisp, som siden 1970 kan betragtes som sprogstandarden, takket være støtten fra laboratoriet for kunstig intelligens fra Massachusetts Institute of Technology, er typeløs, energisk med et stort sæt imperative indeslutninger, der tillader tildeling, ødelæggelse af strukturer . Praktisk. Det er tilstrækkeligt at sige den vektor grafik editor Autocad.

Ordning. En Lisp-dialekt beregnet til videnskabelig forskning i datalogi og undervisning i funktionel programmering. Takket være manglen på imperative inklusioner er sproget meget mindre end almindelig Lisp. Går tilbage til et sprog udviklet af J. McCarthy i 1962. Akademisk, typeløs, energisk, ren.

Refal. En familie af sprog udviklet af V. F. Turchin. Det ældste medlem af denne familie blev først realiseret i 1968 i Rusland. Det er stadig meget brugt i akademiske kredse. Indeholder logiske programmeringselementer (mønstertilpasning). Derfor foreslås Refal-sproget i denne lærebog som sprog for selvstudier.

Miranda. Stærkt skrevet, understøtter brugerdatatyper og polymorfi. Udviklet af Turner baseret på de tidligere SALS- og KRC-sprog. Har doven semantik. Ingen bydende indeslutninger.

Haskell. Udviklingen af ​​sproget skete i slutningen af ​​forrige århundrede. Alment kendt i akademiske kredse. På nogle vestlige universiteter bruges det som hovedsprog for studerende at studere. Et af de mest kraftfulde funktionelle sprog. Dovent sprog. Rent funktionelt sprog. Indtastet. Haskell er et fantastisk værktøj til at lære og eksperimentere med komplekse funktionelle datatyper. Programmer skrevet i Haskell har en betydelig objektkodestørrelse og lav udførelseshastighed.

Ren. En Haskell-dialekt skræddersyet til behovene for praktisk programmering. Ligesom Haskell er det et dovent, rent funktionelt sprog, der indeholder typeklasser. Men Clean indeholder også interessante funktioner, som ikke har noget tilsvarende i Haskell. For eksempel er imperative funktioner i Clean baseret på unikke typer, hvis idé er lånt fra lineær logik. Clean indeholder mekanismer, der kan forbedre effektiviteten af ​​programmer betydeligt. Disse mekanismer undertrykker eksplicit doven beregning. Clean-implementeringen er et kommercielt produkt, men en gratis version er tilgængelig til forsknings- og uddannelsesformål.

ML (Metasprog). Udviklet af en gruppe programmører ledet af Robert Milier i midten af ​​70'erne. i Edinburgh (Edinburgh Logic for Computable Functions). Ideen med sproget var at skabe en mekanisme til at konstruere formelle beviser i et logiksystem for beregnelige funktioner. I 1983 blev sproget revideret til at omfatte begreber som moduler. Kom til at hedde standard ML. ML er stærk maskinskrevne sprog med statisk typekontrol og applikativ programudførelse. Det har vundet stor popularitet i forskningskredse og inden for computerundervisning.