Lambdakalkyl ( λ-calculus ) är ett formellt system utvecklat av den amerikanske matematikern Alonzo Church för att formalisera och analysera begreppet beräkningsbarhet .
Ren λ-kalkyl , vars termer , även kallade objekt ("båda"), eller λ -termer, byggs uteslutande från variabler med användning av tillämpning och abstraktion. Inledningsvis antas inte närvaron av några konstanter.
λ -kalkylen är baserad på två grundläggande operationer:
Den grundläggande formen av ekvivalens definierad i lambda-termer är alfa-ekvivalens. Till exempel och : är alfaekvivalenta lambda-termer och båda representerar samma funktion (identitetsfunktion). Termer och är inte alfaekvivalenta eftersom de inte är i en lambdaabstraktion.
Eftersom uttrycket betecknar en funktion som tilldelar ett värde till varje , då för att utvärdera uttrycket
som inkluderar både tillämpning och abstraktion , är det nödvändigt att utföra substitution av siffran 3 i termen istället för variabeln . Resultatet är . Denna idé kan skrivas i allmän form som
och kallas β-reduktion . Ett uttryck för formen , det vill säga tillämpningen av en abstraktion på en viss term, kallas en redex . Även om β-reduktionen i huvudsak är det enda "väsentliga" axiomet för λ-kalkylen, leder det till en mycket rik och komplex teori. Tillsammans med den har λ-kalkylen egenskapen Turing-fullständighet och är därför det enklaste programmeringsspråket .
-konvertering uttrycker idén att två funktioner är identiska om och endast om de, när de tillämpas på något argument, ger samma resultat. -transformation översätter formler och till varandra (endast om den inte har några fria förekomster i : annars kommer den fria variabeln efter transformationen att bli en bunden extern abstraktion, eller vice versa).
En funktion av två variabler och kan betraktas som en funktion av en variabel , returnerar en funktion av en variabel , det vill säga som ett uttryck . Den här tekniken fungerar exakt likadant för funktioner av vilken art som helst . Detta visar att funktioner för många variabler kan uttryckas i λ-kalkyl och är " syntaktisk socker ". Den beskrivna processen att omvandla funktioner för många variabler till en funktion av en variabel kallas currying (även: currying ), efter den amerikanske matematikern Haskell Curry , även om den först föreslogs av Moses Sheinfinkel ( 1924 ).
Det faktum att termerna för λ-kalkylen fungerar som funktioner som tillämpas på termerna för λ-kalkylen (det vill säga kanske till dem själva) leder till svårigheter att konstruera en adekvat semantik av λ-kalkylen. För att ge λ-kalkylen någon mening är det nödvändigt att erhålla en mängd där dess funktionsutrymme skulle vara inbäddat . I det allmänna fallet existerar detta inte på grund av begränsningar av kardinaliteterna för dessa två uppsättningar, och fungerar från till : den andra har en större kardinalitet än den första.
Dana Scott övervann denna svårighet i början av 1970-talet genom att konstruera begreppet en region (till en början på kompletta gitter [1] , senare generalisera den till en komplett delvis ordnad uppsättning med en speciell topologi ) och skära ner den till funktioner som är kontinuerliga i denna topologi [ 2] . På basis av dessa konstruktioner skapades programmeringsspråkens denotationssemantik , i synnerhet på grund av det faktum att det med hjälp av dem är möjligt att ge en exakt mening till två viktiga programmeringsspråkskonstruktioner, som t.ex. som rekursion och datatyper .
Rekursion är att definiera en funktion i termer av sig själv; vid första anblicken tillåter inte lambdakalkylen detta, men detta intryck är missvisande. Tänk till exempel på en rekursiv funktion som beräknar faktorn :
f(n) = 1, om n = 0; annars n × f(n - 1).I lambdakalkylen kan en funktion inte direkt referera till sig själv. En funktion kan dock skickas med en parameter som är associerad med den. Som regel kommer detta argument först. Förknippar den med en funktion får vi en ny, redan rekursiv funktion. För att göra detta måste ett argument som refererar till sig självt (här betecknat som ) skickas till funktionskroppen.
g := λr. λn.(1, om n = 0; annars n × (rr (n-1))) f := ggDetta löser det specifika problemet med att beräkna faktorialen, men en generell lösning är också möjlig. Givet en lambda-term som representerar kroppen av en rekursiv funktion eller slinga, som ger sig själv som det första argumentet, kommer fixpunktskombinatorn att returnera den önskade rekursiva funktionen eller slingan. Funktioner behöver inte explicit passera sig själva varje gång.
Det finns flera definitioner av fixpunktskombinatorer. Den enklaste av dem:
Y = λg.(λx.g (xx)) (λx.g (xx))I lambdakalkyl är en fixpunkt ; låt oss visa det:
Y g (λh.(λx.h (xx)) (λx.h (xx))) g (λx.g (xx)) (λx.g (xx)) g((λx.g(xx))(λx.g(xx))) g (Yg)Nu, för att definiera faktorial som en rekursiv funktion, kan vi helt enkelt skriva , där är talet för vilket faktorialet beräknas. Låt , vi får:
g (Y g) 4 (λfn.(1, om n = 0; och n (f(n-1)), om n>0)) (Y g) 4 (λn.(1, om n = 0; och n ((Yg) (n-1)), om n>0)) 4 1, om 4 = 0; och 4 (g(Yg) (4-1)), om 4>0 4 (g(Y g) 3) 4 (λn.(1, om n = 0; och n ((Yg) (n-1)), om n>0) 3) 4 (1, om 3 = 0; och 3 (g(Y g) (3-1)), om 3>0) 4 (3 (g(Y g) 2)) 4 (3 (λn.(1, om n = 0; och n ((Yg) (n-1)), om n>0) 2)) 4 (3 (1, om 2 = 0; och 2 (g(Y g) (2-1)), om 2>0)) 4 (3 (2 (g(Y g) 1))) 4 (3 (2 (λn.(1, om n = 0; och n ((Yg) (n-1)), om n>0) 1))) 4 (3 (2 (1, om 1 = 0; och 1 ((Y g) (1-1)), om 1>0))) 4 (3 (2 (1) ((Y g) 0)))) 4 (3 (2 (1) ((λn.(1, om n = 0; och n ((Y g) (n-1)), om n>0) 0)))) 4 (3 (2 (1 (1), om 0 = 0; och 0 ((Y g) (0-1)), om 0>0)))) 4 (3 (2 (1 (1)))) 24Varje rekursiv funktionsdefinition kan representeras som en fixpunkt för motsvarande funktion, och därför kan varje rekursiv definition uttryckas som ett lambda-uttryck. I synnerhet kan vi definiera subtraktion, multiplikation, jämförelse av naturliga tal rekursivt.
I programmeringsspråk förstås "λ-kalkyl" ofta som mekanismen för " anonyma funktioner " - återuppringningsfunktioner som kan definieras direkt på den plats där de används och som har tillgång till de lokala variablerna för den aktuella funktionen ( stängning ).