Händelser
17 mars 21 - 21 mars 10
Gå med i mötesserien för att skapa skalbara AI-lösningar baserat på verkliga användningsfall med andra utvecklare och experter.
Registrera dig nuDen här webbläsaren stöds inte längre.
Uppgradera till Microsoft Edge och dra nytta av de senaste funktionerna och säkerhetsuppdateringarna, samt teknisk support.
Funktionell programmering är en programmeringsstil som betonar användningen av funktioner och oföränderliga data. Typad funktionell programmering är när funktionell programmering kombineras med statiska typer, till exempel med F#. I allmänhet betonas följande begrepp i funktionell programmering:
I den här serien utforskar du begrepp och mönster i funktionell programmering med hjälp av F#. Längs vägen lär du dig lite F# också.
Funktionell programmering, liksom andra programmeringsparadigm, levereras med ett ordförråd som du så småningom behöver lära dig. Här följer några vanliga termer som du ser hela tiden:
Följande exempel visar dessa grundläggande begrepp.
Den vanligaste och grundläggande konstruktionen i funktionell programmering är funktionen. Här är en enkel funktion som lägger till 1 i ett heltal:
let addOne x = x + 1
Dess typsignatur är följande:
val addOne: x:int -> int
Signaturen kan läsas som, "addOne
accepterar ett int
namngivet x
och skapar en int
". Mer formellt addOne
mappasett värde från uppsättningen heltal till uppsättningen heltal. Token ->
betyder den här mappningen. I F# kan du vanligtvis titta på funktionssignaturen för att få en uppfattning om vad den gör.
Så varför är signaturen viktig? I maskinskriven funktionell programmering är implementeringen av en funktion ofta mindre viktig än den faktiska typsignaturen! Det faktum att addOne
lägger till värdet 1 i ett heltal är intressant vid körning, men när du skapar ett program är det det faktum att det accepterar och returnerar ett int
som informerar hur du faktiskt kommer att använda den här funktionen. När du använder den här funktionen korrekt (med avseende på dess typsignatur) kan diagnostisering av eventuella problem endast göras i funktionens addOne
brödtext. Detta är drivkraften bakom typad funktionell programmering.
Uttryck är konstruktioner som utvärderas till ett värde. Till skillnad från instruktioner, som utför en åtgärd, kan uttryck tänkas utföra en åtgärd som ger tillbaka ett värde. Uttryck används nästan alltid i funktionell programmering i stället för instruktioner.
Överväg föregående funktion, addOne
. Brödtexten addOne
i är ett uttryck:
// 'x + 1' is an expression!
let addOne x = x + 1
Det är resultatet av det här uttrycket som definierar funktionens resultattyp addOne
. Uttrycket som utgör den här funktionen kan till exempel ändras till en annan typ, till exempel :string
let addOne x = x.ToString() + "1"
Funktionens signatur är nu:
val addOne: x:'a -> string
Eftersom alla typer i F# kan ha ToString()
anropat den har typen av x
gjorts generisk (kallas automatisk generalisering) och den resulterande typen är en string
.
Uttryck är inte bara funktioners kroppar. Du kan ha uttryck som ger ett värde som du använder någon annanstans. En vanlig är if
:
// Checks if 'x' is odd by using the mod operator
let isOdd x = x % 2 <> 0
let addOneIfOdd input =
let result =
if isOdd input then
input + 1
else
input
result
Uttrycket if
genererar ett värde med namnet result
. Observera att du kan utelämna result
helt och hållet, vilket gör if
uttrycket till funktionens addOneIfOdd
brödtext. Det viktigaste att komma ihåg om uttryck är att de skapar ett värde.
Det finns en särskild typ, unit
, som används när det inte finns något att returnera. Tänk dig till exempel den här enkla funktionen:
let printString (str: string) =
printfn $"String is: {str}"
Signaturen ser ut så här:
val printString: str:string -> unit
Typen unit
anger att det inte finns något faktiskt värde som returneras. Detta är användbart när du har en rutin som måste "göra arbete" trots att du inte har något värde att returnera som ett resultat av det arbetet.
Detta står i skarp kontrast till imperativ programmering, där motsvarande if
konstruktion är en -instruktion, och skapande av värden görs ofta med muterande variabler. I C# kan koden till exempel skrivas så här:
bool IsOdd(int x) => x % 2 != 0;
int AddOneIfOdd(int input)
{
var result = input;
if (IsOdd(input))
{
result = input + 1;
}
return result;
}
Det är värt att notera att C# och andra C-formatspråk stöder ternary-uttrycket, vilket möjliggör uttrycksbaserad villkorsstyrd programmering.
I funktionell programmering är det ovanligt att mutera värden med -instruktioner. Även om vissa funktionella språk stöder instruktioner och mutationer är det inte vanligt att använda dessa begrepp i funktionell programmering.
Som tidigare nämnts är rena funktioner funktioner som:
Det är bra att tänka på matematiska funktioner i det här sammanhanget. I matematik är funktioner endast beroende av deras argument och har inga biverkningar. I den matematiska funktionen f(x) = x + 1
beror värdet för f(x)
endast på värdet för x
. Rena funktioner i funktionell programmering är samma sätt.
När du skriver en ren funktion måste funktionen bara vara beroende av dess argument och inte utföra någon åtgärd som resulterar i en biverkning.
Här är ett exempel på en icke-ren funktion eftersom den är beroende av globalt, föränderligt tillstånd:
let mutable value = 1
let addOneToValue x = x + value
Funktionen addOneToValue
är helt klart oren, eftersom value
kan ändras när som helst för att ha ett annat värde än 1. Det här mönstret för beroende på ett globalt värde ska undvikas i funktionell programmering.
Här är ett annat exempel på en icke-ren funktion, eftersom den utför en bieffekt:
let addOneToValue x =
printfn $"x is %d{x}"
x + 1
Även om den här funktionen inte är beroende av x
ett globalt värde, skriver den värdet för till programmets utdata. Även om det inte är något fel i sig med att göra detta, betyder det att funktionen inte är ren. Om en annan del av programmet är beroende av något utanför programmet, till exempel utdatabufferten, kan anrop av den här funktionen påverka den andra delen av programmet.
Om du tar bort -instruktionen printfn
blir funktionen ren:
let addOneToValue x = x + 1
Även om den här funktionen inte är bättre än den tidigare versionen med -instruktionen printfn
, garanterar den att allt den här funktionen gör är att returnera ett värde. Att anropa den här funktionen valfritt antal gånger ger samma resultat: det genererar bara ett värde. Den förutsägbarhet som renheten ger är något som många funktionella programmerare strävar efter.
Slutligen är ett av de mest grundläggande begreppen för typad funktionell programmering oföränderlighet. I F# är alla värden oföränderliga som standard. Det innebär att de inte kan muteras på plats om du inte uttryckligen markerar dem som föränderliga.
I praktiken innebär arbete med oföränderliga värden att du ändrar din inställning till programmering från "Jag behöver ändra något" till "Jag behöver skapa ett nytt värde".
Om du till exempel lägger till 1 i ett värde skapas ett nytt värde, vilket inte muterar det befintliga värdet:
let value = 1
let secondValue = value + 1
I F#muterar value
inte följande kod funktionen. I stället utför den en likhetskontroll:
let value = 1
value = value + 1 // Produces a 'bool' value!
Vissa funktionella programmeringsspråk stöder inte mutation alls. I F# stöds det, men det är inte standardbeteendet för värden.
Det här konceptet sträcker sig ännu längre till datastrukturer. I funktionell programmering har oföränderliga datastrukturer som uppsättningar (och många fler) en annan implementering än förväntat. Konceptuellt ändrar något som att lägga till ett objekt i en uppsättning inte uppsättningen, utan skapar en ny uppsättning med det extra värdet. Under täcket utförs detta ofta av en annan datastruktur som gör det möjligt att effektivt spåra ett värde så att lämplig representation av data kan ges som ett resultat.
Den här typen av arbete med värden och datastrukturer är viktig eftersom det tvingar dig att behandla alla åtgärder som ändrar något som om det skapar en ny version av den saken. Detta gör att saker som likhet och jämförbarhet kan vara konsekventa i dina program.
I nästa avsnitt går vi igenom funktionerna noggrant och utforskar olika sätt att använda dem i funktionell programmering.
Med hjälp av funktioner i F# utforskas funktioner djupt och visar hur du kan använda dem i olika sammanhang.
Serien Thinking Functionally är en annan bra resurs för att lära sig mer om funktionell programmering med F#. Den beskriver grunderna i funktionell programmering på ett pragmatiskt och lättläst sätt med hjälp av F#-funktioner för att illustrera begreppen.
Feedback om .NET
.NET är ett öppen källkod projekt. Välj en länk för att ge feedback:
Händelser
17 mars 21 - 21 mars 10
Gå med i mötesserien för att skapa skalbara AI-lösningar baserat på verkliga användningsfall med andra utvecklare och experter.
Registrera dig nuUtbildning
Utbildningsväg
Ta dina första steg med F# - Training
F# är ett plattformsoberoende programmeringsspråk med öppen källkod som gör det enkelt att skriva kortfattad, högpresterande, robust och praktisk kod. Det är ett allmänt språk som gör att du kan skapa många olika typer av program som Webb-API, Desktop, IoT, Gaming med mera.