Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Funktioner är den grundläggande enheten för programkörning på alla programmeringsspråk. Precis som på andra språk har en F#-funktion ett namn, kan ha parametrar och ta argument och har en brödtext. F# stöder också funktionella programmeringskonstruktioner som att behandla funktioner som värden, använda icke namngivna funktioner i uttryck, sammansättning av funktioner för att bilda nya funktioner, curryfunktioner och implicit definition av funktioner genom partiell tillämpning av funktionsargument.
Du definierar funktioner med hjälp av nyckelordet let eller, om funktionen är rekursiv, nyckelordskombinationen let rec .
Syntax
// Non-recursive function definition.
let [inline] function-name parameter-list [: return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body
Anmärkningar
Funktionsnamnet är en identifierare som representerar funktionen. Parameterlistan består av efterföljande parametrar som avgränsas med blanksteg. Du kan ange en explicit typ för varje parameter enligt beskrivningen i avsnittet Parametrar. Om du inte anger någon specifik argumenttyp försöker kompilatorn härleda typen från funktionstexten. Funktionstexten består av ett uttryck. Uttrycket som utgör funktionstexten är vanligtvis ett sammansatt uttryck som består av ett antal uttryck som kulminerar i ett slutligt uttryck som är returvärdet. Returtypen är ett kolon följt av en typ och är valfri. Om du inte uttryckligen anger typen av returvärde avgör kompilatorn returtypen från det slutliga uttrycket.
En enkel funktionsdefinition liknar följande:
let f x = x + 1
I föregående exempel är ffunktionsnamnet , argumentet är x, som har typen int, funktionstexten är x + 1och returvärdet är av typen int.
Funktioner kan markeras inline. Mer information om inlinefinns i Infogade funktioner.
Omfång
På någon annan omfångsnivå än modulomfånget är det inte ett fel att återanvända ett värde eller funktionsnamn. Om du återanvänder ett namn skuggar namnet som deklarerats senare namnet som deklarerats tidigare. Men på den översta nivån i en modul måste namnen vara unika. Följande kod genererar till exempel ett fel när den visas i modulomfånget, men inte när den visas i en funktion:
let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [ 1; 2; 3 ]
let list1 = []
list1
Men följande kod är acceptabel på alla omfångsnivå:
let list1 = [ 1; 2; 3 ]
let sumPlus x =
// OK: inner list1 hides the outer list1.
let list1 = [ 1; 5; 10 ]
x + List.sum list1
Parameterar
Namn på parametrar visas efter funktionsnamnet. Du kan ange en typ för en parameter, som du ser i följande exempel:
let f (x: int) = x + 1
Om du anger en typ följer den namnet på parametern och avgränsas från namnet med ett kolon. Om du utelämnar typen för parametern härleds parametertypen av kompilatorn. I följande funktionsdefinition härleds till exempel argumentet x till att vara av typen int eftersom 1 är av typen int.
let f x = x + 1
Kompilatorn försöker dock göra funktionen så generisk som möjligt. Observera till exempel följande kod:
let f x = (x, x)
Funktionen skapar en tuppeln från ett argument av vilken typ som helst. Eftersom typen inte har angetts kan funktionen användas med valfri argumenttyp. Mer information finns i Automatisk generalisering.
Funktionskroppar
En funktionstext kan innehålla definitioner av lokala variabler och funktioner. Sådana variabler och funktioner finns i omfånget i den aktuella funktionens brödtext men inte utanför den. Du måste använda indrag för att ange att en definition finns i en funktionstext, som du ser i följande exempel:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Mer information finns i Riktlinjer för kodformatering och utförlig syntax.
Returnera värden
Kompilatorn använder det sista uttrycket i en funktionstext för att fastställa returvärdet och typen. Kompilatorn kan härleda typen av det slutliga uttrycket från tidigare uttryck. I funktionen cylinderVolume, som visas i föregående avsnitt, bestäms typen av pi från vilken typ av literal 3.14159 som ska vara float. Kompilatorn använder typen av pi för att fastställa vilken typ av uttryck length * pi * radius * radius som ska vara float. Därför är floatfunktionens övergripande returtyp .
Om du vill ange returtypen explicit skriver du koden på följande sätt:
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Eftersom koden är skriven ovan tillämpar kompilatorn flyttal för hela funktionen. Om du vill använda den även för parametertyperna använder du följande kod:
let cylinderVolume (radius: float) (length: float) : float
Anropa en funktion
Du anropar funktioner genom att ange funktionsnamnet följt av ett blanksteg och sedan eventuella argument avgränsade med blanksteg. Om du till exempel vill anropa funktionscylindernVolume och tilldela resultatet till värdet vol skriver du följande kod:
let vol = cylinderVolume 2.0 3.0
Partiell tillämpning av argument
Om du anger färre än det angivna antalet argument skapar du en ny funktion som förväntar sig de återstående argumenten. Den här metoden för att hantera argument kallas currying och är en egenskap hos funktionella programmeringsspråk som F#. Anta till exempel att du arbetar med två rörstorlekar: den ena har en radie på 2,0 och den andra har en radie på 3,0. Du kan skapa funktioner som avgör rörvolymen enligt följande:
let smallPipeRadius = 2.0
let bigPipeRadius = 3.0
// These define functions that take the length as a remaining
// argument:
let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius
Du skulle sedan ange det slutliga argumentet efter behov för olika längder av rör av de två olika storlekarna:
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Rekursiva funktioner
Rekursiva funktioner är funktioner som anropar sig själva. De kräver att du anger nyckelordet rec efter nyckelordet let . Anropa den rekursiva funktionen inifrån funktionens brödtext precis som du anropar alla funktionsanrop. Följande rekursiva funktion beräknar det n:e Fibonacci-talet. Fibonacci-nummersekvensen har varit känd sedan antiken och är en sekvens där varje efterföljande tal är summan av de föregående två talen i sekvensen.
let rec fib n =
if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Vissa rekursiva funktioner kan spilla över programstacken eller utföra ineffektivt om du inte skriver dem med försiktighet och med medvetenhet om särskilda tekniker, till exempel användning av tail rekursion, ackumulatorer och fortsättningar.
Funktionsvärden
I F# betraktas alla funktioner som värden. I själva verket kallas de för funktionsvärden. Eftersom funktioner är värden kan de användas som argument till andra funktioner eller i andra sammanhang där värden används. Följande är ett exempel på en funktion som tar ett funktionsvärde som argument:
let apply1 (transform: int -> int) y = transform y
Du anger typ av funktionsvärde med hjälp -> av token. Till vänster i den här token är typen av argument, och till höger är returvärdet. I föregående exempel apply1 är en funktion som tar en funktion transform som ett argument, där transform är en funktion som tar ett heltal och returnerar ett annat heltal. Följande kod visar hur du använder apply1:
let increment x = x + 1
let result1 = apply1 increment 100
Värdet result för blir 101 efter att föregående kod har körts.
Flera argument avgränsas med efterföljande -> token, vilket visas i följande exempel:
let apply2 (f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
Resultatet är 200.
Lambda-uttryck
Ett lambda-uttryck är en namnlös funktion. I föregående exempel kan du i stället för att definiera namngivna funktioner öka och mula använda lambda-uttryck på följande sätt:
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y) 10 20
Du definierar lambda-uttryck med hjälp av nyckelordet fun . Ett lambda-uttryck liknar en funktionsdefinition, förutom att token i stället = för token -> används för att separera argumentlistan från funktionstexten. Precis som i en vanlig funktionsdefinition kan argumenttyperna härledas eller anges explicit, och returtypen för lambda-uttrycket härleds från typen av det sista uttrycket i brödtexten. Mer information finns i Lambda-uttryck: Nyckelordetfun.
Rörledningar
Röroperatorn |> används i stor utsträckning vid bearbetning av data i F#. Med den här operatorn kan du skapa "pipelines" av funktioner på ett flexibelt sätt. Pipelining gör att funktionsanrop kan sammanlänkas som efterföljande åtgärder:
let result = 100 |> function1 |> function2
I följande exempel går vi igenom hur du kan använda dessa operatorer för att skapa en enkel funktionell pipeline:
/// Square the odd values of the input and add one, using F# pipe operators.
let squareAndAddOdd values =
values |> List.filter (fun x -> x % 2 <> 0) |> List.map (fun x -> x * x + 1)
let numbers = [ 1; 2; 3; 4; 5 ]
let result = squareAndAddOdd numbers
Resultatet är [2; 10; 26]. I föregående exempel används listbearbetningsfunktioner som visar hur funktioner kan användas för att bearbeta data när du skapar pipelines. Själva pipelineoperatorn definieras i F#-kärnbiblioteket enligt följande:
let (|>) x f = f x
Funktionssammansättning
Funktioner i F# kan bestå av andra funktioner. Sammansättningen av två funktioner function1 och function2 är en annan funktion som representerar tillämpningen av function1 följt av tillämpningen av function2:
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
Resultatet är 202.
Kompositionsoperatorn >> tar två funktioner och returnerar en funktion. Pipelineoperatorn |> tar däremot ett värde och en funktion och returnerar ett värde. I följande kodexempel visas skillnaden mellan pipeline- och kompositionsoperatorerna genom att visa skillnaderna i funktionssignaturer och användning.
// Function composition and pipeline operators compared.
let addOne x = x + 1
let timesTwo x = 2 * x
// Composition operator
// ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
let Compose2 = addOne >> timesTwo
// Backward composition operator
// ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
let Compose1 = addOne << timesTwo
// Result is 5
let result1 = Compose1 2
// Result is 6
let result2 = Compose2 2
// Pipelining
// Pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo
// Backward pipeline operator
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x
// Result is 5
let result3 = Pipeline1 2
// Result is 6
let result4 = Pipeline2 2
Överlagringsfunktioner
Du kan överbelasta metoder av en typ men inte funktioner. Mer information finns i Metoder.