Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Functies zijn de fundamentele eenheid van de uitvoering van programma's in elke programmeertaal. Net als in andere talen heeft een F#-functie een naam, kunnen parameters hebben en argumenten aannemen en een hoofdtekst hebben. F# ondersteunt ook functionele programmeerconstructies, zoals het behandelen van functies als waarden, het gebruik van niet-benoemde functies in expressies, de samenstelling van functies om nieuwe functies te vormen, curriede functies en de impliciete definitie van functies door middel van de gedeeltelijke toepassing van functieargumenten.
U definieert functies met behulp van het let trefwoord, of, als de functie recursief is, de let rec combinatie van trefwoorden.
Syntaxis
// 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
Opmerkingen
De functienaam is een id die de functie vertegenwoordigt. De parameterlijst bestaat uit opeenvolgende parameters die worden gescheiden door spaties. U kunt een expliciet type opgeven voor elke parameter, zoals beschreven in de sectie Parameters. Als u geen specifiek argumenttype opgeeft, probeert de compiler het type uit de hoofdtekst van de functie af te stellen. De functietekst bestaat uit een expressie. De expressie waaruit de hoofdtekst van de functie bestaat, is doorgaans een samengestelde expressie die bestaat uit een aantal expressies die eindigen op een uiteindelijke expressie die de retourwaarde is. Het retourtype is een dubbele punt gevolgd door een type en is optioneel. Als u niet expliciet het type van de retourwaarde opgeeft, bepaalt de compiler het retourtype van de uiteindelijke expressie.
Een eenvoudige functiedefinitie lijkt op het volgende:
let f x = x + 1
In het vorige voorbeeld is fde naam van de functie , het argument xis , wat type heeft int, de hoofdtekst van de functie is x + 1en de retourwaarde is van het type int.
Functies kunnen worden gemarkeerd inline. Zie inline voor meer informatie.
Omvang
Op een ander bereiksniveau dan het modulebereik is het geen fout om een waarde of functienaam opnieuw te gebruiken. Als u een naam opnieuw gebruikt, wordt de naam die later is gedeclareerd, de eerder gedeclareerde naam weergegeven. In het bereik op het hoogste niveau in een module moeten namen echter uniek zijn. Met de volgende code wordt bijvoorbeeld een fout gegenereerd wanneer deze wordt weergegeven in het modulebereik, maar niet wanneer deze binnen een functie wordt weergegeven:
let list1 = [ 1; 2; 3 ]
// Error: duplicate definition.
let list1 = []
let function1 () =
let list1 = [ 1; 2; 3 ]
let list1 = []
list1
Maar de volgende code is acceptabel op elk niveau van het bereik:
let list1 = [ 1; 2; 3 ]
let sumPlus x =
// OK: inner list1 hides the outer list1.
let list1 = [ 1; 5; 10 ]
x + List.sum list1
Parameterwaarden
Namen van parameters worden weergegeven na de functienaam. U kunt een type voor een parameter opgeven, zoals wordt weergegeven in het volgende voorbeeld:
let f (x: int) = x + 1
Als u een type opgeeft, volgt deze de naam van de parameter en wordt deze gescheiden van de naam door een dubbele punt. Als u het type voor de parameter weglaat, wordt het parametertype afgeleid door de compiler. In de volgende functiedefinitie wordt het argument x bijvoorbeeld afgeleid van het type int omdat 1 van het type intis.
let f x = x + 1
De compiler probeert de functie echter zo algemeen mogelijk te maken. Noteer bijvoorbeeld de volgende code:
let f x = (x, x)
De functie maakt een tuple van één argument van elk type. Omdat het type niet is opgegeven, kan de functie worden gebruikt met elk argumenttype. Zie Automatische generalisatie voor meer informatie.
Functieteksten
Een functietekst kan definities van lokale variabelen en functies bevatten. Dergelijke variabelen en functies vallen binnen het bereik van de hoofdtekst van de huidige functie, maar niet buiten de functie. U moet inspringing gebruiken om aan te geven dat een definitie zich in een functietekst bevindt, zoals wordt weergegeven in het volgende voorbeeld:
let cylinderVolume radius length =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Zie Richtlijnen voor codeopmaak en uitgebreide syntaxis voor meer informatie.
Retourwaarden
De compiler gebruikt de uiteindelijke expressie in een functietekst om de retourwaarde en het type te bepalen. De compiler kan het type van de uiteindelijke expressie afleiden uit eerdere expressies. In de functie cylinderVolume, die in de vorige sectie wordt weergegeven, wordt het type pi bepaald van het type letterlijke gegevens 3.14159float. De compiler gebruikt het type om pi het type expressie length * pi * radius * radius te floatbepalen. Daarom is floathet algehele retourtype van de functie .
Als u het retourtype expliciet wilt opgeven, schrijft u de code als volgt:
let cylinderVolume radius length : float =
// Define a local value pi.
let pi = 3.14159
length * pi * radius * radius
Zoals de code hierboven is geschreven, past de compiler float toe op de gehele functie; Als u deze ook wilt toepassen op de parametertypen, gebruikt u de volgende code:
let cylinderVolume (radius: float) (length: float) : float
Een functie aanroepen
U roept functies aan door de functienaam op te geven, gevolgd door een spatie en vervolgens argumenten gescheiden door spaties. Als u bijvoorbeeld het functie cilinderVolume wilt aanroepen en het resultaat wilt toewijzen aan de waarde vol, schrijft u de volgende code:
let vol = cylinderVolume 2.0 3.0
Gedeeltelijke toepassing van argumenten
Als u minder argumenten opgeeft dan het opgegeven aantal argumenten, maakt u een nieuwe functie die de resterende argumenten verwacht. Deze methode voor het afhandelen van argumenten wordt kerrie genoemd en is een kenmerk van functionele programmeertalen zoals F#. Stel dat u met twee grootten pijp werkt: de ene heeft een straal van 2,0 en de andere een straal van 3,0. U kunt functies maken die het volume van de pijp als volgt bepalen:
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
Vervolgens zou u het laatste argument indien nodig opgeven voor verschillende lengten van de pijp van de twee verschillende grootten:
let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2
Recursieve functies
Recursieve functies zijn functies die zichzelf aanroepen. Hiervoor moet u het trefwoord rec opgeven na het trefwoord let . Roep de recursieve functie aan vanuit de hoofdtekst van de functie, net zoals u een functie-aanroep zou aanroepen. Met de volgende recursieve functie wordt het ne Fibonacci-getal berekend. De Fibonacci-nummerreeks is sinds de oudheid bekend en is een reeks waarin elk opeenvolgend getal de som is van de vorige twee getallen in de reeks.
let rec fib n =
if n < 2 then 1 else fib (n - 1) + fib (n - 2)
Sommige recursieve functies kunnen de programmastack overlopen of efficiënt uitvoeren als u ze niet met zorg en met kennis van speciale technieken schrijft, zoals het gebruik van tail-recursie, accumulators en vervolgen.
Functiewaarden
In F# worden alle functies beschouwd als waarden; in feite worden ze functiewaarden genoemd. Omdat functies waarden zijn, kunnen ze worden gebruikt als argumenten voor andere functies of in andere contexten waarin waarden worden gebruikt. Hier volgt een voorbeeld van een functie die een functiewaarde als argument gebruikt:
let apply1 (transform: int -> int) y = transform y
U geeft het type van een functiewaarde op met behulp van het -> token. Aan de linkerkant van dit token is het type van het argument en aan de rechterkant is de retourwaarde. In het vorige voorbeeld apply1 is een functie die een functie transform als argument gebruikt, waarbij transform een functie een geheel getal gebruikt en een ander geheel getal retourneert. De volgende code laat zien hoe u :apply1
let increment x = x + 1
let result1 = apply1 increment 100
De waarde is result 101 nadat de vorige code is uitgevoerd.
Meerdere argumenten worden gescheiden door opeenvolgende -> tokens, zoals wordt weergegeven in het volgende voorbeeld:
let apply2 (f: int -> int -> int) x y = f x y
let mul x y = x * y
let result2 = apply2 mul 10 20
Het resultaat is 200.
Lambda-expressies
Een lambda-expressie is een niet-benoemde functie. In de vorige voorbeelden kunt u lambda-expressies als volgt gebruiken in plaats van benoemde functies te definiëren:
let result3 = apply1 (fun x -> x + 1) 100
let result4 = apply2 (fun x y -> x * y) 10 20
U definieert lambda-expressies met behulp van het fun trefwoord. Een lambda-expressie lijkt op een functiedefinitie, behalve dat in plaats van het = token het -> token wordt gebruikt om de lijst met argumenten van de hoofdtekst van de functie te scheiden. Net als in een reguliere functiedefinitie kunnen de argumenttypen expliciet worden afgeleid of opgegeven en wordt het retourtype van de lambda-expressie afgeleid van het type van de laatste expressie in de hoofdtekst. Zie Lambda-expressies: Het fun trefwoord voor meer informatie.
Pijpleidingen
De pijpoperator |> wordt uitgebreid gebruikt bij het verwerken van gegevens in F#. Met deze operator kunt u 'pijplijnen' van functies op een flexibele manier tot stand brengen. Pipelining maakt het mogelijk om functie-aanroepen als opeenvolgende bewerkingen te koppelen:
let result = 100 |> function1 |> function2
In het volgende voorbeeld wordt uitgelegd hoe u deze operators kunt gebruiken om een eenvoudige functionele pijplijn te bouwen:
/// 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
Het resultaat is [2; 10; 26]. In het vorige voorbeeld worden lijstverwerkingsfuncties gebruikt, waarmee wordt gedemonstreerd hoe functies kunnen worden gebruikt om gegevens te verwerken bij het bouwen van pijplijnen. De pijplijnoperator zelf wordt als volgt gedefinieerd in de F#-kernbibliotheek:
let (|>) x f = f x
Functiesamenstelling
Functies in F# kunnen worden samengesteld uit andere functies. De samenstelling van twee functies , functie1 en functie2 , is een andere functie die de toepassing van functie1 vertegenwoordigt, gevolgd door de toepassing van functie2:
let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100
Het resultaat is 202.
De samenstellingsoperator >> heeft twee functies en retourneert daarentegen een functie. De pijplijnoperator |> neemt daarentegen een waarde en een functie en retourneert een waarde. In het volgende codevoorbeeld ziet u het verschil tussen de pijplijn- en samenstellingsoperatoren door de verschillen in de functiehandtekeningen en het gebruik weer te geven.
// 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
Overbelastingsfuncties
U kunt methoden van een type overbelasten, maar geen functies. Zie Methoden voor meer informatie.